diff --git a/.cargo/config b/.cargo/config.toml similarity index 59% rename from .cargo/config rename to .cargo/config.toml index b9f0cd28e..e61e7526b 100644 --- a/.cargo/config +++ b/.cargo/config.toml @@ -4,7 +4,3 @@ unit-test = "test --lib" [build] rustflags = ["--cfg", "tracing_unstable"] - -[env] -SOLIDITY_GATEWAY_VERSION = "v5.9.0" -SOLIDITY_RELEASES_URL = "https://github.com/axelarnetwork/axelar-gmp-sdk-solidity/releases/download" diff --git a/.dockerignore b/.dockerignore index 977dbf345..883057c49 100644 --- a/.dockerignore +++ b/.dockerignore @@ -4,7 +4,6 @@ # For more help, visit the .dockerignore file reference guide at # https://docs.docker.com/engine/reference/builder/#dockerignore-file -Cargo.lock **/.DS_Store **/.dockerignore **/.git diff --git a/.github/actions/release/action.yaml b/.github/actions/release/action.yaml index 2796ba234..f18b10429 100644 --- a/.github/actions/release/action.yaml +++ b/.github/actions/release/action.yaml @@ -16,6 +16,9 @@ inputs: description: "minor pattern match string" change-path: description: "paths to observe for changes" + github_token: + description: "GitHub token for pushing changes" + required: true runs: using: "composite" @@ -156,9 +159,20 @@ runs: inputs.dry-run == 'false' && steps.semantic-version.outputs.changed == 'true' run: | - git config --global user.email "devops@axelar.network" - git config --global user.name "Axelar DevOps" + git config --global user.email "devops@interoplabs.io" + git config --global user.name "Interop Labs CI" cargo release -x \ --no-confirm \ + --no-push \ -p ${{ inputs.binary-to-release }} \ ${{ steps.semantic-version.outputs.version_type }} + + - name: Push changes of cargo release + if: + inputs.dry-run == 'false' && + steps.semantic-version.outputs.changed == 'true' + uses: ad-m/github-push-action@master + with: + github_token: ${{ inputs.github_token }} + branch: ${{ github.ref }} + tags: true diff --git a/.github/workflows/basic.yaml b/.github/workflows/basic.yaml index a9e699f1c..8c5721efe 100644 --- a/.github/workflows/basic.yaml +++ b/.github/workflows/basic.yaml @@ -1,13 +1,15 @@ # Based on https://github.com/actions-rs/example/blob/master/.github/workflows/quickstart.yml +name: Basic on: pull_request: push: branches: - main - - releases/** -name: Basic +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: test: @@ -20,7 +22,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.75.0 + toolchain: 1.78.0 override: true - name: Install protoc @@ -29,9 +31,14 @@ jobs: repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Cache build artifacts - uses: useblacksmith/rust-cache@v3 + uses: useblacksmith/rust-cache@v3.0.1 + id: cache with: - shared-key: "cache" + shared-key: "cache-tests" + + - name: Log crates.toml + if: steps.cache.outputs.cache-hit == 'true' + run: cat /home/runner/.cargo/.crates.toml - name: Run tests uses: actions-rs/cargo@v1 @@ -52,14 +59,29 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.75.0 + toolchain: 1.78.0 target: wasm32-unknown-unknown + default: true override: true + - name: Install cosmwasm-check compatible toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: 1.75.0 + target: wasm32-unknown-unknown + default: false + override: false + - name: Cache build artifacts - uses: useblacksmith/rust-cache@v3 + id: cache + uses: useblacksmith/rust-cache@v3.0.1 with: - shared-key: "cache" + shared-key: "cache-cosmwasm-compilation" + + - name: Log crates.toml + if: steps.cache.outputs.cache-hit == 'true' + run: cat /home/runner/.cargo/.crates.toml - name: Build wasm release run: | @@ -69,28 +91,68 @@ jobs: (cd $C && cargo build --release --lib --target wasm32-unknown-unknown --locked) done + - name: Build ITS release + working-directory: ./interchain-token-service + run: cargo build --release --target wasm32-unknown-unknown --locked + + # cosmwasm-check v1.3.x is used to check for compatibility with wasmvm v1.3.x used by Axelar + # Older rust toolchain is required to install cosmwasm-check v1.3.x - name: Install cosmwasm-check uses: actions-rs/cargo@v1 with: command: install - args: --debug --version 1.3.3 --locked cosmwasm-check + toolchain: 1.75.0 + args: --version 1.3.4 --locked cosmwasm-check - name: Check wasm contracts run: cosmwasm-check ./target/wasm32-unknown-unknown/release/*.wasm + ampd-compilation: + name: Ampd Release Compilation + runs-on: blacksmith-16vcpu-ubuntu-2204 + steps: + - uses: actions/checkout@v4 + + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: 1.78.0 + target: wasm32-unknown-unknown + override: true + + - name: Install protoc + uses: arduino/setup-protoc@v2 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Cache build artifacts + id: cache + uses: useblacksmith/rust-cache@v3.0.1 + with: + shared-key: "cache-ampd-compilation" + + - name: Log crates.toml + if: steps.cache.outputs.cache-hit == 'true' + run: cat /home/runner/.cargo/.crates.toml + + - name: Build ampd + working-directory: ./ampd + run: cargo build --release --locked + lints: name: Lints runs-on: blacksmith-16vcpu-ubuntu-2204 steps: - uses: actions/checkout@v4 - - name: Install stable toolchain + - name: Install nightly toolchain uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.75.0 + toolchain: nightly + components: rustfmt override: true - components: rustfmt, clippy - name: Install protoc uses: arduino/setup-protoc@v2 @@ -98,9 +160,14 @@ jobs: repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Cache build artifacts - uses: useblacksmith/rust-cache@v3 + uses: useblacksmith/rust-cache@v3.0.1 + id: cache with: - shared-key: "cache" + shared-key: "cache-lints" + + - name: Log crates.toml + if: steps.cache.outputs.cache-hit == 'true' + run: cat /home/runner/.cargo/.crates.toml - name: Install cargo-sort uses: baptiste0928/cargo-install@v2 @@ -113,6 +180,14 @@ jobs: command: fmt args: --all -- --check + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: 1.78.0 + components: clippy + override: true + - name: Run cargo sort uses: actions-rs/cargo@v1 with: diff --git a/.github/workflows/build-ampd-main.yaml b/.github/workflows/build-ampd-main.yaml index 18bc7d1aa..f273aca28 100644 --- a/.github/workflows/build-ampd-main.yaml +++ b/.github/workflows/build-ampd-main.yaml @@ -1,4 +1,4 @@ -name: Amplifier - Build main branch +name: ampd (push to main) - Build and push image to ECR on: push: diff --git a/.github/workflows/build-ampd-release.yaml b/.github/workflows/build-ampd-release.yaml index 1a7537716..d1de5e86c 100644 --- a/.github/workflows/build-ampd-release.yaml +++ b/.github/workflows/build-ampd-release.yaml @@ -1,4 +1,4 @@ -name: Amplifier - Build Release +name: ampd - Build and release binary and image on: workflow_dispatch: @@ -14,11 +14,15 @@ jobs: name: Validate tag and extract semver outputs: semver: ${{ steps.extract_semver.outputs.semver }} + version: ${{ steps.extract_semver.outputs.version }} steps: - name: Extract semver from tag id: extract_semver run: | - echo "semver=$(echo ${{ github.event.inputs.tag }} | sed 's/ampd-//')" >> $GITHUB_OUTPUT + full_semver=$(echo ${{ github.event.inputs.tag }} | sed 's/ampd-//') + version_number=$(echo $full_semver | sed 's/^v//') + echo "semver=$full_semver" >> $GITHUB_OUTPUT + echo "version=$version_number" >> $GITHUB_OUTPUT - name: Validate tag env: @@ -35,8 +39,8 @@ jobs: needs: extract-semver strategy: matrix: - os: [ubuntu-22.04, macos-12] - arch: [amd64, arm64] + os: [ ubuntu-22.04, macos-12 ] + arch: [ amd64, arm64 ] permissions: contents: write @@ -60,7 +64,7 @@ jobs: - name: Set up Rust uses: actions-rs/toolchain@v1 with: - toolchain: 1.75 + toolchain: 1.78 override: true - name: Import GPG key @@ -176,6 +180,25 @@ jobs: run: | aws s3 cp ./ampdbin ${S3_PATH}/ --recursive + - name: Prepare source directory structure for r2 upload + id: prepare-r2-release + run: | + version="${{ needs.extract-semver.outputs.version }}" + mkdir -p "./${version}" + cp -R ./ampdbin/. "./${version}/" + echo "release-dir=./${version}" >> $GITHUB_OUTPUT + echo "r2-destination-dir=./releases/ampd/" >> $GITHUB_OUTPUT + + + - uses: ryand56/r2-upload-action@latest + with: + r2-account-id: ${{ secrets.R2_ACCOUNT_ID }} + r2-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_CF }} + r2-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_CF }} + r2-bucket: ${{ secrets.R2_BUCKET }} + source-dir: ${{ steps.prepare-r2-release.outputs.release-dir }} + destination-dir: ${{ steps.prepare-r2-release.outputs.r2-destination-dir }} + release-docker: runs-on: ubuntu-22.04 needs: extract-semver diff --git a/.github/workflows/build-contracts-and-push-to-r2.yaml b/.github/workflows/build-contracts-and-push-to-r2.yaml new file mode 100644 index 000000000..fb0e3f79c --- /dev/null +++ b/.github/workflows/build-contracts-and-push-to-r2.yaml @@ -0,0 +1,171 @@ +name: Amplifier wasm contracts - Upload wasm binaries to Cloudflare R2 bucket + +on: + push: + branches: + - main + create: + tags: + - '*-v[0-9]+.[0-9]+.[0-9]+' + workflow_dispatch: + inputs: + ref: + description: Github branch or tag to checkout for compilation + required: true + default: main + type: string + + +jobs: + compile-and-upload: + name: Compile contracts and push to R2 + runs-on: ubuntu-22.04 + permissions: + contents: write + packages: write + id-token: write + steps: + - name: Get tag + id: get-tag + run: | + echo "github_ref=$GITHUB_REF" + if [[ $GITHUB_REF == refs/tags/* ]]; then + echo "tag=${GITHUB_REF#refs/tags/}" + echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT + elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + input_ref="${{ github.event.inputs.ref }}" + if [[ $input_ref =~ ^([a-zA-Z-]+)-v([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then + echo "tag=$input_ref" + echo "tag=$input_ref" >> $GITHUB_OUTPUT + else + echo "tag=" + echo "tag=" >> $GITHUB_OUTPUT + fi + else + echo "tag=" + echo "tag=" >> $GITHUB_OUTPUT + fi + + + - name: Check for release information from tag + id: check-release + run: | + tag="${{ steps.get-tag.outputs.tag }}" + is_release="false" + + if [[ $tag =~ ^([a-zA-Z-]+)-v([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then + is_release="true" + crate_name="${BASH_REMATCH[1]}" + crate_version="${BASH_REMATCH[2]}" + + if [[ $crate_name == "ampd" ]]; then + echo "Ampd release. Ignoring the wasm build and upload." + exit 1 + fi + + echo "Is release: $is_release" + echo "Crate Name: $crate_name" + echo "Crate Version: $crate_version" + + echo "is-release=$is_release" >> $GITHUB_OUTPUT + echo "crate-name=$crate_name" >> $GITHUB_OUTPUT + echo "crate-version=$crate_version" >> $GITHUB_OUTPUT + else + echo "Is release: $is_release" + echo "Not a release tag. Skipping crate name and version extraction." + echo "is-release=$is_release" >> $GITHUB_OUTPUT + fi + + + - name: Determine checkout ref + id: get-checkout-ref + run: | + if [[ $GITHUB_REF == refs/tags/* ]]; then + echo "ref=$GITHUB_REF" >> $GITHUB_OUTPUT + elif [ "${{ github.event_name }}" == "push" ]; then + echo "ref=main" >> $GITHUB_OUTPUT + else + echo "ref=${{ inputs.ref }}" >> $GITHUB_OUTPUT + fi + + + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: "0" + path: axelar-amplifier + submodules: recursive + ref: ${{ steps.get-checkout-ref.outputs.ref }} + token: ${{ secrets.INTEROP_CI_ACTION_TOKEN }} + + + - name: Import GPG key + id: import_gpg + uses: crazy-max/ghaction-import-gpg@v4 + with: + gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.GPG_PASSPHRASE }} + + + - name: Compile all amplifier contracts + id: compile-contracts + run: | + cd axelar-amplifier + docker run --rm -v "$(pwd)":/code \ + --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ + --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ + cosmwasm/optimizer:0.16.0 + + commit_hash=$(git rev-parse --short HEAD) + cd .. + mkdir -p ./$commit_hash/ + cp -R axelar-amplifier/artifacts/* ./$commit_hash/ + echo "wasm-directory=./$commit_hash" >> $GITHUB_OUTPUT + + + - name: Prepare and sign release artifacts + if: steps.check-release.outputs.is-release == 'true' + id: prepare-release + run: | + cd ${{ steps.compile-contracts.outputs.wasm-directory }} + crate_name="${{ steps.check-release.outputs.crate-name }}" + crate_version="${{ steps.check-release.outputs.crate-version }}" + wasm_file=$(find . -name "${crate_name//-/_}.wasm") + checksum_file=$(find . -name "checksums.txt") + + if [ -z "$wasm_file" ]; then + echo "Error: Could not find .wasm file for $crate_name" + exit 1 + fi + + mkdir -p "../${crate_version}" + cp "$wasm_file" "../${crate_version}/${crate_name}.wasm" + cp "$checksum_file" "../${crate_version}/" + + gpg --armor --detach-sign ../${crate_version}/${crate_name}.wasm + gpg --armor --detach-sign ../${crate_version}/checksums.txt + + echo "release-artifacts-dir=./${crate_version}" >> $GITHUB_OUTPUT + echo "r2-destination-dir=./releases/cosmwasm/${crate_name}" >> $GITHUB_OUTPUT + + + - uses: ryand56/r2-upload-action@latest + if: steps.check-release.outputs.is-release == 'true' + with: + r2-account-id: ${{ secrets.R2_ACCOUNT_ID }} + r2-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_CF }} + r2-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_CF }} + r2-bucket: ${{ secrets.R2_BUCKET }} + source-dir: ${{ steps.prepare-release.outputs.release-artifacts-dir }} + destination-dir: ${{ steps.prepare-release.outputs.r2-destination-dir }} + + + - uses: ryand56/r2-upload-action@latest + if: steps.check-release.outputs.is-release != 'true' + with: + r2-account-id: ${{ secrets.R2_ACCOUNT_ID }} + r2-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_CF }} + r2-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_CF }} + r2-bucket: ${{ secrets.R2_BUCKET }} + source-dir: ${{ steps.compile-contracts.outputs.wasm-directory }} + destination-dir: ./pre-releases/cosmwasm/ diff --git a/.github/workflows/codecov.yaml b/.github/workflows/codecov.yaml index 3536f0b8a..11cf307cc 100644 --- a/.github/workflows/codecov.yaml +++ b/.github/workflows/codecov.yaml @@ -7,6 +7,10 @@ on: - main - releases/** +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: coverage: runs-on: blacksmith-16vcpu-ubuntu-2204 @@ -19,7 +23,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.75.0 + toolchain: 1.78.0 override: true - name: Install protoc @@ -31,9 +35,14 @@ jobs: run: sudo apt-get install libclang-dev - name: Cache build artifacts - uses: useblacksmith/rust-cache@v3 + uses: useblacksmith/rust-cache@v3.0.1 + id: cache with: - shared-key: "cache" + shared-key: "cache-codecov" + + - name: Log crates.toml + if: steps.cache.outputs.cache-hit == 'true' + run: cat /home/runner/.cargo/.crates.toml - name: Install cargo-llvm-cov uses: taiki-e/install-action@cargo-llvm-cov diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 6b762fd79..e022a6a2a 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,4 +1,4 @@ -name: Release +name: Update and tag release version on: workflow_dispatch: @@ -17,6 +17,7 @@ on: - service-registry - voting-verifier - coordinator + - axelarnet-gateway dry-run: description: Dry run type: boolean @@ -28,25 +29,27 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 + token: ${{ secrets.INTEROP_CI_ACTION_TOKEN }} - name: Setup variables for sub-project to release id: setup-variables shell: bash run: | binary="${{ github.event.inputs.binary-to-release }}" declare -A binaries_data=( - ["ampd"]="ampd,major-ampd,minor-ampd," - ["router"]="router,/(major-router)|(major-contracts)|(major-connection-router)/,/(minor-router)|(minor-contracts)|(minor-connection-router)/,contracts/router packages" - ["gateway"]="gateway,/(major-gateway)|(major-contracts)/,/(minor-gateway)|(minor-contracts)/,contracts/gateway packages" - ["multisig"]="multisig,/(major-multisig)|(major-contracts)/,/(minor-multisig)|(minor-contracts)/,contracts/multisig packages" - ["multisig-prover"]="multisig-prover,/(major-multisig-prover)|(major-contracts)/,/(minor-multisig-prover)|(minor-contracts)/,contracts/multisig-prover packages" - ["nexus-gateway"]="nexus-gateway,/(major-nexus-gateway)|(major-contracts)/,/(minor-nexus-gateway)|(minor-contracts)/,contracts/nexus-gateway packages" - ["rewards"]="rewards,/(major-rewards)|(major-contracts)/,/(minor-rewards)|(minor-contracts)/,contracts/rewards packages" - ["service-registry"]="service-registry,/(major-service-registry)|(major-contracts)/,/(minor-service-registry)|(minor-contracts)/,contracts/service-registry packages" - ["voting-verifier"]="voting-verifier,/(major-voting-verifier)|(major-contracts)/,/(minor-voting-verifier)|(minor-contracts)/,contracts/voting-verifier packages" - ["coordinator"]="coordinator,/(major-coordinator)|(major-contracts)/,/(minor-coordinator)|(minor-contracts)/,contracts/coordinator packages" + ["ampd"]="ampd,/(major)|(major-ampd)/,/(minor)|(minor-ampd)/,ampd packages" + ["router"]="router,/(major)|(major-router)|(major-contracts)|(major-connection-router)/,/(minor)|(minor-router)|(minor-contracts)|(minor-connection-router)/,contracts/router packages" + ["gateway"]="gateway,/(major)|(major-gateway)|(major-contracts)/,/(minor)|(minor-gateway)|(minor-contracts)/,contracts/gateway packages" + ["multisig"]="multisig,/(major)|(major-multisig)|(major-contracts)/,/(minor)|(minor-multisig)|(minor-contracts)/,contracts/multisig packages" + ["multisig-prover"]="multisig-prover,/(major)|(major-multisig-prover)|(major-contracts)/,/(minor)|(minor-multisig-prover)|(minor-contracts)/,contracts/multisig-prover packages" + ["nexus-gateway"]="nexus-gateway,/(major)|(major-nexus-gateway)|(major-contracts)/,/(minor)|(minor-nexus-gateway)|(minor-contracts)/,contracts/nexus-gateway packages" + ["rewards"]="rewards,/(major)|(major-rewards)|(major-contracts)/,/(minor)|(minor-rewards)|(minor-contracts)/,contracts/rewards packages" + ["service-registry"]="service-registry,/(major)|(major-service-registry)|(major-contracts)/,/(minor)|(minor-service-registry)|(minor-contracts)/,contracts/service-registry packages" + ["voting-verifier"]="voting-verifier,/(major)|(major-voting-verifier)|(major-contracts)/,/(minor)|(minor-voting-verifier)|(minor-contracts)/,contracts/voting-verifier packages" + ["coordinator"]="coordinator,/(major)|(major-coordinator)|(major-contracts)/,/(minor)|(minor-coordinator)|(minor-contracts)/,contracts/coordinator packages" + ["axelarnet-gateway"]="axelarnet-gateway,/(major)|(major-axelarnet-gateway)|(major-contracts)/,/(minor)|(minor-axelarnet-gateway)|(minor-contracts)/,contracts/axelarnet-gateway packages" ) if [[ -n "${binaries_data[$binary]}" ]]; then @@ -68,3 +71,4 @@ jobs: major-pattern: ${{ steps.setup-variables.outputs.major-pattern }} minor-pattern: ${{ steps.setup-variables.outputs.minor-pattern }} change-path: ${{ steps.setup-variables.outputs.change-path }} + github_token: ${{ secrets.INTEROP_CI_ACTION_TOKEN }} diff --git a/.gitignore b/.gitignore index 0944804d6..1c3a92dba 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ *.iml .idea .vscode + +config.toml diff --git a/Cargo.lock b/Cargo.lock index e2ea3c80a..fc55f41ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,9 +25,9 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -61,9 +61,9 @@ dependencies = [ [[package]] name = "aes-gcm" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ "aead", "aes", @@ -75,9 +75,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ "getrandom", "once_cell", @@ -86,20 +86,21 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.3" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "once_cell", "version_check", + "zerocopy", ] [[package]] name = "aho-corasick" -version = "1.1.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -125,126 +126,122 @@ dependencies = [ "alloc-no-stdlib", ] -[[package]] -name = "alloy-json-abi" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e30946aa6173020259055a44971f5cf40a7d76c931d209caeb51b333263df4f" -dependencies = [ - "alloy-primitives", - "alloy-sol-type-parser", - "serde", -] - [[package]] name = "alloy-primitives" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8aa973e647ec336810a9356af8aea787249c9d00b1525359f3db29a68d231b" +checksum = "ccb3ead547f4532bc8af961649942f0b9c16ee9226e26caa3f38420651cc0bf4" dependencies = [ + "alloy-rlp", "bytes", "cfg-if", "const-hex", "derive_more", + "getrandom", "hex-literal", "itoa", + "k256", + "keccak-asm", + "proptest", + "rand", "ruint", "serde", "tiny-keccak", ] +[[package]] +name = "alloy-rlp" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d58d9f5da7b40e9bfff0b7e7816700be4019db97d4b6359fe7f94a9e22e42ac" +dependencies = [ + "arrayvec", + "bytes", +] + [[package]] name = "alloy-sol-macro" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dbd17d67f3e89478c8a634416358e539e577899666c927bc3d2b1328ee9b6ca" +checksum = "2b40397ddcdcc266f59f959770f601ce1280e699a91fc1862f29cef91707cd09" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error", - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] name = "alloy-sol-macro-expander" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6da95adcf4760bb4b108fefa51d50096c5e5fdd29ee72fed3e86ee414f2e34" +checksum = "867a5469d61480fea08c7333ffeca52d5b621f5ca2e44f271b117ec1fc9a0525" dependencies = [ - "alloy-json-abi", "alloy-sol-macro-input", "const-hex", - "heck 0.4.1", - "indexmap 2.0.0", + "heck 0.5.0", + "indexmap 2.2.6", "proc-macro-error", - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", "syn-solidity", "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32c8da04c1343871fb6ce5a489218f9c85323c8340a36e9106b5fc98d4dd59d5" +checksum = "2e482dc33a32b6fadbc0f599adea520bd3aaa585c141a80b404d0a3e3fa72528" dependencies = [ - "alloy-json-abi", "const-hex", "dunce", "heck 0.5.0", - "proc-macro2 1.0.67", - "quote 1.0.33", - "serde_json", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", "syn-solidity", ] -[[package]] -name = "alloy-sol-type-parser" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368cae4dc052cad1d8f72eb2ae0c38027116933eeb49213c200a9e9875f208d7" -dependencies = [ - "winnow", -] - [[package]] name = "alloy-sol-types" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a64d2d2395c1ac636b62419a7b17ec39031d6b2367e66e9acbf566e6055e9c" +checksum = "a91ca40fa20793ae9c3841b83e74569d1cc9af29a2f5237314fd3452d51e38c7" dependencies = [ - "alloy-json-abi", "alloy-primitives", "alloy-sol-macro", "const-hex", + "serde", ] [[package]] name = "ampd" -version = "0.4.0" +version = "1.0.0" dependencies = [ "async-trait", "axelar-wasm-std", "axum 0.7.5", - "base64 0.21.4", + "base64 0.21.7", "bcs", "clap", "config", - "cosmos-sdk-proto 0.16.0", "cosmrs", "cosmwasm-std", + "der 0.7.9", "deref-derive", "dirs", "ecdsa", + "ed25519 2.2.3", "elliptic-curve", "enum-display-derive", "error-stack", - "ethers", + "ethers-contract", + "ethers-core", + "ethers-providers", "events", "events-derive", "evm-gateway", @@ -254,32 +251,34 @@ dependencies = [ "humantime-serde", "itertools 0.11.0", "k256", - "mockall", + "mockall 0.11.4", "move-core-types", "multisig", + "multiversx-sdk", "num-traits", "prost 0.11.9", "prost-types", "rand", "random-string", "report", - "reqwest 0.11.25", + "reqwest 0.11.27", "router-api", "serde", "serde_json", - "serde_with 3.3.0", + "serde_with 3.8.1", "service-registry", - "sha3 0.10.8", + "sha3", + "sui-gateway", "sui-json-rpc-types", - "sui-types", + "sui-types 0.1.0", "tendermint 0.33.0", "tendermint-rpc", "thiserror", "tokio", "tokio-stream", - "tokio-util 0.7.9", + "tokio-util", "toml 0.5.11", - "tonic 0.8.3", + "tonic 0.9.2", "tonic-build", "tracing", "tracing-core", @@ -309,7 +308,7 @@ dependencies = [ [[package]] name = "anemo" version = "0.0.0" -source = "git+https://github.com/mystenlabs/anemo.git?rev=1169850e6af127397068cd86764c29b1d49dbe35#1169850e6af127397068cd86764c29b1d49dbe35" +source = "git+https://github.com/mystenlabs/anemo.git?rev=26d415eb9aa6a2417be3c03c57d6e93c30bd1ad7#26d415eb9aa6a2417be3c03c57d6e93c30bd1ad7" dependencies = [ "anyhow", "async-trait", @@ -318,7 +317,7 @@ dependencies = [ "ed25519 1.5.3", "futures", "hex", - "http 0.2.9", + "http 0.2.12", "matchit 0.5.0", "pin-project-lite", "pkcs8 0.9.0", @@ -326,16 +325,16 @@ dependencies = [ "quinn-proto", "rand", "rcgen", - "ring", - "rustls 0.21.7", - "rustls-webpki 0.101.5", + "ring 0.16.20", + "rustls 0.21.12", + "rustls-webpki 0.101.7", "serde", "serde_json", - "socket2 0.5.4", + "socket2", "tap", "thiserror", "tokio", - "tokio-util 0.7.9", + "tokio-util", "tower", "tracing", "x509-parser", @@ -343,66 +342,58 @@ dependencies = [ [[package]] name = "anstream" -version = "0.5.0" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.3" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.1" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "2.1.0" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anyhow" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" - -[[package]] -name = "arbitrary" -version = "1.3.2" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" -dependencies = [ - "derive_arbitrary", -] +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "ark-bls12-381" @@ -411,9 +402,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" dependencies = [ "ark-ec", - "ark-ff", - "ark-serialize", - "ark-std", + "ark-ff 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", ] [[package]] @@ -423,8 +414,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" dependencies = [ "ark-ec", - "ark-ff", - "ark-std", + "ark-ff 0.4.2", + "ark-std 0.4.0", ] [[package]] @@ -434,15 +425,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3a13b34da09176a8baba701233fdffbaa7c1b1192ce031a3da4e55ce1f1a56" dependencies = [ "ark-ec", - "ark-ff", + "ark-ff 0.4.2", "ark-relations", - "ark-serialize", + "ark-serialize 0.4.2", "ark-snark", - "ark-std", + "ark-std 0.4.0", "blake2", "derivative", "digest 0.10.7", - "sha2 0.10.7", + "sha2 0.10.8", ] [[package]] @@ -451,10 +442,10 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" dependencies = [ - "ark-ff", + "ark-ff 0.4.2", "ark-poly", - "ark-serialize", - "ark-std", + "ark-serialize 0.4.2", + "ark-std 0.4.0", "derivative", "hashbrown 0.13.2", "itertools 0.10.5", @@ -462,33 +453,73 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint 0.4.5", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + [[package]] name = "ark-ff" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" dependencies = [ - "ark-ff-asm", - "ark-ff-macros", - "ark-serialize", - "ark-std", + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", "derivative", "digest 0.10.7", "itertools 0.10.5", - "num-bigint 0.4.4", + "num-bigint 0.4.5", "num-traits", "paste", - "rustc_version", + "rustc_version 0.4.0", "zeroize", ] +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote 1.0.36", + "syn 1.0.109", +] + [[package]] name = "ark-ff-asm" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" dependencies = [ - "quote 1.0.33", + "quote 1.0.36", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint 0.4.5", + "num-traits", + "quote 1.0.36", "syn 1.0.109", ] @@ -498,10 +529,10 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ - "num-bigint 0.4.4", + "num-bigint 0.4.5", "num-traits", - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", ] @@ -513,11 +544,11 @@ checksum = "20ceafa83848c3e390f1cbf124bc3193b3e639b3f02009e0e290809a501b95fc" dependencies = [ "ark-crypto-primitives", "ark-ec", - "ark-ff", + "ark-ff 0.4.2", "ark-poly", "ark-relations", - "ark-serialize", - "ark-std", + "ark-serialize 0.4.2", + "ark-std 0.4.0", ] [[package]] @@ -526,9 +557,9 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" dependencies = [ - "ark-ff", - "ark-serialize", - "ark-std", + "ark-ff 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", "derivative", "hashbrown 0.13.2", ] @@ -539,8 +570,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00796b6efc05a3f48225e59cb6a2cda78881e7c390872d5786aaf112f31fb4f0" dependencies = [ - "ark-ff", - "ark-std", + "ark-ff 0.4.2", + "ark-std 0.4.0", "tracing", ] @@ -551,8 +582,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3975a01b0a6e3eae0f72ec7ca8598a6620fc72fa5981f6f5cca33b7cd788f633" dependencies = [ "ark-ec", - "ark-ff", - "ark-std", + "ark-ff 0.4.2", + "ark-std 0.4.0", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", ] [[package]] @@ -562,9 +603,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" dependencies = [ "ark-serialize-derive", - "ark-std", + "ark-std 0.4.0", "digest 0.10.7", - "num-bigint 0.4.4", + "num-bigint 0.4.5", ] [[package]] @@ -573,8 +614,8 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", ] @@ -584,10 +625,20 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84d3cc6833a335bb8a600241889ead68ee89a3cf8448081fb7694c0fe503da63" dependencies = [ - "ark-ff", + "ark-ff 0.4.2", "ark-relations", - "ark-serialize", - "ark-std", + "ark-serialize 0.4.2", + "ark-std 0.4.0", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand", ] [[package]] @@ -612,15 +663,6 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" -[[package]] -name = "ascii-canvas" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" -dependencies = [ - "term", -] - [[package]] name = "asn1-rs" version = "0.5.2" @@ -643,10 +685,10 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", - "synstructure", + "synstructure 0.12.6", ] [[package]] @@ -655,8 +697,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", ] @@ -674,6 +716,15 @@ dependencies = [ "tokio", ] +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener", +] + [[package]] name = "async-stream" version = "0.3.5" @@ -691,25 +742,20 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] -[[package]] -name = "async-task" -version = "4.3.0" -source = "git+https://github.com/mystenmark/async-task?rev=4e45b26e11126b191701b9b2ce5e2346b8d7682f#4e45b26e11126b191701b9b2ce5e2346b8d7682f" - [[package]] name = "async-trait" -version = "0.1.73" +version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -720,19 +766,24 @@ checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" dependencies = [ "futures", "pharos", - "rustc_version", + "rustc_version 0.4.0", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "auto_impl" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ - "proc-macro-error", - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 1.0.109", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -743,22 +794,27 @@ checksum = "7460f7dd8e100147b82a63afca1a20eb6c231ee36b90ba7272e14951cb58af59" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "axelar-wasm-std" -version = "0.1.0" +version = "1.0.0" dependencies = [ + "alloy-primitives", + "axelar-wasm-std-derive", "bs58 0.5.1", "cosmwasm-schema", "cosmwasm-std", "cw-multi-test", "cw-storage-plus 1.2.0", + "cw2 1.1.2", "error-stack", "flagset", "hex", + "into-inner-derive", + "itertools 0.11.0", "lazy_static", "num-traits", "rand", @@ -767,21 +823,43 @@ dependencies = [ "schemars", "serde", "serde_json", - "sha3 0.10.8", + "sha3", "strum 0.25.0", + "sui-types 1.0.0", "thiserror", "valuable", ] [[package]] name = "axelar-wasm-std-derive" +version = "1.0.0" +dependencies = [ + "axelar-wasm-std", + "error-stack", + "quote 1.0.36", + "report", + "syn 2.0.68", + "thiserror", +] + +[[package]] +name = "axelarnet-gateway" version = "0.1.0" dependencies = [ "axelar-wasm-std", + "client", + "cosmwasm-schema", + "cosmwasm-std", + "cw-multi-test", + "cw-storage-plus 1.2.0", + "cw2 1.1.2", "error-stack", - "quote 1.0.33", + "msgs-derive", "report", - "syn 2.0.37", + "router-api", + "serde", + "serde_json", + "sha3", "thiserror", ] @@ -793,16 +871,16 @@ checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", "axum-core 0.3.4", - "base64 0.21.4", + "base64 0.21.7", "bitflags 1.3.2", "bytes", "futures-util", "headers", - "http 0.2.9", - "http-body 0.4.5", - "hyper 0.14.27", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.29", "itoa", - "matchit 0.7.2", + "matchit 0.7.3", "memchr", "mime", "percent-encoding", @@ -837,7 +915,7 @@ dependencies = [ "hyper 1.3.1", "hyper-util", "itoa", - "matchit 0.7.2", + "matchit 0.7.3", "memchr", "mime", "percent-encoding", @@ -864,8 +942,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 0.2.9", - "http-body 0.4.5", + "http 0.2.12", + "http-body 0.4.6", "mime", "rustversion", "tower-layer", @@ -895,9 +973,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", @@ -920,6 +998,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base32" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa" + [[package]] name = "base64" version = "0.13.1" @@ -928,9 +1012,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.4" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" @@ -946,9 +1030,9 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bcs" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bd3ffe8b19a604421a5d461d4a70346223e535903fbc3067138bddbebddcf77" +checksum = "85b6598a2f5d564fb7855dc6b06fd1c38cff5a72bd8b863a4d021938497b440a" dependencies = [ "serde", "thiserror", @@ -960,6 +1044,15 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" +dependencies = [ + "serde", +] + [[package]] name = "bellpepper" version = "0.4.1" @@ -985,33 +1078,32 @@ dependencies = [ ] [[package]] -name = "bincode" -version = "1.3.3" +name = "better_any" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +checksum = "b359aebd937c17c725e19efcb661200883f04c49c53e7132224dac26da39d4a0" dependencies = [ - "serde", + "better_typeid_derive", ] [[package]] -name = "bindgen" -version = "0.65.1" +name = "better_typeid_derive" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" +checksum = "3deeecb812ca5300b7d3f66f730cc2ebd3511c3d36c691dd79c165d5b19a26e3" dependencies = [ - "bitflags 1.3.2", - "cexpr", - "clang-sys", - "lazy_static", - "lazycell", - "peeking_take_while", - "prettyplease 0.2.15", - "proc-macro2 1.0.67", - "quote 1.0.33", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 1.0.109", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", ] [[package]] @@ -1024,14 +1116,27 @@ dependencies = [ "hmac", "k256", "once_cell", - "pbkdf2 0.12.2", + "pbkdf2", "rand_core 0.6.4", "ripemd", - "sha2 0.10.7", + "sha2 0.10.8", "subtle", "zeroize", ] +[[package]] +name = "bip39" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" +dependencies = [ + "bitcoin_hashes 0.11.0", + "rand", + "rand_core 0.6.4", + "serde", + "unicode-normalization", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -1053,6 +1158,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" +[[package]] +name = "bitcoin_hashes" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" + [[package]] name = "bitcoin_hashes" version = "0.12.0" @@ -1070,9 +1181,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bitmaps" @@ -1124,7 +1235,7 @@ checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" dependencies = [ "arrayref", "arrayvec", - "constant_time_eq 0.3.0", + "constant_time_eq", ] [[package]] @@ -1135,21 +1246,7 @@ checksum = "94230421e395b9920d23df13ea5d77a20e1725331f90fbbf6df6040b33f756ae" dependencies = [ "arrayref", "arrayvec", - "constant_time_eq 0.3.0", -] - -[[package]] -name = "blake3" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "199c42ab6972d92c9f8995f086273d25c42fc0f7b2a1fcefba465c1352d25ba5" -dependencies = [ - "arrayref", - "arrayvec", - "cc", - "cfg-if", - "constant_time_eq 0.3.0", - "digest 0.10.7", + "constant_time_eq", ] [[package]] @@ -1158,7 +1255,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "block-padding 0.2.1", "generic-array", ] @@ -1171,12 +1267,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block-padding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" - [[package]] name = "block-padding" version = "0.3.3" @@ -1188,9 +1278,9 @@ dependencies = [ [[package]] name = "blst" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c94087b935a822949d3291a9989ad2b2051ea141eda0fd4e478a75f6aa3e604b" +checksum = "62dc83a094a71d43eeadd254b1ec2d24cb6a0bb6cadce00df51f0db594711a32" dependencies = [ "cc", "glob", @@ -1206,7 +1296,6 @@ checksum = "7a8a8ed6fefbeef4a8c7b460e4110e12c5e22a5b7cf32621aae6ad650c4dcf29" dependencies = [ "blst", "byte-slice-cast", - "ec-gpu", "ff", "group", "pairing", @@ -1223,9 +1312,9 @@ checksum = "56953345e39537a3e18bdaeba4cb0c58a78c1f61f361dc0fa7c5c7340ae87c5f" [[package]] name = "brotli" -version = "3.3.4" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -1234,9 +1323,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", @@ -1254,28 +1343,18 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ - "sha2 0.10.7", + "sha2 0.10.8", "tinyvec", ] [[package]] -name = "bulletproofs" -version = "4.0.0" +name = "bstr" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40e698f1df446cc6246afd823afbe2d121134d089c9102c1dd26d1264991ba32" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ - "byteorder", - "clear_on_drop", - "curve25519-dalek-ng", - "digest 0.9.0", - "merlin", - "rand", - "rand_core 0.6.4", + "memchr", "serde", - "serde_derive", - "sha3 0.9.1", - "subtle-ng", - "thiserror", ] [[package]] @@ -1292,79 +1371,58 @@ checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "bytecount" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" +checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" [[package]] name = "bytemuck" -version = "1.14.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5" [[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" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" dependencies = [ "serde", ] [[package]] -name = "bzip2" -version = "0.4.4" +name = "camino" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" dependencies = [ - "bzip2-sys", - "libc", + "serde", ] [[package]] -name = "bzip2-sys" -version = "0.1.11+1.0.8" +name = "cargo-platform" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" dependencies = [ - "cc", - "libc", - "pkg-config", + "serde", ] [[package]] -name = "camino" -version = "1.1.6" +name = "cargo_metadata" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo-platform" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cfa25e60aea747ec7e1124f238816749faa93759c6ff5b31f1ccdda137f4479" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", - "semver", + "semver 1.0.23", "serde", "serde_json", "thiserror", @@ -1381,22 +1439,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "jobserver", - "libc", -] - -[[package]] -name = "cexpr" -version = "0.6.0" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" [[package]] name = "cfg-if" @@ -1406,15 +1451,17 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", + "js-sys", "num-traits", "serde", - "windows-targets 0.48.5", + "wasm-bindgen", + "windows-targets 0.52.5", ] [[package]] @@ -1427,22 +1474,11 @@ dependencies = [ "inout", ] -[[package]] -name = "clang-sys" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" -dependencies = [ - "glob", - "libc", - "libloading", -] - [[package]] name = "clap" -version = "4.4.4" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d7b8d5ec32af0fadc644bf1fd509a688c2103b185644bb1e29d164e0703136" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" dependencies = [ "clap_builder", "clap_derive", @@ -1450,46 +1486,38 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.4" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5179bb514e4d7c2051749d8fcefa2ed6d06a9f4e6d69faf3805f5d80b8cf8d56" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.11.1", + "terminal_size", ] [[package]] name = "clap_derive" -version = "4.4.2" +version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" dependencies = [ - "heck 0.4.1", - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "heck 0.5.0", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] name = "clap_lex" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" - -[[package]] -name = "clear_on_drop" -version = "0.2.5" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38508a63f4979f0048febc9966fadbd48e5dab31fd0ec6a3f151bbf4a74f7423" -dependencies = [ - "cc", -] +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "client" -version = "0.1.0" +version = "1.0.0" dependencies = [ "cosmwasm-std", "error-stack", @@ -1518,86 +1546,27 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "coins-bip32" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b6be4a5df2098cd811f3194f64ddb96c267606bffd9689ac7b0160097b01ad3" -dependencies = [ - "bs58 0.5.1", - "coins-core", - "digest 0.10.7", - "hmac", - "k256", - "serde", - "sha2 0.10.7", - "thiserror", -] - -[[package]] -name = "coins-bip39" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db8fba409ce3dc04f7d804074039eb68b960b0829161f8e06c95fea3f122528" -dependencies = [ - "bitvec 1.0.1", - "coins-bip32", - "hmac", - "once_cell", - "pbkdf2 0.12.2", - "rand", - "sha2 0.10.7", - "thiserror", -] - -[[package]] -name = "coins-core" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5286a0843c21f8367f7be734f89df9b822e0321d8bcce8d6e735aadff7d74979" -dependencies = [ - "base64 0.21.4", - "bech32", - "bs58 0.5.1", - "digest 0.10.7", - "generic-array", - "hex", - "ripemd", - "serde", - "serde_derive", - "sha2 0.10.7", - "sha3 0.10.8", - "thiserror", -] - -[[package]] -name = "collectable" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08abddbaad209601e53c7dd4308d8c04c06f17bb7df006434e586a22b83be45a" - [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "colored" -version = "2.0.4" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" dependencies = [ - "is-terminal", "lazy_static", "windows-sys 0.48.0", ] [[package]] name = "config" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d379af7f68bfc21714c6c7dea883544201741d2ce8274bb12fa54f89507f52a7" +checksum = "23738e11972c7643e4ec947840fc463b6a571afcd3e735bdfce7d03c7a784aca" dependencies = [ "async-trait", "json5", @@ -1612,23 +1581,35 @@ dependencies = [ "yaml-rust", ] +[[package]] +name = "consensus-config" +version = "0.1.0" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" +dependencies = [ + "fastcrypto", + "mysten-network", + "rand", + "serde", + "shared-crypto", +] + [[package]] name = "console" -version = "0.15.7" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" dependencies = [ "encode_unicode", "lazy_static", "libc", - "windows-sys 0.45.0", + "windows-sys 0.52.0", ] [[package]] name = "const-hex" -version = "1.11.4" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ff96486ccc291d36a958107caf2c0af8c78c0af7d31ae2f35ce055130de1a6" +checksum = "94fb8a24a26d37e1ffd45343323dc9fe6654ceea44c12f2fcb3d7ac29e610bc6" dependencies = [ "cfg-if", "cpufeatures", @@ -1639,15 +1620,9 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" - -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "constant_time_eq" @@ -1661,28 +1636,19 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "coordinator" -version = "0.1.2" +version = "1.0.0" dependencies = [ "axelar-wasm-std", - "axelar-wasm-std-derive", "cosmwasm-schema", "cosmwasm-std", "cw-multi-test", "cw-storage-plus 1.2.0", - "cw2 1.1.0", + "cw2 1.1.2", "error-stack", "integration-tests", + "msgs-derive", "multisig", "report", "router-api", @@ -1692,9 +1658,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -1702,9 +1668,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "core2" @@ -1715,18 +1681,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "cosmos-sdk-proto" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4776e787b24d9568dd61d3237eeb4eb321d622fb881b858c7b82806420e87d4" -dependencies = [ - "prost 0.11.9", - "prost-types", - "tendermint-proto 0.27.0", - "tonic 0.8.3", -] - [[package]] name = "cosmos-sdk-proto" version = "0.19.0" @@ -1736,6 +1690,7 @@ dependencies = [ "prost 0.11.9", "prost-types", "tendermint-proto 0.32.2", + "tonic 0.9.2", ] [[package]] @@ -1745,7 +1700,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af13955d6f356272e6def9ff5e2450a7650df536d8934f47052a20c76513d2f6" dependencies = [ "bip32", - "cosmos-sdk-proto 0.19.0", + "cosmos-sdk-proto", "ecdsa", "eyre", "getrandom", @@ -1800,8 +1755,8 @@ version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bb57855fbfc83327f8445ae0d413b1a05ac0d68c396ab4d122b2abd7bb82cb6" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", ] @@ -1811,7 +1766,7 @@ version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c1556156fdf892a55cced6115968b961eaaadd6f724a2c2cb7d1e168e32dd3" dependencies = [ - "base64 0.21.4", + "base64 0.21.7", "bech32", "bnum", "cosmwasm-crypto", @@ -1822,16 +1777,16 @@ dependencies = [ "schemars", "serde", "serde-json-wasm", - "sha2 0.10.7", + "sha2 0.10.8", "static_assertions", "thiserror", ] [[package]] name = "cosmwasm-storage" -version = "1.3.4" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73fc6766006aef09dea5db0c0728446e8c0e22b26eeb33c83ae70a500894ff3" +checksum = "66de2ab9db04757bcedef2b5984fbe536903ada4a8a9766717a4a71197ef34f6" dependencies = [ "cosmwasm-std", "serde", @@ -1839,77 +1794,33 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] -name = "crc" -version = "3.2.1" +name = "crate-git-revision" +version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +checksum = "c521bf1f43d31ed2f73441775ed31935d77901cb3451e44b38a1c1612fcbaf98" dependencies = [ - "crc-catalog", + "serde", + "serde_derive", + "serde_json", ] -[[package]] -name = "crc-catalog" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" - [[package]] name = "crc32fast" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.15" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ - "autocfg", "cfg-if", - "crossbeam-utils", - "memoffset", - "scopeguard", ] -[[package]] -name = "crossbeam-utils" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" - [[package]] name = "crunchy" version = "0.2.2" @@ -1918,9 +1829,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-bigint" -version = "0.5.3" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", "rand_core 0.6.4", @@ -1972,17 +1883,16 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.1" +version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", "digest 0.10.7", "fiat-crypto", - "platforms", - "rustc_version", + "rustc_version 0.4.0", "subtle", "zeroize", ] @@ -1993,9 +1903,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -2007,7 +1917,6 @@ dependencies = [ "byteorder", "digest 0.9.0", "rand_core 0.6.4", - "serde", "subtle-ng", "zeroize", ] @@ -2031,6 +1940,15 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cw-storage-macro" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "853c8ebf6d20542ea0dc57519ee458e7ee0caa3a1848beced2c603153d3f4dbe" +dependencies = [ + "syn 1.0.109", +] + [[package]] name = "cw-storage-plus" version = "0.15.1" @@ -2049,6 +1967,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5ff29294ee99373e2cd5fd21786a3c0ced99a52fec2ca347d565489c61b723c" dependencies = [ "cosmwasm-std", + "cw-storage-macro", "schemars", "serde", ] @@ -2063,22 +1982,22 @@ dependencies = [ "cosmwasm-std", "cw2 0.15.1", "schemars", - "semver", + "semver 1.0.23", "serde", "thiserror", ] [[package]] name = "cw-utils" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c80e93d1deccb8588db03945016a292c3c631e6325d349ebb35d2db6f4f946f7" +checksum = "1c4a657e5caacc3a0d00ee96ca8618745d050b8f757c709babafb81208d4239c" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw2 1.1.0", + "cw2 1.1.2", "schemars", - "semver", + "semver 1.0.23", "serde", "thiserror", ] @@ -2098,14 +2017,15 @@ dependencies = [ [[package]] name = "cw2" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ac2dc7a55ad64173ca1e0a46697c31b7a5c51342f55a1e84a724da4eb99908" +checksum = "c6c120b24fbbf5c3bedebb97f2cc85fbfa1c3287e09223428e7e597b5293c1fa" dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus 1.2.0", "schemars", + "semver 1.0.23", "serde", "thiserror", ] @@ -2122,12 +2042,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.3" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" dependencies = [ - "darling_core 0.20.3", - "darling_macro 0.20.3", + "darling_core 0.20.9", + "darling_macro 0.20.9", ] [[package]] @@ -2138,24 +2058,24 @@ checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.67", - "quote 1.0.33", - "strsim", + "proc-macro2 1.0.85", + "quote 1.0.36", + "strsim 0.10.0", "syn 1.0.109", ] [[package]] name = "darling_core" -version = "0.20.3" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.67", - "quote 1.0.33", - "strsim", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "strsim 0.11.1", + "syn 2.0.68", ] [[package]] @@ -2165,19 +2085,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ "darling_core 0.14.4", - "quote 1.0.33", + "quote 1.0.36", "syn 1.0.109", ] [[package]] name = "darling_macro" -version = "0.20.3" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ - "darling_core 0.20.3", - "quote 1.0.33", - "syn 2.0.37", + "darling_core 0.20.9", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -2187,7 +2107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown 0.14.0", + "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core", @@ -2195,15 +2115,15 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.4.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "data-encoding-macro" -version = "0.1.13" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c904b33cc60130e1aeea4956ab803d08a3f4a0ca82d64ed757afac3891f2bb99" +checksum = "f1559b6cba622276d6d63706db152618eeb15b89b3e4041446b05876e352e639" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -2211,20 +2131,14 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fdf3fce3ce863539ec1d7fd1b6dcc3c645663376b43ed376bbf887733e4f772" +checksum = "332d754c0af53bc87c108fed664d121ecf59207ec4196041f04d6ab9002ad33f" dependencies = [ "data-encoding", "syn 1.0.109", ] -[[package]] -name = "deflate64" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83ace6c86376be0b6cdcf3fb41882e81d94b31587573d1cfa9d01cd06bba210d" - [[package]] name = "der" version = "0.6.1" @@ -2238,9 +2152,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "der_derive", @@ -2257,7 +2171,7 @@ dependencies = [ "asn1-rs", "displaydoc", "nom", - "num-bigint 0.4.4", + "num-bigint 0.4.5", "num-traits", "rusticata-macros", ] @@ -2268,9 +2182,9 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fe87ce4529967e0ba1dcf8450bab64d97dfd5010a6256187ffe2e43e6f0e049" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -2289,8 +2203,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4255bb7dd538590188bd0aea52e48bd699b19bd90b0d069ec2ced8461fe23273" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", ] @@ -2300,33 +2214,22 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", ] -[[package]] -name = "derive_arbitrary" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" -dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", -] - [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ - "convert_case 0.4.0", - "proc-macro2 1.0.67", - "quote 1.0.33", - "rustc_version", - "syn 1.0.109", + "convert_case", + "proc-macro2 1.0.85", + "quote 1.0.36", + "rustc_version 0.4.0", + "syn 2.0.68", ] [[package]] @@ -2416,9 +2319,9 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -2433,12 +2336,6 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" -[[package]] -name = "downcast-rs" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" - [[package]] name = "dunce" version = "1.0.4" @@ -2447,28 +2344,22 @@ checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" [[package]] name = "dyn-clone" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d2f3407d9a573d666de4b5bdf10569d73ca9478087346697dcbae6244bfbcd" - -[[package]] -name = "ec-gpu" -version = "0.2.0" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd63582de2b59ea1aa48d7c1941b5d87618d95484397521b3acdfa0e1e9f5e45" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] name = "ecdsa" -version = "0.16.8" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "der 0.7.8", + "der 0.7.9", "digest 0.10.7", "elliptic-curve", "rfc6979", - "signature 2.1.0", - "spki 0.7.2", + "signature 2.2.0", + "spki 0.7.3", ] [[package]] @@ -2484,12 +2375,12 @@ dependencies = [ [[package]] name = "ed25519" -version = "2.2.2" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60f6d271ca33075c88028be6f04d502853d63a5ece419d269c15315d4fc1cf1d" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ "pkcs8 0.10.2", - "signature 2.1.0", + "signature 2.2.0", ] [[package]] @@ -2513,12 +2404,10 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ - "curve25519-dalek 4.1.1", - "ed25519 2.2.2", + "curve25519-dalek 4.1.3", + "ed25519 2.2.3", "rand_core 0.6.4", - "serde", - "sha2 0.10.7", - "signature 2.1.0", + "sha2 0.10.8", "subtle", "zeroize", ] @@ -2540,15 +2429,15 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "elliptic-curve" -version = "0.13.5" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", @@ -2564,15 +2453,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "ena" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" -dependencies = [ - "log", -] - [[package]] name = "encode_unicode" version = "0.3.6" @@ -2581,9 +2461,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] @@ -2594,7 +2474,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a3d8dc56e02f954cac8eb489772c552c473346fc34f67412bb6244fd647f7e4" dependencies = [ - "base64 0.21.4", + "base64 0.21.7", "bytes", "hex", "k256", @@ -2602,14 +2482,14 @@ dependencies = [ "rand", "rlp", "serde", - "sha3 0.10.8", + "sha3", "zeroize", ] [[package]] name = "enum-compat-util" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "serde_yaml", ] @@ -2620,21 +2500,21 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f16ef37b2a9b242295d61a154ee91ae884afff6b8b933b486b12481cc58310ca" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", ] [[package]] name = "enum_dispatch" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e" +checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" dependencies = [ "once_cell", - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -2643,35 +2523,14 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "erasable" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f11890ce181d47a64e5d1eb4b6caba0e7bae911a356723740d058a5d0340b7d" -dependencies = [ - "autocfg", - "scopeguard", -] - [[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.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ - "cc", "libc", + "windows-sys 0.52.0", ] [[package]] @@ -2682,30 +2541,14 @@ checksum = "27a72baa257b5e0e2de241967bc5ee8f855d6072351042688621081d66b2a76b" dependencies = [ "anyhow", "eyre", - "rustc_version", + "rustc_version 0.4.0", ] [[package]] -name = "eth-keystore" -version = "0.5.0" +name = "escape-bytes" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" -dependencies = [ - "aes", - "ctr", - "digest 0.10.7", - "hex", - "hmac", - "pbkdf2 0.11.0", - "rand", - "scrypt", - "serde", - "serde_json", - "sha2 0.10.7", - "sha3 0.10.8", - "thiserror", - "uuid 0.8.2", -] +checksum = "2bfcf67fea2815c2fc3b90873fae90957be12ff417335dfadc7f52927feb03b2" [[package]] name = "ethabi" @@ -2719,7 +2562,7 @@ dependencies = [ "regex", "serde", "serde_json", - "sha3 0.10.8", + "sha3", "thiserror", "uint", ] @@ -2750,56 +2593,27 @@ dependencies = [ "impl-codec 0.6.0", "impl-rlp", "impl-serde 0.4.0", - "primitive-types 0.12.1", + "primitive-types 0.12.2", "scale-info", "uint", ] [[package]] -name = "ethers" +name = "ethers-contract" version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "816841ea989f0c69e459af1cf23a6b0033b19a55424a1ea3a30099becdb8dec0" +checksum = "6fceafa3578c836eeb874af87abacfb041f92b4da0a78a5edd042564b8ecdaaa" dependencies = [ - "ethers-addressbook", - "ethers-contract", + "const-hex", + "ethers-contract-abigen", + "ethers-contract-derive", "ethers-core", - "ethers-etherscan", - "ethers-middleware", - "ethers-providers", - "ethers-signers", - "ethers-solc", -] - -[[package]] -name = "ethers-addressbook" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5495afd16b4faa556c3bba1f21b98b4983e53c1755022377051a975c3b021759" -dependencies = [ - "ethers-core", - "once_cell", - "serde", - "serde_json", -] - -[[package]] -name = "ethers-contract" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fceafa3578c836eeb874af87abacfb041f92b4da0a78a5edd042564b8ecdaaa" -dependencies = [ - "const-hex", - "ethers-contract-abigen", - "ethers-contract-derive", - "ethers-core", - "ethers-providers", - "futures-util", - "once_cell", - "pin-project", - "serde", - "serde_json", - "thiserror", + "futures-util", + "once_cell", + "pin-project", + "serde", + "serde_json", + "thiserror", ] [[package]] @@ -2812,17 +2626,15 @@ dependencies = [ "const-hex", "dunce", "ethers-core", - "ethers-etherscan", "eyre", - "prettyplease 0.2.15", - "proc-macro2 1.0.67", - "quote 1.0.33", + "prettyplease 0.2.20", + "proc-macro2 1.0.85", + "quote 1.0.36", "regex", - "reqwest 0.11.25", "serde", "serde_json", - "syn 2.0.37", - "toml 0.8.13", + "syn 2.0.68", + "toml 0.8.14", "walkdir", ] @@ -2836,10 +2648,10 @@ dependencies = [ "const-hex", "ethers-contract-abigen", "ethers-core", - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "serde_json", - "syn 2.0.37", + "syn 2.0.68", ] [[package]] @@ -2857,7 +2669,7 @@ dependencies = [ "ethabi", "generic-array", "k256", - "num_enum", + "num_enum 0.7.2", "once_cell", "open-fastrlp", "rand", @@ -2865,56 +2677,13 @@ dependencies = [ "serde", "serde_json", "strum 0.26.2", - "syn 2.0.37", + "syn 2.0.68", "tempfile", "thiserror", "tiny-keccak", "unicode-xid 0.2.4", ] -[[package]] -name = "ethers-etherscan" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79e5973c26d4baf0ce55520bd732314328cabe53193286671b47144145b9649" -dependencies = [ - "chrono", - "ethers-core", - "reqwest 0.11.25", - "semver", - "serde", - "serde_json", - "thiserror", - "tracing", -] - -[[package]] -name = "ethers-middleware" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48f9fdf09aec667c099909d91908d5eaf9be1bd0e2500ba4172c1d28bfaa43de" -dependencies = [ - "async-trait", - "auto_impl", - "ethers-contract", - "ethers-core", - "ethers-etherscan", - "ethers-providers", - "ethers-signers", - "futures-channel", - "futures-locks", - "futures-util", - "instant", - "reqwest 0.11.25", - "serde", - "serde_json", - "thiserror", - "tokio", - "tracing", - "tracing-futures", - "url", -] - [[package]] name = "ethers-providers" version = "2.0.14" @@ -2923,7 +2692,7 @@ checksum = "6434c9a33891f1effc9c75472e12666db2fa5a0fec4b29af6221680a6fe83ab2" dependencies = [ "async-trait", "auto_impl", - "base64 0.21.4", + "base64 0.21.7", "bytes", "const-hex", "enr", @@ -2932,12 +2701,12 @@ dependencies = [ "futures-timer", "futures-util", "hashers", - "http 0.2.9", + "http 0.2.12", "instant", "jsonwebtoken", "once_cell", "pin-project", - "reqwest 0.11.25", + "reqwest 0.11.27", "serde", "serde_json", "thiserror", @@ -2953,68 +2722,23 @@ dependencies = [ ] [[package]] -name = "ethers-signers" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "228875491c782ad851773b652dd8ecac62cda8571d3bc32a5853644dd26766c2" -dependencies = [ - "async-trait", - "coins-bip32", - "coins-bip39", - "const-hex", - "elliptic-curve", - "eth-keystore", - "ethers-core", - "rand", - "sha2 0.10.7", - "thiserror", - "tracing", -] - -[[package]] -name = "ethers-solc" -version = "2.0.14" +name = "ethnum" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66244a771d9163282646dbeffe0e6eca4dda4146b6498644e678ac6089b11edd" -dependencies = [ - "cfg-if", - "const-hex", - "dirs", - "dunce", - "ethers-core", - "glob", - "home", - "md-5", - "num_cpus", - "once_cell", - "path-slash", - "rayon", - "regex", - "semver", - "serde", - "serde_json", - "solang-parser", - "svm-rs", - "thiserror", - "tiny-keccak", - "tokio", - "tracing", - "walkdir", - "yansi", -] +checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c" [[package]] -name = "ethnum" -version = "1.4.0" +name = "event-listener" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8ff382b2fa527fb7fb06eeebfc5bbb3f17e3cc6b9d70b006c41daa8824adac" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "events" -version = "0.1.0" +version = "1.0.0" dependencies = [ "axelar-wasm-std", - "base64 0.21.4", + "base64 0.21.7", "cosmrs", "error-stack", "serde_json", @@ -3024,39 +2748,37 @@ dependencies = [ [[package]] name = "events-derive" -version = "0.1.0" +version = "1.0.0" dependencies = [ "error-stack", "events", - "quote 1.0.33", + "quote 1.0.36", "serde", "serde_json", - "syn 2.0.37", + "syn 2.0.68", ] [[package]] name = "evm-gateway" -version = "0.1.0" +version = "1.0.0" dependencies = [ "axelar-wasm-std", "cosmwasm-std", "error-stack", - "ethers", + "ethers-contract", + "ethers-core", "k256", "multisig", - "reqwest 0.12.4", - "serde", - "serde_json", - "sha3 0.10.8", + "router-api", + "sha3", "thiserror", - "zip 2.0.0", ] [[package]] name = "eyre" -version = "0.6.8" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" dependencies = [ "indenter", "once_cell", @@ -3064,23 +2786,22 @@ dependencies = [ [[package]] name = "fastcrypto" -version = "0.1.7" -source = "git+https://github.com/MystenLabs/fastcrypto?rev=69180dc7275f5f0efb69e11e9d03f6db338d1dd6#69180dc7275f5f0efb69e11e9d03f6db338d1dd6" +version = "0.1.8" +source = "git+https://github.com/MystenLabs/fastcrypto?rev=4988a4744fcaf8bc7f60bf660d9a223ed0f54cc6#4988a4744fcaf8bc7f60bf660d9a223ed0f54cc6" dependencies = [ "aes", "aes-gcm", "ark-ec", - "ark-ff", + "ark-ff 0.4.2", "ark-secp256r1", - "ark-serialize", + "ark-serialize 0.4.2", "auto_ops", "base64ct", + "bech32", "bincode", "blake2", - "blake3", "blst", "bs58 0.4.0", - "bulletproofs", "cbc", "ctr", "curve25519-dalek-ng", @@ -3089,13 +2810,13 @@ dependencies = [ "ecdsa", "ed25519-consensus", "elliptic-curve", - "eyre", "fastcrypto-derive", "generic-array", "hex", + "hex-literal", "hkdf", "lazy_static", - "merlin", + "num-bigint 0.4.5", "once_cell", "p256", "rand", @@ -3105,12 +2826,11 @@ dependencies = [ "schemars", "secp256k1", "serde", - "serde_bytes", "serde_json", "serde_with 2.3.3", - "sha2 0.10.7", - "sha3 0.10.8", - "signature 2.1.0", + "sha2 0.10.8", + "sha3", + "signature 2.2.0", "static_assertions", "thiserror", "tokio", @@ -3121,62 +2841,55 @@ dependencies = [ [[package]] name = "fastcrypto-derive" version = "0.1.3" -source = "git+https://github.com/MystenLabs/fastcrypto?rev=69180dc7275f5f0efb69e11e9d03f6db338d1dd6#69180dc7275f5f0efb69e11e9d03f6db338d1dd6" +source = "git+https://github.com/MystenLabs/fastcrypto?rev=4988a4744fcaf8bc7f60bf660d9a223ed0f54cc6#4988a4744fcaf8bc7f60bf660d9a223ed0f54cc6" dependencies = [ - "convert_case 0.6.0", - "proc-macro2 1.0.67", - "quote 1.0.33", + "quote 1.0.36", "syn 1.0.109", ] [[package]] name = "fastcrypto-tbls" version = "0.1.0" -source = "git+https://github.com/MystenLabs/fastcrypto?rev=69180dc7275f5f0efb69e11e9d03f6db338d1dd6#69180dc7275f5f0efb69e11e9d03f6db338d1dd6" +source = "git+https://github.com/MystenLabs/fastcrypto?rev=4988a4744fcaf8bc7f60bf660d9a223ed0f54cc6#4988a4744fcaf8bc7f60bf660d9a223ed0f54cc6" dependencies = [ "bcs", - "bincode", "digest 0.10.7", "fastcrypto", - "fastcrypto-derive", "hex", "itertools 0.10.5", "rand", "serde", - "sha3 0.10.8", + "sha3", "tap", "tracing", "typenum", - "zeroize", ] [[package]] name = "fastcrypto-zkp" -version = "0.1.2" -source = "git+https://github.com/MystenLabs/fastcrypto?rev=69180dc7275f5f0efb69e11e9d03f6db338d1dd6#69180dc7275f5f0efb69e11e9d03f6db338d1dd6" +version = "0.1.3" +source = "git+https://github.com/MystenLabs/fastcrypto?rev=4988a4744fcaf8bc7f60bf660d9a223ed0f54cc6#4988a4744fcaf8bc7f60bf660d9a223ed0f54cc6" dependencies = [ "ark-bls12-381", "ark-bn254", "ark-ec", - "ark-ff", + "ark-ff 0.4.2", "ark-groth16", "ark-relations", - "ark-serialize", + "ark-serialize 0.4.2", "ark-snark", - "bcs", "blst", "byte-slice-cast", "derive_more", "fastcrypto", "ff", "im", + "itertools 0.12.1", "lazy_static", "neptune", - "num-bigint 0.4.4", + "num-bigint 0.4.5", "once_cell", - "regex", - "reqwest 0.11.25", - "rustls-webpki 0.101.5", + "reqwest 0.11.27", "schemars", "serde", "serde_json", @@ -3194,17 +2907,19 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] -name = "fdlimit" -version = "0.2.1" +name = "fastrlp" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c4c9e43643f5a3be4ca5b67d26b98031ff9db6806c3440ae32e02e3ceac3f1b" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" dependencies = [ - "libc", + "arrayvec", + "auto_impl", + "bytes", ] [[package]] @@ -3231,16 +2946,16 @@ dependencies = [ "num-bigint 0.3.3", "num-integer", "num-traits", - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", ] [[package]] name = "fiat-crypto" -version = "0.2.5" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "fixed-hash" @@ -3280,9 +2995,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flagset" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a7e408202050813e6f1d9addadcaafef3dca7530c7ddfb005d4081cce6779" +checksum = "cdeb3aa5e95cf9aabc17f060cfa0ced7b83f042390760ca53bf09df9968acaa1" dependencies = [ "serde", ] @@ -3339,9 +3054,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -3358,16 +3073,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" -[[package]] -name = "fs2" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "funty" version = "1.1.0" @@ -3382,9 +3087,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -3397,9 +3102,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -3407,15 +3112,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -3424,48 +3129,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-locks" -version = "0.7.1" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45ec6fe3675af967e67c5536c0b9d44e34e6c52f86bedc4ea49c5317b8e94d06" -dependencies = [ - "futures-channel", - "futures-task", -] +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-timer" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" dependencies = [ "gloo-timers", "send_wrapper 0.4.0", @@ -3473,9 +3168,9 @@ dependencies = [ [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -3500,19 +3195,19 @@ dependencies = [ [[package]] name = "gateway" -version = "0.2.2" +version = "1.0.0" dependencies = [ "axelar-wasm-std", - "axelar-wasm-std-derive", "client", "cosmwasm-schema", "cosmwasm-std", "cw-multi-test", "cw-storage-plus 1.2.0", - "cw2 1.1.0", + "cw2 1.1.2", "error-stack", "gateway-api", "itertools 0.11.0", + "rand", "report", "router-api", "serde", @@ -3523,9 +3218,13 @@ dependencies = [ [[package]] name = "gateway-api" -version = "0.1.0" +version = "1.0.0" dependencies = [ + "axelar-wasm-std", "cosmwasm-schema", + "cosmwasm-std", + "error-stack", + "msgs-derive", "router-api", ] @@ -3543,9 +3242,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", @@ -3556,9 +3255,9 @@ dependencies = [ [[package]] name = "ghash" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" dependencies = [ "opaque-debug", "polyval", @@ -3566,9 +3265,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "glob" @@ -3576,6 +3275,19 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + [[package]] name = "gloo-timers" version = "0.2.6" @@ -3588,6 +3300,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "goldie" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa70c42797cac60e6182e00f33f629212e02ba80d67e8a976f6168b57568d78e" +dependencies = [ + "anyhow", + "once_cell", + "pretty_assertions", + "serde", + "serde_json", + "upon", + "yansi 1.0.1", +] + [[package]] name = "group" version = "0.13.0" @@ -3603,39 +3330,39 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", "futures-core", "futures-sink", "futures-util", - "http 0.2.9", - "indexmap 2.0.0", + "http 0.2.12", + "indexmap 2.2.6", "slab", "tokio", - "tokio-util 0.7.9", + "tokio-util", "tracing", ] [[package]] name = "h2" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" dependencies = [ + "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "futures-util", "http 1.1.0", - "indexmap 2.0.0", + "indexmap 2.2.6", "slab", "tokio", - "tokio-util 0.7.9", + "tokio-util", "tracing", ] @@ -3645,7 +3372,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.8", ] [[package]] @@ -3654,14 +3381,14 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.3", + "ahash 0.8.11", ] [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashers" @@ -3674,15 +3401,11 @@ dependencies = [ [[package]] name = "hdrhistogram" -version = "7.5.2" +version = "7.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f19b9f54f7c7f55e31401bb647626ce0cf0f67b0004982ce815b3ee72a02aa8" +checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" dependencies = [ - "base64 0.13.1", "byteorder", - "crossbeam-channel", - "flate2", - "nom", "num-traits", ] @@ -3692,10 +3415,10 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ - "base64 0.21.4", + "base64 0.21.7", "bytes", "headers-core", - "http 0.2.9", + "http 0.2.12", "httpdate", "mime", "sha1", @@ -3707,16 +3430,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" dependencies = [ - "http 0.2.9", -] - -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", + "http 0.2.12", ] [[package]] @@ -3733,9 +3447,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -3754,9 +3468,9 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "hkdf" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ "hmac", ] @@ -3772,18 +3486,18 @@ dependencies = [ [[package]] name = "home" -version = "0.5.5" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "http" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -3803,12 +3517,12 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http 0.2.9", + "http 0.2.12", "pin-project-lite", ] @@ -3824,12 +3538,12 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", - "futures-core", + "futures-util", "http 1.1.0", "http-body 1.0.0", "pin-project-lite", @@ -3843,9 +3557,9 @@ checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "httpdate" @@ -3871,22 +3585,22 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", - "h2 0.3.24", - "http 0.2.9", - "http-body 0.4.5", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.9", + "socket2", "tokio", "tower-service", "tracing", @@ -3902,7 +3616,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.4", + "h2 0.4.6", "http 1.1.0", "http-body 1.0.0", "httparse", @@ -3923,14 +3637,14 @@ dependencies = [ "bytes", "futures", "headers", - "http 0.2.9", - "hyper 0.14.27", + "http 0.2.12", + "hyper 0.14.29", "hyper-rustls 0.22.1", - "rustls-native-certs", + "rustls-native-certs 0.5.0", "tokio", "tokio-rustls 0.22.0", "tower-service", - "webpki", + "webpki 0.21.4", ] [[package]] @@ -3941,37 +3655,70 @@ checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64" dependencies = [ "ct-logs", "futures-util", - "hyper 0.14.27", + "hyper 0.14.29", "log", "rustls 0.19.1", - "rustls-native-certs", + "rustls-native-certs 0.5.0", "tokio", "tokio-rustls 0.22.0", - "webpki", + "webpki 0.21.4", "webpki-roots 0.21.1", ] [[package]] name = "hyper-rustls" -version = "0.24.1" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +dependencies = [ + "http 0.2.12", + "hyper 0.14.29", + "log", + "rustls 0.20.9", + "rustls-native-certs 0.6.3", + "tokio", + "tokio-rustls 0.23.4", + "webpki-roots 0.22.6", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", - "http 0.2.9", - "hyper 0.14.27", - "rustls 0.21.7", + "http 0.2.12", + "hyper 0.14.29", + "rustls 0.21.12", "tokio", "tokio-rustls 0.24.1", ] +[[package]] +name = "hyper-rustls" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.3.1", + "hyper-util", + "rustls 0.23.12", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.0", + "tower-service", +] + [[package]] name = "hyper-timeout" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "hyper 0.14.27", + "hyper 0.14.29", "pin-project-lite", "tokio", "tokio-io-timeout", @@ -3995,9 +3742,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56" dependencies = [ "bytes", "futures-channel", @@ -4006,7 +3753,7 @@ dependencies = [ "http-body 1.0.0", "hyper 1.3.1", "pin-project-lite", - "socket2 0.5.4", + "socket2", "tokio", "tower", "tower-service", @@ -4015,16 +3762,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.57" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows-core", ] [[package]] @@ -4037,32 +3784,152 @@ dependencies = [ ] [[package]] -name = "ident_case" -version = "1.0.1" +name = "icu_collections" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] [[package]] -name = "idna" -version = "0.4.0" +name = "icu_locid" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", ] [[package]] -name = "im" -version = "15.1.0" +name = "icu_locid_transform" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" dependencies = [ - "bitmaps", - "rand_core 0.6.4", - "rand_xoshiro", - "sized-chunks", - "typenum", + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed" +dependencies = [ + "icu_normalizer", + "icu_properties", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "im" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" +dependencies = [ + "bitmaps", + "rand_core 0.6.4", + "rand_xoshiro", + "sized-chunks", + "typenum", "version_check", ] @@ -4081,7 +3948,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" dependencies = [ - "parity-scale-codec 3.6.5", + "parity-scale-codec 3.6.12", ] [[package]] @@ -4117,8 +3984,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", ] @@ -4141,12 +4008,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.0" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.5", "serde", ] @@ -4156,15 +4023,15 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "block-padding 0.3.3", + "block-padding", "generic-array", ] [[package]] name = "insta" -version = "1.31.0" +version = "1.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0770b0a3d4c70567f0d58331f3088b0e4c4f56c9b8d764efe654b4a5d46de3a" +checksum = "810ae6042d48e2c9e9215043563a58a80b877bc863228a74cf10c49d4620a6f5" dependencies = [ "console", "lazy_static", @@ -4173,21 +4040,20 @@ dependencies = [ "pest_derive", "serde", "similar", - "yaml-rust", ] [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", ] [[package]] name = "integration-tests" -version = "0.1.0" +version = "1.0.0" dependencies = [ "axelar-wasm-std", "coordinator", @@ -4207,29 +4073,48 @@ dependencies = [ "serde", "serde_json", "service-registry", - "sha3 0.10.8", + "sha3", "tofn", "voting-verifier", ] [[package]] -name = "internment" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ab388864246d58a276e60e7569a833d9cc4cd75c66e5ca77c177dad38e59996" +name = "interchain-token-service" +version = "0.1.0" dependencies = [ - "ahash 0.7.6", - "dashmap", - "hashbrown 0.12.3", - "once_cell", - "parking_lot", + "alloy-primitives", + "alloy-sol-types", + "axelar-wasm-std", + "cosmwasm-schema", + "cosmwasm-std", + "error-stack", + "goldie", + "report", + "router-api", + "schemars", + "serde", + "serde_json", + "strum 0.25.0", + "thiserror", +] + +[[package]] +name = "into-inner-derive" +version = "1.0.0" +dependencies = [ + "axelar-wasm-std", + "error-stack", + "quote 1.0.36", + "report", + "syn 2.0.68", + "thiserror", ] [[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 = "iri-string" @@ -4241,15 +4126,10 @@ dependencies = [ ] [[package]] -name = "is-terminal" -version = "0.4.9" +name = "is_terminal_polyfill" +version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" -dependencies = [ - "hermit-abi", - "rustix", - "windows-sys 0.48.0", -] +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" [[package]] name = "itertools" @@ -4270,25 +4150,25 @@ dependencies = [ ] [[package]] -name = "itoa" -version = "1.0.9" +name = "itertools" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] [[package]] -name = "jobserver" -version = "0.1.26" +name = "itoa" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" -dependencies = [ - "libc", -] +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -4313,15 +4193,151 @@ dependencies = [ "tabled", ] +[[package]] +name = "jsonrpsee" +version = "0.16.2" +source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" +dependencies = [ + "jsonrpsee-core", + "jsonrpsee-http-client", + "jsonrpsee-proc-macros", + "jsonrpsee-server", + "jsonrpsee-types", + "jsonrpsee-ws-client", + "tracing", +] + +[[package]] +name = "jsonrpsee-client-transport" +version = "0.16.2" +source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" +dependencies = [ + "futures-util", + "http 0.2.12", + "jsonrpsee-core", + "jsonrpsee-types", + "pin-project", + "rustls-native-certs 0.6.3", + "soketto", + "thiserror", + "tokio", + "tokio-rustls 0.23.4", + "tokio-util", + "tracing", + "webpki-roots 0.22.6", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.16.2" +source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" +dependencies = [ + "anyhow", + "arrayvec", + "async-lock", + "async-trait", + "beef", + "futures-channel", + "futures-timer", + "futures-util", + "globset", + "hyper 0.14.29", + "jsonrpsee-types", + "parking_lot", + "rand", + "rustc-hash", + "serde", + "serde_json", + "soketto", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-http-client" +version = "0.16.2" +source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" +dependencies = [ + "async-trait", + "hyper 0.14.29", + "hyper-rustls 0.23.2", + "jsonrpsee-core", + "jsonrpsee-types", + "rustc-hash", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-proc-macros" +version = "0.16.2" +source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 1.1.3", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 1.0.109", +] + +[[package]] +name = "jsonrpsee-server" +version = "0.16.2" +source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" +dependencies = [ + "futures-channel", + "futures-util", + "http 0.2.12", + "hyper 0.14.29", + "jsonrpsee-core", + "jsonrpsee-types", + "serde", + "serde_json", + "soketto", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tracing", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.16.2" +source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" +dependencies = [ + "anyhow", + "beef", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "jsonrpsee-ws-client" +version = "0.16.2" +source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" +dependencies = [ + "http 0.2.12", + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-types", +] + [[package]] name = "jsonwebtoken" version = "8.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" dependencies = [ - "base64 0.21.4", - "pem", - "ring", + "base64 0.21.7", + "pem 1.1.1", + "ring 0.16.20", "serde", "serde_json", "simple_asn1", @@ -4337,109 +4353,64 @@ dependencies = [ "ecdsa", "elliptic-curve", "once_cell", - "sha2 0.10.7", - "signature 2.1.0", + "sha2 0.10.8", + "signature 2.2.0", ] [[package]] name = "keccak" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" dependencies = [ "cpufeatures", ] [[package]] -name = "lalrpop" -version = "0.20.0" +name = "keccak-asm" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da4081d44f4611b66c6dd725e6de3169f9f63905421e8626fcb86b6a898998b8" +checksum = "47a3633291834c4fbebf8673acbc1b04ec9d151418ff9b8e26dcd79129928758" dependencies = [ - "ascii-canvas", - "bit-set", - "diff", - "ena", - "is-terminal", - "itertools 0.10.5", - "lalrpop-util", - "petgraph 0.6.4", - "regex", - "regex-syntax 0.7.5", - "string_cache", - "term", - "tiny-keccak", - "unicode-xid 0.2.4", + "digest 0.10.7", + "sha3-asm", ] -[[package]] -name = "lalrpop-util" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f35c735096c0293d313e8f2a641627472b83d01b937177fe76e5e2708d31e0d" - [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" dependencies = [ - "spin", + "spin 0.5.2", ] [[package]] -name = "lazycell" -version = "1.3.0" +name = "leb128" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.153" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" - -[[package]] -name = "libloading" -version = "0.7.4" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if", - "winapi", -] +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libm" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] -name = "librocksdb-sys" -version = "0.11.0+8.1.1" +name = "libredox" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3386f101bcb4bd252d8e9d2fb41ec3b0862a15a62b478c355b2982efa469e3e" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bindgen", - "bzip2-sys", - "cc", - "glob", + "bitflags 2.5.0", "libc", - "libz-sys", - "lz4-sys", - "zstd-sys", -] - -[[package]] -name = "libz-sys" -version = "1.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", ] [[package]] @@ -4450,26 +4421,26 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.7" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", ] -[[package]] -name = "lockfree-object-pool" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" - [[package]] name = "log" version = "0.4.21" @@ -4480,23 +4451,12 @@ dependencies = [ ] [[package]] -name = "lz4-sys" -version = "1.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "lzma-rs" -version = "0.3.0" +name = "lru" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" +checksum = "718e8fae447df0c7e1ba7f5189829e63fd536945c8988d61444c19039f16b670" dependencies = [ - "byteorder", - "crc", + "hashbrown 0.13.2", ] [[package]] @@ -4513,45 +4473,15 @@ checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" [[package]] name = "matchit" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed1202b2a6f884ae56f04cff409ab315c5ce26b5e58d7412e484f01fd52f52ef" - -[[package]] -name = "md-5" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" -dependencies = [ - "digest 0.10.7", -] +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "memchr" -version = "2.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" - -[[package]] -name = "memoffset" -version = "0.9.0" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "merlin" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" -dependencies = [ - "byteorder", - "keccak", - "rand_core 0.6.4", - "zeroize", -] +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mime" @@ -4577,21 +4507,20 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.8" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", - "log", "wasi", "windows-sys 0.48.0", ] @@ -4606,49 +4535,68 @@ dependencies = [ "downcast", "fragile", "lazy_static", - "mockall_derive", - "predicates", + "mockall_derive 0.11.4", + "predicates 2.1.5", "predicates-tree", ] +[[package]] +name = "mockall" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43766c2b5203b10de348ffe19f7e54564b64f3d6018ff7648d1e2d6d3a0f0a48" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "lazy_static", + "mockall_derive 0.12.1", + "predicates 3.1.0", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" +dependencies = [ + "cfg-if", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 1.0.109", +] + [[package]] name = "mockall_derive" -version = "0.11.4" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" +checksum = "af7cbce79ec385a1d4f54baa90a76401eb15d9cab93685f62e7e9f942aa00ae2" dependencies = [ "cfg-if", - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 1.0.109", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] -name = "move-abigen" +name = "move-abstract-interpreter" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ - "anyhow", - "bcs", - "heck 0.3.3", - "log", "move-binary-format", - "move-bytecode-verifier", - "move-command-line-common", - "move-core-types", - "move-model", - "serde", + "move-bytecode-verifier-meter", ] [[package]] name = "move-abstract-stack" version = "0.0.1" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" [[package]] name = "move-binary-format" version = "0.0.3" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "anyhow", "enum-compat-util", @@ -4662,12 +4610,12 @@ dependencies = [ [[package]] name = "move-borrow-graph" version = "0.0.1" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" [[package]] name = "move-bytecode-source-map" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "anyhow", "bcs", @@ -4682,7 +4630,7 @@ dependencies = [ [[package]] name = "move-bytecode-utils" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "anyhow", "move-binary-format", @@ -4694,42 +4642,56 @@ dependencies = [ [[package]] name = "move-bytecode-verifier" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ + "move-abstract-interpreter", "move-abstract-stack", "move-binary-format", "move-borrow-graph", + "move-bytecode-verifier-meter", "move-core-types", "move-vm-config", "petgraph 0.5.1", ] +[[package]] +name = "move-bytecode-verifier-meter" +version = "0.1.0" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" +dependencies = [ + "move-binary-format", + "move-core-types", + "move-vm-config", +] + [[package]] name = "move-command-line-common" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "anyhow", "difference", "dirs-next", "hex", "move-core-types", - "num-bigint 0.4.4", + "num-bigint 0.4.5", "once_cell", "serde", "sha2 0.9.9", + "vfs", "walkdir", ] [[package]] name = "move-compiler" version = "0.0.1" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "anyhow", "bcs", "clap", "codespan-reporting", + "dunce", "hex", "move-binary-format", "move-borrow-graph", @@ -4739,24 +4701,30 @@ dependencies = [ "move-core-types", "move-ir-to-bytecode", "move-ir-types", + "move-proc-macros", "move-symbol-pool", "once_cell", "petgraph 0.5.1", "regex", "serde", + "serde_json", + "similar", + "stacker", "tempfile", + "vfs", ] [[package]] name = "move-core-types" version = "0.0.4" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "anyhow", "bcs", "enum-compat-util", "ethnum", "hex", + "leb128", "move-proc-macros", "num", "once_cell", @@ -4765,19 +4733,21 @@ dependencies = [ "ref-cast", "serde", "serde_bytes", + "thiserror", "uint", ] [[package]] name = "move-coverage" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "anyhow", "bcs", "clap", "codespan", "colored", + "move-abstract-interpreter", "move-binary-format", "move-bytecode-source-map", "move-command-line-common", @@ -4790,13 +4760,14 @@ dependencies = [ [[package]] name = "move-disassembler" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "anyhow", "bcs", "clap", "colored", "hex", + "move-abstract-interpreter", "move-binary-format", "move-bytecode-source-map", "move-command-line-common", @@ -4806,28 +4777,10 @@ dependencies = [ "move-ir-types", ] -[[package]] -name = "move-docgen" -version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" -dependencies = [ - "anyhow", - "codespan", - "codespan-reporting", - "itertools 0.10.5", - "log", - "move-compiler", - "move-model", - "num", - "once_cell", - "regex", - "serde", -] - [[package]] name = "move-ir-to-bytecode" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "anyhow", "codespan-reporting", @@ -4845,7 +4798,7 @@ dependencies = [ [[package]] name = "move-ir-to-bytecode-syntax" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "anyhow", "hex", @@ -4858,7 +4811,7 @@ dependencies = [ [[package]] name = "move-ir-types" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "hex", "move-command-line-common", @@ -4868,77 +4821,20 @@ dependencies = [ "serde", ] -[[package]] -name = "move-model" -version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" -dependencies = [ - "anyhow", - "codespan", - "codespan-reporting", - "internment", - "itertools 0.10.5", - "log", - "move-binary-format", - "move-bytecode-source-map", - "move-command-line-common", - "move-compiler", - "move-core-types", - "move-disassembler", - "move-ir-types", - "move-symbol-pool", - "num", - "once_cell", - "regex", - "serde", -] - -[[package]] -name = "move-package" -version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" -dependencies = [ - "anyhow", - "clap", - "colored", - "move-abigen", - "move-binary-format", - "move-bytecode-source-map", - "move-bytecode-utils", - "move-command-line-common", - "move-compiler", - "move-core-types", - "move-docgen", - "move-model", - "move-symbol-pool", - "named-lock", - "once_cell", - "petgraph 0.5.1", - "regex", - "serde", - "serde_yaml", - "sha2 0.9.9", - "tempfile", - "toml 0.5.11", - "treeline", - "walkdir", - "whoami", -] - [[package]] name = "move-proc-macros" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "enum-compat-util", - "quote 1.0.33", - "syn 2.0.37", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] name = "move-symbol-pool" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "once_cell", "phf", @@ -4948,26 +4844,28 @@ dependencies = [ [[package]] name = "move-vm-config" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "move-binary-format", + "once_cell", ] [[package]] name = "move-vm-profiler" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "move-vm-config", "once_cell", "serde", "serde_json", + "tracing", ] [[package]] name = "move-vm-test-utils" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "anyhow", "move-binary-format", @@ -4981,7 +4879,7 @@ dependencies = [ [[package]] name = "move-vm-types" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "bcs", "move-binary-format", @@ -4992,42 +4890,26 @@ dependencies = [ ] [[package]] -name = "msim" -version = "0.1.0" -source = "git+https://github.com/MystenLabs/mysten-sim.git?rev=1a52783d6600ecc22e15253a982f77881bd47c77#1a52783d6600ecc22e15253a982f77881bd47c77" +name = "msgs-derive" +version = "1.0.0" dependencies = [ - "ahash 0.7.6", - "async-task", - "bincode", - "bytes", - "cc", - "downcast-rs", - "erasable", - "futures", - "lazy_static", - "libc", - "msim-macros", - "naive-timer", - "pin-project-lite", - "rand", - "real_tokio", - "serde", - "socket2 0.4.9", - "tap", - "tokio-util 0.7.7", - "toml 0.5.11", - "tracing", - "tracing-subscriber", + "axelar-wasm-std", + "cosmwasm-std", + "error-stack", + "itertools 0.11.0", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] name = "msim-macros" version = "0.1.0" -source = "git+https://github.com/MystenLabs/mysten-sim.git?rev=1a52783d6600ecc22e15253a982f77881bd47c77#1a52783d6600ecc22e15253a982f77881bd47c77" +source = "git+https://github.com/MystenLabs/mysten-sim.git?rev=077b735b484cf33e79f9d621db1d0c3a5827b81e#077b735b484cf33e79f9d621db1d0c3a5827b81e" dependencies = [ "darling 0.14.4", - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", ] @@ -5078,12 +4960,12 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro-error", - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", - "synstructure", + "synstructure 0.12.6", ] [[package]] @@ -5094,76 +4976,106 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "multisig" -version = "0.3.0" +version = "1.0.0" dependencies = [ "axelar-wasm-std", - "axelar-wasm-std-derive", "cosmwasm-crypto", "cosmwasm-schema", "cosmwasm-std", - "curve25519-dalek 4.1.1", + "curve25519-dalek 4.1.3", "cw-multi-test", "cw-storage-plus 1.2.0", - "cw-utils 1.0.1", - "cw2 1.1.0", + "cw-utils 1.0.3", + "cw2 1.1.2", "ed25519-dalek", "enum-display-derive", "error-stack", "getrandom", + "goldie", + "hex", "itertools 0.11.0", "k256", + "msgs-derive", "report", "rewards", "router-api", "serde", "serde_json", - "sha3 0.10.8", + "sha3", "signature-verifier-api", "thiserror", ] [[package]] name = "multisig-prover" -version = "0.5.0" +version = "1.0.0" dependencies = [ - "alloy-primitives", - "alloy-sol-types", "anyhow", "axelar-wasm-std", - "axelar-wasm-std-derive", "bcs", "coordinator", "cosmwasm-schema", "cosmwasm-std", "cw-multi-test", "cw-storage-plus 1.2.0", - "cw-utils 1.0.1", - "cw2 1.1.0", + "cw-utils 1.0.3", + "cw2 1.1.2", "elliptic-curve", "error-stack", - "ethabi", - "ethers", + "ethers-contract", + "ethers-core", + "evm-gateway", "gateway", "gateway-api", "generic-array", + "goldie", "hex", "itertools 0.11.0", "k256", + "msgs-derive", "multisig", "prost 0.12.6", "report", "router-api", "serde_json", "service-registry", - "sha3 0.10.8", + "sha3", + "stellar", + "sui-gateway", "thiserror", "voting-verifier", ] +[[package]] +name = "multiversx-sdk" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cb2f8dd4a17ce9c9fa1ab3d80152929702968be6536499f32bd7e2278c2e0fb" +dependencies = [ + "anyhow", + "base64 0.22.1", + "bech32", + "bip39", + "hex", + "hmac", + "itertools 0.12.1", + "pbkdf2", + "pem 3.0.4", + "rand", + "reqwest 0.12.5", + "serde", + "serde_json", + "serde_repr", + "sha2 0.10.8", + "sha3", + "tokio", + "zeroize", +] + [[package]] name = "mysten-metrics" version = "0.7.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "async-trait", "axum 0.6.20", @@ -5177,38 +5089,37 @@ dependencies = [ "tap", "tokio", "tracing", - "uuid 1.4.1", - "workspace-hack", + "uuid 1.8.0", ] [[package]] name = "mysten-network" version = "0.2.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "anemo", "bcs", "bytes", "eyre", "futures", - "http 0.2.9", + "http 0.2.12", "multiaddr", + "pin-project-lite", "serde", "snap", "tokio", "tokio-stream", - "tonic 0.10.2", + "tonic 0.11.0", "tonic-health", "tower", "tower-http", "tracing", - "workspace-hack", ] [[package]] name = "mysten-util-mem" version = "0.11.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "cfg-if", "ed25519-consensus", @@ -5216,53 +5127,30 @@ dependencies = [ "fastcrypto-tbls", "hashbrown 0.12.3", "impl-trait-for-tuples", - "indexmap 1.9.3", + "indexmap 2.2.6", "mysten-util-mem-derive", "once_cell", "parking_lot", "roaring", "smallvec", - "workspace-hack", ] [[package]] name = "mysten-util-mem-derive" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.85", "syn 1.0.109", - "synstructure", - "workspace-hack", -] - -[[package]] -name = "naive-timer" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034a0ad7deebf0c2abcf2435950a6666c3c15ea9d8fad0c0f48efa8a7f843fed" - -[[package]] -name = "named-lock" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a3eb6b7c682b65d1f631ec3176829d72ab450b3aacdd3f719bf220822e59ac" -dependencies = [ - "libc", - "once_cell", - "parking_lot", - "thiserror", - "widestring", - "winapi", + "synstructure 0.12.6", ] [[package]] name = "narwhal-config" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "fastcrypto", - "fastcrypto-tbls", "match_opt", "mysten-network", "mysten-util-mem", @@ -5272,29 +5160,25 @@ dependencies = [ "serde_json", "thiserror", "tracing", - "workspace-hack", ] [[package]] name = "narwhal-crypto" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "bcs", "fastcrypto", - "fastcrypto-tbls", "serde", "shared-crypto", - "workspace-hack", ] [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -5325,29 +5209,26 @@ dependencies = [ "trait-set", ] -[[package]] -name = "new_debug_unreachable" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" - [[package]] name = "nexus-gateway" -version = "0.2.3" +version = "1.0.0" dependencies = [ "axelar-wasm-std", - "axelar-wasm-std-derive", + "axelarnet-gateway", + "client", "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus 1.2.0", - "cw2 1.1.0", + "cw2 1.1.2", "error-stack", "hex", - "mockall", + "mockall 0.11.4", + "msgs-derive", "report", "router-api", "schemars", "serde", + "sha3", "thiserror", "voting-verifier", ] @@ -5362,6 +5243,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nonempty" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "995defdca0a589acfdd1bd2e8e3b896b4d4f7675a31fd14c32611440c7f608e6" + [[package]] name = "normalize-line-endings" version = "0.3.0" @@ -5380,11 +5267,11 @@ dependencies = [ [[package]] name = "num" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" dependencies = [ - "num-bigint 0.4.4", + "num-bigint 0.4.5", "num-complex", "num-integer", "num-iter", @@ -5405,11 +5292,10 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" dependencies = [ - "autocfg", "num-integer", "num-traits", "rand", @@ -5434,9 +5320,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", ] @@ -5453,26 +5339,25 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", ] [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -5481,21 +5366,20 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "autocfg", - "num-bigint 0.4.4", + "num-bigint 0.4.5", "num-integer", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", @@ -5513,30 +5397,51 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.0" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +dependencies = [ + "num_enum_derive 0.6.1", +] + +[[package]] +name = "num_enum" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70bf6736f74634d299d00086f02986875b3c2d924781a6a2cb6c201e73da0ceb" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" dependencies = [ - "num_enum_derive", + "num_enum_derive 0.7.2", ] [[package]] name = "num_enum_derive" -version = "0.7.0" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +dependencies = [ + "proc-macro-crate 1.1.3", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ea360eafe1022f7cc56cd7b869ed57330fb2453d0c7831d99b74c65d2f5597" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "proc-macro-crate", - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro-crate 3.1.0", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] name = "object" -version = "0.32.1" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" dependencies = [ "memchr", ] @@ -5558,9 +5463,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "open-fastrlp" @@ -5582,18 +5487,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" dependencies = [ "bytes", - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", ] [[package]] name = "openssl" -version = "0.10.64" +version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.5.0", "cfg-if", "foreign-types", "libc", @@ -5608,9 +5513,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -5621,9 +5526,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.102" +version = "0.9.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", @@ -5666,9 +5571,9 @@ checksum = "ec4c6225c69b4ca778c0aea097321a64c421cf4577b331c61b229267edabb6f8" dependencies = [ "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -5686,7 +5591,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2 0.10.7", + "sha2 0.10.8", ] [[package]] @@ -5725,15 +5630,15 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.6.5" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dec8a8073036902368c2cdc0387e85ff9a37054d7e7c98e592145e0c92cd4fb" +checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" dependencies = [ "arrayvec", "bitvec 1.0.1", "byte-slice-cast", "impl-trait-for-tuples", - "parity-scale-codec-derive 3.6.5", + "parity-scale-codec-derive 3.6.12", "serde", ] @@ -5743,29 +5648,29 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" dependencies = [ - "proc-macro-crate", - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro-crate 1.1.3", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", ] [[package]] name = "parity-scale-codec-derive" -version = "3.6.5" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312270ee71e1cd70289dacf597cab7b207aa107d2f28191c2ae45b2ece18a260" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ - "proc-macro-crate", - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro-crate 3.1.0", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", ] [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -5773,26 +5678,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", + "redox_syscall", "smallvec", - "windows-targets 0.48.5", -] - -[[package]] -name = "password-hash" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" -dependencies = [ - "base64ct", - "rand_core 0.6.4", - "subtle", + "windows-targets 0.52.5", ] [[package]] @@ -5802,7 +5696,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e57598f73cc7e1b2ac63c79c517b31a0877cd7c402cdcaa311b5208de7a095" dependencies = [ "blake2b_simd", - "ec-gpu", "ff", "group", "hex", @@ -5815,15 +5708,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" - -[[package]] -name = "path-slash" -version = "0.2.1" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pathdiff" @@ -5831,18 +5718,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" -[[package]] -name = "pbkdf2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" -dependencies = [ - "digest 0.10.7", - "hmac", - "password-hash", - "sha2 0.10.7", -] - [[package]] name = "pbkdf2" version = "0.12.2" @@ -5853,12 +5728,6 @@ dependencies = [ "hmac", ] -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - [[package]] name = "peg" version = "0.7.0" @@ -5876,8 +5745,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aa52829b8decbef693af90202711348ab001456803ba2a98eb4ec8fb70844c" dependencies = [ "peg-runtime", - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", ] [[package]] @@ -5895,6 +5764,16 @@ dependencies = [ "base64 0.13.1", ] +[[package]] +name = "pem" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +dependencies = [ + "base64 0.22.1", + "serde", +] + [[package]] name = "pem-rfc7468" version = "0.6.0" @@ -5915,15 +5794,15 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.3" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a4d085fd991ac8d5b05a147b437791b4260b76326baf0fc60cf7c9c27ecd33" +checksum = "560131c633294438da9f7c4b08189194b20946c8274c6b9e38881a7874dc8ee8" dependencies = [ "memchr", "thiserror", @@ -5932,9 +5811,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.3" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bee7be22ce7918f641a33f08e3f43388c7656772244e2bbb2477f44cc9021a" +checksum = "26293c9193fbca7b1a3bf9b79dc1e388e927e6cacaa78b4a3ab705a1d3d41459" dependencies = [ "pest", "pest_generator", @@ -5942,26 +5821,26 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.3" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1511785c5e98d79a05e8a6bc34b4ac2168a0e3e92161862030ad84daa223141" +checksum = "3ec22af7d3fb470a85dd2ca96b7c577a1eb4ef6f1683a9fe9a8c16e136c04687" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] name = "pest_meta" -version = "2.7.3" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42f0394d3123e33353ca5e1e89092e533d2cc490389f2bd6131c43c634ebc5f" +checksum = "d7a240022f37c361ec1878d646fc5b7d7c4d28d5946e1a80ad5a7a4f4ca0bdcd" dependencies = [ "once_cell", "pest", - "sha2 0.10.7", + "sha2 0.10.8", ] [[package]] @@ -5976,12 +5855,12 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset 0.4.2", - "indexmap 2.0.0", + "indexmap 2.2.6", ] [[package]] @@ -5991,7 +5870,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" dependencies = [ "futures", - "rustc_version", + "rustc_version 0.4.0", ] [[package]] @@ -6001,7 +5880,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ "phf_macros", - "phf_shared 0.11.2", + "phf_shared", ] [[package]] @@ -6010,7 +5889,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" dependencies = [ - "phf_shared 0.11.2", + "phf_shared", "rand", ] @@ -6018,22 +5897,13 @@ dependencies = [ name = "phf_macros" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" -dependencies = [ - "phf_generator", - "phf_shared 0.11.2", - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" dependencies = [ - "siphasher", + "phf_generator", + "phf_shared", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -6047,29 +5917,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -6105,27 +5975,21 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der 0.7.8", - "spki 0.7.2", + "der 0.7.9", + "spki 0.7.3", ] [[package]] name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - -[[package]] -name = "platforms" -version = "3.2.0" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14e6ab3f592e6fb464fc9712d8d6e6912de6473954635fd76a589d832cffcbb0" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "polyval" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ "cfg-if", "cpufeatures", @@ -6145,12 +6009,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - [[package]] name = "predicates" version = "2.1.5" @@ -6165,6 +6023,16 @@ dependencies = [ "regex", ] +[[package]] +name = "predicates" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" +dependencies = [ + "anstyle", + "predicates-core", +] + [[package]] name = "predicates-core" version = "1.0.6" @@ -6181,31 +6049,41 @@ dependencies = [ "termtree", ] +[[package]] +name = "pretty_assertions" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +dependencies = [ + "diff", + "yansi 0.5.1", +] + [[package]] name = "prettyplease" version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.85", "syn 1.0.109", ] [[package]] name = "prettyplease" -version = "0.2.15" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ - "proc-macro2 1.0.67", - "syn 2.0.37", + "proc-macro2 1.0.85", + "syn 2.0.68", ] [[package]] name = "primeorder" -version = "0.13.2" +version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c2fcef82c0ec6eefcc179b978446c399b3cdf73c392c35604e399eee6df1ee3" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" dependencies = [ "elliptic-curve", ] @@ -6224,9 +6102,9 @@ dependencies = [ [[package]] name = "primitive-types" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash 0.8.0", "impl-codec 0.6.0", @@ -6246,6 +6124,15 @@ dependencies = [ "toml 0.5.11", ] +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -6253,8 +6140,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", "version_check", ] @@ -6265,8 +6152,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "version_check", ] @@ -6281,18 +6168,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.67" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] [[package]] name = "prometheus" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" +checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1" dependencies = [ "cfg-if", "fnv", @@ -6306,12 +6193,11 @@ dependencies = [ [[package]] name = "prometheus-closure-metric" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "anyhow", "prometheus", "protobuf", - "workspace-hack", ] [[package]] @@ -6322,13 +6208,13 @@ checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.4.0", + "bitflags 2.5.0", "lazy_static", "num-traits", "rand", "rand_chacha", "rand_xorshift", - "regex-syntax 0.8.2", + "regex-syntax", "rusty-fork", "tempfile", "unarray", @@ -6387,7 +6273,7 @@ dependencies = [ "lazy_static", "log", "multimap", - "petgraph 0.6.4", + "petgraph 0.6.5", "prettyplease 0.1.25", "prost 0.11.9", "prost-types", @@ -6405,8 +6291,8 @@ checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" dependencies = [ "anyhow", "itertools 0.10.5", - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", ] @@ -6418,8 +6304,8 @@ checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools 0.10.5", - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", ] @@ -6430,10 +6316,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.11.0", - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "itertools 0.12.1", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -6454,6 +6340,15 @@ dependencies = [ "bytes", ] +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -6472,7 +6367,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.21.7", + "rustls 0.21.12", "thiserror", "tokio", "tracing", @@ -6480,15 +6375,15 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.10.4" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13f81c9a9d574310b8351f8666f5a93ac3b0069c45c28ad52c10291389a7cf9" +checksum = "141bf7dfde2fbc246bfd3fe12f2455aa24b0fbd9af535d8c86c7bd1381ff2b1a" dependencies = [ "bytes", "rand", - "ring", + "ring 0.16.20", "rustc-hash", - "rustls 0.21.7", + "rustls 0.21.12", "slab", "thiserror", "tinyvec", @@ -6503,7 +6398,7 @@ checksum = "055b4e778e8feb9f93c4e439f71dc2156ef13360b432b799e179a8c4cdf0b1d7" dependencies = [ "bytes", "libc", - "socket2 0.5.4", + "socket2", "tracing", "windows-sys 0.48.0", ] @@ -6519,11 +6414,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ - "proc-macro2 1.0.67", + "proc-macro2 1.0.85", ] [[package]] @@ -6594,163 +6489,108 @@ dependencies = [ [[package]] name = "random-string" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4e63111ec5292d8af9c220f06fe3bb87991cc78b6f1f7e291d1ae6b8a60817" +checksum = "f70fd13c3024ae3f17381bb5c4d409c6dc9ea6895c08fa2147aba305bea3c4af" dependencies = [ "fastrand 1.9.0", ] -[[package]] -name = "rayon" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] - [[package]] name = "rcgen" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" dependencies = [ - "pem", - "ring", + "pem 1.1.1", + "ring 0.16.20", "time", "yasna", ] [[package]] name = "readonly" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8f439da1766942fe069954da6058b2e6c1760eb878bae76f5be9fc29f56f574" -dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", -] - -[[package]] -name = "real_tokio" -version = "1.28.1" -source = "git+https://github.com/mystenmark/tokio-madsim-fork.git?rev=e4693500118d5e79ce098ee6dfc2c48f3ef19e45#e4693500118d5e79ce098ee6dfc2c48f3ef19e45" -dependencies = [ - "autocfg", - "bytes", - "libc", - "mio", - "num_cpus", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2 0.4.9", - "tokio-macros 2.1.0 (git+https://github.com/mystenmark/tokio-madsim-fork.git?rev=e4693500118d5e79ce098ee6dfc2c48f3ef19e45)", - "windows-sys 0.48.0", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "a25d631e41bfb5fdcde1d4e2215f62f7f0afa3ff11e26563765bd6ea1d229aeb" dependencies = [ - "bitflags 1.3.2", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", ] [[package]] name = "redox_users" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ "getrandom", - "redox_syscall 0.2.16", + "libredox", "thiserror", ] [[package]] name = "ref-cast" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acde58d073e9c79da00f2b5b84eed919c8326832648a5b109b3fce1bb1175280" +checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7473c2cfcf90008193dd0e3e16599455cb601a9fce322b5bb55de799664925" +checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] name = "regex" -version = "1.10.2" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", "regex-automata", - "regex-syntax 0.8.2", + "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" - -[[package]] -name = "regex-syntax" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "report" -version = "0.1.0" +version = "1.0.0" dependencies = [ "error-stack", "eyre", @@ -6761,20 +6601,20 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.25" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eea5a9eb898d3783f17c6407670e3592fd174cb81a10e51d4c37f49450b9946" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "base64 0.21.4", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", "futures-util", - "h2 0.3.24", - "http 0.2.9", - "http-body 0.4.5", - "hyper 0.14.27", - "hyper-rustls 0.24.1", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.29", + "hyper-rustls 0.24.2", "ipnet", "js-sys", "log", @@ -6782,13 +6622,13 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.7", - "rustls-pemfile 1.0.3", + "rustls 0.21.12", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", "sync_wrapper 0.1.2", - "system-configuration 0.6.0", + "system-configuration", "tokio", "tokio-rustls 0.24.1", "tower-service", @@ -6796,15 +6636,15 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 0.25.2", + "webpki-roots 0.25.4", "winreg 0.50.0", ] [[package]] name = "reqwest" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" +checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" dependencies = [ "base64 0.22.1", "bytes", @@ -6812,11 +6652,12 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.4.4", + "h2 0.4.6", "http 1.1.0", "http-body 1.0.0", "http-body-util", "hyper 1.3.1", + "hyper-rustls 0.27.2", "hyper-tls", "hyper-util", "ipnet", @@ -6827,12 +6668,12 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile 2.1.2", + "rustls-pemfile 2.1.3", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration 0.5.1", + "sync_wrapper 1.0.1", + "system-configuration", "tokio", "tokio-native-tls", "tower-service", @@ -6843,27 +6684,22 @@ dependencies = [ "winreg 0.52.0", ] -[[package]] -name = "retain_mut" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c31b5c4033f8fdde8700e4657be2c497e7288f01515be52168c631e2e4d4086" - [[package]] name = "rewards" -version = "0.3.0" +version = "1.0.0" dependencies = [ "axelar-wasm-std", - "axelar-wasm-std-derive", "cosmwasm-schema", "cosmwasm-std", "cw-multi-test", "cw-storage-plus 1.2.0", - "cw2 1.1.0", + "cw2 1.1.2", "error-stack", "itertools 0.11.0", + "msgs-derive", "report", "router-api", + "serde_json", "thiserror", ] @@ -6886,12 +6722,27 @@ dependencies = [ "cc", "libc", "once_cell", - "spin", - "untrusted", + "spin 0.5.2", + "untrusted 0.7.1", "web-sys", "winapi", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys 0.52.0", +] + [[package]] name = "ripemd" version = "0.1.3" @@ -6918,30 +6769,19 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", ] [[package]] name = "roaring" -version = "0.10.2" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6106b5cf8587f5834158895e9715a3c6c9716c8aefab57f1f7680917191c7873" +checksum = "7699249cc2c7d71939f30868f47e9d7add0bdc030d90ee10bfd16887ff8bb1c8" dependencies = [ "bytemuck", "byteorder", - "retain_mut", -] - -[[package]] -name = "rocksdb" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb6f170a4041d50a0ce04b0d2e14916d6ca863ea2e422689a5b694395d299ffe" -dependencies = [ - "libc", - "librocksdb-sys", ] [[package]] @@ -6957,46 +6797,47 @@ dependencies = [ [[package]] name = "router" -version = "0.3.2" +version = "1.0.0" dependencies = [ "axelar-wasm-std", - "axelar-wasm-std-derive", "cosmwasm-schema", "cosmwasm-std", "cw-multi-test", "cw-storage-plus 1.2.0", - "cw2 1.1.0", + "cw2 1.1.2", "error-stack", "flagset", "gateway-api", "hex", "integration-tests", "itertools 0.11.0", - "mockall", + "mockall 0.12.1", + "msgs-derive", "rand", "report", "router-api", "serde_json", + "thiserror", ] [[package]] name = "router-api" -version = "0.1.0" +version = "1.0.0" dependencies = [ "axelar-wasm-std", - "axelar-wasm-std-derive", "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus 1.2.0", "error-stack", "flagset", "hex", + "msgs-derive", "rand", "report", "schemars", "serde", "serde_json", - "sha3 0.10.8", + "sha3", "thiserror", "valuable", ] @@ -7016,20 +6857,30 @@ dependencies = [ "pkcs1", "pkcs8 0.9.0", "rand_core 0.6.4", - "sha2 0.10.7", - "signature 2.1.0", + "sha2 0.10.8", + "signature 2.2.0", "subtle", "zeroize", ] [[package]] name = "ruint" -version = "1.12.1" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f308135fef9fc398342da5472ce7c484529df23743fb7c734e0f3d472971e62" +checksum = "2c3cc4c2511671f327125da14133d0c5c5d137f006a1017a16f557bc85b16286" dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "bytes", + "fastrlp", + "num-bigint 0.4.5", + "num-traits", + "parity-scale-codec 3.6.12", + "primitive-types 0.12.2", "proptest", "rand", + "rlp", "ruint-macro", "serde", "valuable", @@ -7038,9 +6889,9 @@ dependencies = [ [[package]] name = "ruint-macro" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f86854cf50259291520509879a5c294c3c9a4c334e9ff65071c51e42ef1e2343" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" [[package]] name = "rust-ini" @@ -7054,9 +6905,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -7070,13 +6921,22 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver", + "semver 1.0.23", ] [[package]] @@ -7090,15 +6950,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.14" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -7109,21 +6969,46 @@ checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" dependencies = [ "base64 0.13.1", "log", - "ring", + "ring 0.16.20", "sct 0.6.1", - "webpki", + "webpki 0.21.4", ] [[package]] name = "rustls" -version = "0.21.7" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" +dependencies = [ + "log", + "ring 0.16.20", + "sct 0.7.1", + "webpki 0.22.4", +] + +[[package]] +name = "rustls" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", - "ring", - "rustls-webpki 0.101.5", - "sct 0.7.0", + "ring 0.17.8", + "rustls-webpki 0.101.7", + "sct 0.7.1", +] + +[[package]] +name = "rustls" +version = "0.23.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki 0.102.6", + "subtle", + "zeroize", ] [[package]] @@ -7138,20 +7023,32 @@ dependencies = [ "security-framework", ] +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile 1.0.4", + "schannel", + "security-framework", +] + [[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.7", ] [[package]] name = "rustls-pemfile" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" dependencies = [ "base64 0.22.1", "rustls-pki-types", @@ -7159,35 +7056,36 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" [[package]] name = "rustls-webpki" -version = "0.100.3" +version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6a5fc258f1c1276dfe3016516945546e2d5383911efc0fc4f1cdc5df3a4ae3" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring", - "untrusted", + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] name = "rustls-webpki" -version = "0.101.5" +version = "0.102.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a27e3b59326c16e23d30aeb7a36a24cc0d29e71d68ff611cdfb4a01d013bed" +checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" dependencies = [ - "ring", - "untrusted", + "ring 0.17.8", + "rustls-pki-types", + "untrusted 0.9.0", ] [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "rusty-fork" @@ -7203,18 +7101,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[package]] -name = "salsa20" -version = "0.10.2" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" -dependencies = [ - "cipher", -] +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -7227,42 +7116,42 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.9.0" +version = "2.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0a159d0c45c12b20c5a844feb1fe4bea86e28f17b92a5f0c42193634d3782" +checksum = "eca070c12893629e2cc820a9761bedf6ce1dcddc9852984d1dc734b8bd9bd024" dependencies = [ "cfg-if", "derive_more", - "parity-scale-codec 3.6.5", + "parity-scale-codec 3.6.12", "scale-info-derive", ] [[package]] name = "scale-info-derive" -version = "2.9.0" +version = "2.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29" +checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62" dependencies = [ - "proc-macro-crate", - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro-crate 3.1.0", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", ] [[package]] name = "schannel" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "schemars" -version = "0.8.15" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f7b0ce13155372a76ee2e1c5ffba1fe61ede73fbea5630d61eee6fac4929c0c" +checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" dependencies = [ "dyn-clone", "either", @@ -7273,14 +7162,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.15" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e85e2a16b12bdb763244c69ab79363d71db2b4b918a2def53f80b02e0574b13c" +checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "serde_derive_internals", - "syn 1.0.109", + "syn 2.0.68", ] [[package]] @@ -7289,36 +7178,24 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "scrypt" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f9e24d2b632954ded8ab2ef9fea0a0c769ea56ea98bddbafbad22caeeadf45d" -dependencies = [ - "hmac", - "pbkdf2 0.11.0", - "salsa20", - "sha2 0.10.7", -] - [[package]] name = "sct" 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" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring", - "untrusted", + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] @@ -7328,7 +7205,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct", - "der 0.7.8", + "der 0.7.9", "generic-array", "pkcs8 0.10.2", "subtle", @@ -7341,7 +7218,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 0.12.0", "rand", "secp256k1-sys", ] @@ -7357,11 +7234,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.2" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "core-foundation", "core-foundation-sys", "libc", @@ -7370,9 +7247,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" dependencies = [ "core-foundation-sys", "libc", @@ -7380,13 +7257,31 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.18" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" dependencies = [ "serde", ] +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + [[package]] name = "send_wrapper" version = "0.4.0" @@ -7401,9 +7296,9 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] @@ -7440,42 +7335,42 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.12" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" +checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] name = "serde_derive_internals" -version = "0.26.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 1.0.109", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ - "indexmap 2.0.0", + "indexmap 2.2.6", "itoa", "ryu", "serde", @@ -7483,9 +7378,9 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" dependencies = [ "itoa", "serde", @@ -7493,13 +7388,13 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.16" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -7541,18 +7436,19 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.3.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca3b16a3d82c4088f343b7480a93550b3eabe1a358569c2dfe38bbcead07237" +checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20" dependencies = [ - "base64 0.21.4", + "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.0.0", + "indexmap 2.2.6", "serde", + "serde_derive", "serde_json", - "serde_with_macros 3.3.0", + "serde_with_macros 3.8.1", "time", ] @@ -7562,22 +7458,22 @@ version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" dependencies = [ - "darling 0.20.3", - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "darling 0.20.9", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] name = "serde_with_macros" -version = "3.3.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e6be15c453eb305019bfa438b1593c731f36a289a7853f7707ee29e870b3b3c" +checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2" dependencies = [ - "darling 0.20.3", - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "darling 0.20.9", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -7594,17 +7490,18 @@ dependencies = [ [[package]] name = "service-registry" -version = "0.3.0" +version = "1.0.0" dependencies = [ "axelar-wasm-std", - "axelar-wasm-std-derive", + "coordinator", "cosmwasm-schema", "cosmwasm-std", "cw-multi-test", "cw-storage-plus 1.2.0", - "cw2 1.1.0", + "cw2 1.1.2", "error-stack", "integration-tests", + "msgs-derive", "report", "router-api", "schemars", @@ -7612,6 +7509,19 @@ dependencies = [ "thiserror", ] +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + [[package]] name = "sha1" version = "0.10.6" @@ -7638,9 +7548,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", @@ -7650,23 +7560,11 @@ dependencies = [ [[package]] name = "sha2-asm" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27ba7066011e3fb30d808b51affff34f0a66d3a03a58edd787c6e420e40e44e" -dependencies = [ - "cc", -] - -[[package]] -name = "sha3" -version = "0.9.1" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +checksum = "b845214d6175804686b2bd482bcffe96651bb2d1200742b712003504a2dac1ab" dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "keccak", - "opaque-debug", + "cc", ] [[package]] @@ -7679,11 +7577,21 @@ dependencies = [ "keccak", ] +[[package]] +name = "sha3-asm" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9b57fd861253bff08bb1919e995f90ba8f4889de2726091c8876f3a4e823b40" +dependencies = [ + "cc", + "cfg-if", +] + [[package]] name = "sharded-slab" -version = "0.1.4" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] @@ -7691,27 +7599,20 @@ dependencies = [ [[package]] name = "shared-crypto" version = "0.0.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "bcs", "eyre", "fastcrypto", "serde", "serde_repr", - "workspace-hack", ] -[[package]] -name = "shlex" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" - [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -7724,9 +7625,9 @@ checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" [[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", @@ -7734,7 +7635,7 @@ dependencies = [ [[package]] name = "signature-verifier-api" -version = "0.1.0" +version = "1.0.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -7742,17 +7643,11 @@ dependencies = [ "thiserror", ] -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - [[package]] name = "similar" -version = "2.2.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf" +checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" [[package]] name = "simple_asn1" @@ -7760,7 +7655,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" dependencies = [ - "num-bigint 0.4.4", + "num-bigint 0.4.5", "num-traits", "thiserror", "time", @@ -7799,49 +7694,47 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "snap" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831" +checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" [[package]] name = "socket2" -version = "0.4.9" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "winapi", + "windows-sys 0.52.0", ] [[package]] -name = "socket2" -version = "0.5.4" +name = "soketto" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" dependencies = [ - "libc", - "windows-sys 0.48.0", + "base64 0.13.1", + "bytes", + "futures", + "http 0.2.12", + "httparse", + "log", + "rand", + "sha-1", ] [[package]] -name = "solang-parser" -version = "0.3.3" +name = "spin" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c425ce1c59f4b154717592f0bdf4715c3a1d55058883622d3157e1f0908a5b26" -dependencies = [ - "itertools 0.11.0", - "lalrpop", - "lalrpop-util", - "phf", - "thiserror", - "unicode-xid 0.2.4", -] +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spin" -version = "0.5.2" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "spki" @@ -7855,12 +7748,31 @@ dependencies = [ [[package]] name = "spki" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der 0.7.8", + "der 0.7.9", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stacker" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "winapi", ] [[package]] @@ -7870,16 +7782,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] -name = "string_cache" -version = "0.8.7" +name = "stellar" +version = "1.0.0" +dependencies = [ + "axelar-wasm-std", + "cosmwasm-std", + "error-stack", + "goldie", + "hex", + "multisig", + "rand", + "router-api", + "serde", + "serde_json", + "sha3", + "stellar-strkey", + "stellar-xdr", + "thiserror", +] + +[[package]] +name = "stellar-strkey" +version = "0.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +checksum = "12d2bf45e114117ea91d820a846fd1afbe3ba7d717988fee094ce8227a3bf8bd" dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot", - "phf_shared 0.10.0", - "precomputed-hash", + "base32", + "crate-git-revision", + "thiserror", +] + +[[package]] +name = "stellar-xdr" +version = "21.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2675a71212ed39a806e415b0dbf4702879ff288ec7f5ee996dda42a135512b50" +dependencies = [ + "crate-git-revision", + "escape-bytes", + "hex", + "stellar-strkey", ] [[package]] @@ -7888,6 +7830,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.24.1" @@ -7903,7 +7851,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]] @@ -7912,7 +7860,7 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" dependencies = [ - "strum_macros 0.26.2", + "strum_macros 0.26.4", ] [[package]] @@ -7922,43 +7870,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "rustversion", "syn 1.0.109", ] [[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", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "rustversion", - "syn 2.0.37", + "syn 2.0.68", ] [[package]] name = "strum_macros" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck 0.4.1", - "proc-macro2 1.0.67", - "quote 1.0.33", + "heck 0.5.0", + "proc-macro2 1.0.85", + "quote 1.0.36", "rustversion", - "syn 2.0.37", + "syn 2.0.68", ] [[package]] name = "subtle" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "subtle-encoding" @@ -7978,34 +7926,34 @@ checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" [[package]] name = "sui-enum-compat-util" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "serde_yaml", - "workspace-hack", ] [[package]] -name = "sui-framework" -version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +name = "sui-gateway" +version = "1.0.0" dependencies = [ - "anyhow", "bcs", - "move-binary-format", - "move-core-types", - "move-package", - "once_cell", + "bs58 0.5.1", + "cosmwasm-std", + "error-stack", + "hex", + "multisig", + "rand", + "router-api", "serde", - "sui-move-build", - "sui-types", - "tracing", - "workspace-hack", + "serde_json", + "sha3", + "sui-types 1.0.0", + "thiserror", ] [[package]] name = "sui-json" version = "0.0.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "anyhow", "bcs", @@ -8016,15 +7964,13 @@ dependencies = [ "schemars", "serde", "serde_json", - "sui-framework", - "sui-types", - "workspace-hack", + "sui-types 0.1.0", ] [[package]] name = "sui-json-rpc-types" version = "0.0.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "anyhow", "bcs", @@ -8044,106 +7990,125 @@ dependencies = [ "sui-enum-compat-util", "sui-json", "sui-macros", + "sui-package-resolver", "sui-protocol-config", - "sui-types", + "sui-types 0.1.0", "tabled", "tracing", - "workspace-hack", ] [[package]] name = "sui-macros" version = "0.7.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "futures", "once_cell", "sui-proc-macros", "tracing", - "workspace-hack", ] [[package]] -name = "sui-move-build" -version = "0.0.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +name = "sui-package-resolver" +version = "0.1.0" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ - "anyhow", - "fastcrypto", + "async-trait", + "bcs", + "eyre", + "lru", "move-binary-format", - "move-bytecode-utils", - "move-bytecode-verifier", "move-command-line-common", - "move-compiler", "move-core-types", - "move-ir-types", - "move-package", - "move-symbol-pool", - "serde-reflection", - "sui-types", - "sui-verifier-latest", - "tempfile", - "workspace-hack", + "serde", + "sui-rest-api", + "sui-types 0.1.0", + "thiserror", + "tokio", ] [[package]] name = "sui-proc-macros" version = "0.7.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "msim-macros", - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "sui-enum-compat-util", - "syn 2.0.37", - "workspace-hack", + "syn 2.0.68", ] [[package]] name = "sui-protocol-config" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "clap", "insta", + "move-vm-config", "schemars", "serde", "serde_with 2.3.3", "sui-protocol-config-macros", "tracing", - "workspace-hack", ] [[package]] name = "sui-protocol-config-macros" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", - "workspace-hack", +] + +[[package]] +name = "sui-rest-api" +version = "0.1.0" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" +dependencies = [ + "anyhow", + "axum 0.6.20", + "bcs", + "fastcrypto", + "mime", + "rand", + "reqwest 0.11.27", + "serde", + "serde_json", + "serde_with 2.3.3", + "sui-types 0.1.0", + "tap", + "thiserror", ] [[package]] name = "sui-types" version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ "anemo", "anyhow", "bcs", + "better_any", "bincode", "byteorder", + "chrono", + "consensus-config", "derivative", "derive_more", "enum_dispatch", "eyre", "fastcrypto", + "fastcrypto-tbls", "fastcrypto-zkp", "im", - "indexmap 1.9.3", + "indexmap 2.2.6", "itertools 0.10.5", + "jsonrpsee", + "lru", "move-binary-format", "move-bytecode-utils", "move-command-line-common", @@ -8157,7 +8122,12 @@ dependencies = [ "mysten-network", "narwhal-config", "narwhal-crypto", + "nonempty", + "num-bigint 0.4.5", + "num-traits", + "num_enum 0.6.1", "once_cell", + "parking_lot", "prometheus", "proptest", "proptest-derive", @@ -8178,45 +8148,20 @@ dependencies = [ "sui-protocol-config", "tap", "thiserror", - "tonic 0.10.2", + "tonic 0.11.0", "tracing", - "typed-store", - "workspace-hack", -] - -[[package]] -name = "sui-verifier-latest" -version = "0.1.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" -dependencies = [ - "move-abstract-stack", - "move-binary-format", - "move-bytecode-utils", - "move-bytecode-verifier", - "move-core-types", - "move-vm-config", - "sui-types", - "workspace-hack", + "typed-store-error", ] [[package]] -name = "svm-rs" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597e3a746727984cb7ea2487b6a40726cad0dbe86628e7d429aa6b8c4c153db4" +name = "sui-types" +version = "1.0.0" dependencies = [ - "dirs", - "fs2", + "error-stack", "hex", - "once_cell", - "reqwest 0.11.25", - "semver", + "rand", "serde", - "serde_json", - "sha2 0.10.7", "thiserror", - "url", - "zip 0.6.6", ] [[package]] @@ -8236,32 +8181,32 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.37" +version = "2.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "unicode-ident", ] [[package]] name = "syn-solidity" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8db114c44cf843a8bacd37a146e37987a0b823a0e8bc4fdc610c9c72ab397a5" +checksum = "c837dc8852cb7074e46b444afb81783140dab12c58867b49fb3898fbafedf7ea" dependencies = [ "paste", - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -8282,32 +8227,32 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", "unicode-xid 0.2.4", ] [[package]] -name = "system-configuration" -version = "0.5.1" +name = "synstructure" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys 0.5.0", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] name = "system-configuration" -version = "0.6.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bc6ee10a9b4fcf576e9b0819d95ec16f4d2c02d39fd83ac1c8789785c4a42" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ - "bitflags 2.4.0", + "bitflags 1.3.2", "core-foundation", - "system-configuration-sys 0.6.0", + "system-configuration-sys", ] [[package]] @@ -8320,16 +8265,6 @@ dependencies = [ "libc", ] -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "tabled" version = "0.12.2" @@ -8349,8 +8284,8 @@ checksum = "99f688a08b54f4f02f0a3c382aefdb7884d3d69609f785bd253dc033243e3fe4" dependencies = [ "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", ] @@ -8362,15 +8297,14 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.8.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", - "fastrand 2.0.0", - "redox_syscall 0.3.5", + "fastrand 2.1.0", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -8381,7 +8315,7 @@ checksum = "3f0a7d05cf78524782337f8edd55cbc578d159a16ad4affe2135c92f7dbac7f0" dependencies = [ "bytes", "digest 0.10.7", - "ed25519 2.2.2", + "ed25519 2.2.3", "ed25519-consensus", "flex-error", "futures", @@ -8395,8 +8329,8 @@ dependencies = [ "serde_bytes", "serde_json", "serde_repr", - "sha2 0.10.7", - "signature 2.1.0", + "sha2 0.10.8", + "signature 2.2.0", "subtle", "subtle-encoding", "tendermint-proto 0.32.2", @@ -8411,7 +8345,7 @@ source = "git+https://github.com/axelarnetwork/tendermint-rs.git?branch=v0.33.x# dependencies = [ "bytes", "digest 0.10.7", - "ed25519 2.2.2", + "ed25519 2.2.3", "ed25519-consensus", "flex-error", "futures", @@ -8423,8 +8357,8 @@ dependencies = [ "serde_bytes", "serde_json", "serde_repr", - "sha2 0.10.7", - "signature 2.1.0", + "sha2 0.10.8", + "signature 2.2.0", "subtle", "subtle-encoding", "tendermint-proto 0.33.0", @@ -8445,24 +8379,6 @@ dependencies = [ "url", ] -[[package]] -name = "tendermint-proto" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5895470f28c530f8ae8c4071bf8190304ce00bd131d25e81730453124a3375c" -dependencies = [ - "bytes", - "flex-error", - "num-derive", - "num-traits", - "prost 0.11.9", - "prost-types", - "serde", - "serde_bytes", - "subtle-encoding", - "time", -] - [[package]] name = "tendermint-proto" version = "0.32.2" @@ -8508,13 +8424,13 @@ dependencies = [ "flex-error", "futures", "getrandom", - "http 0.2.9", - "hyper 0.14.27", + "http 0.2.12", + "hyper 0.14.29", "hyper-proxy", "hyper-rustls 0.22.1", "peg", "pin-project", - "semver", + "semver 1.0.23", "serde", "serde_bytes", "serde_json", @@ -8533,23 +8449,22 @@ dependencies = [ ] [[package]] -name = "term" -version = "0.7.0" +name = "termcolor" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ - "dirs-next", - "rustversion", - "winapi", + "winapi-util", ] [[package]] -name = "termcolor" -version = "1.1.3" +name = "terminal_size" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" dependencies = [ - "winapi-util", + "rustix", + "windows-sys 0.48.0", ] [[package]] @@ -8560,29 +8475,29 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.48" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.48" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -8637,6 +8552,16 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -8654,30 +8579,30 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tofn" -version = "0.2.0" -source = "git+https://github.com/axelarnetwork/tofn.git?branch=update-deps#88285a1ed5f5135aab20ae73e124a8af63087c35" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "255a99b697179124254f23d05c2bc394276ba870791d99a9f5896f7f60d40eb9" dependencies = [ "bincode", "crypto-bigint", - "der 0.7.8", "ecdsa", - "ed25519 2.2.2", + "ed25519 2.2.3", "ed25519-dalek", "hmac", "k256", "rand", "rand_chacha", "serde", - "sha2 0.10.7", + "sha2 0.10.8", "tracing", "zeroize", ] [[package]] name = "tokio" -version = "1.32.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes", @@ -8687,8 +8612,8 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.4", - "tokio-macros 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2", + "tokio-macros", "windows-sys 0.48.0", ] @@ -8696,31 +8621,21 @@ dependencies = [ name = "tokio-io-timeout" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" -dependencies = [ - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "pin-project-lite", + "tokio", ] [[package]] name = "tokio-macros" -version = "2.1.0" -source = "git+https://github.com/mystenmark/tokio-madsim-fork.git?rev=e4693500118d5e79ce098ee6dfc2c48f3ef19e45#e4693500118d5e79ce098ee6dfc2c48f3ef19e45" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -8741,7 +8656,18 @@ checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" dependencies = [ "rustls 0.19.1", "tokio", - "webpki", + "webpki 0.21.4", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls 0.20.9", + "tokio", + "webpki 0.22.4", ] [[package]] @@ -8750,66 +8676,60 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.7", + "rustls 0.21.12", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls 0.23.12", + "rustls-pki-types", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", "tokio", - "tokio-util 0.7.9", + "tokio-util", ] [[package]] name = "tokio-tungstenite" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2dbec703c26b00d74844519606ef15d09a7d6857860f84ad223dec002ddea2" +checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ "futures-util", "log", - "rustls 0.21.7", + "rustls 0.21.12", "tokio", "tokio-rustls 0.24.1", "tungstenite", - "webpki-roots 0.23.1", -] - -[[package]] -name = "tokio-util" -version = "0.7.7" -source = "git+https://github.com/mystenmark/tokio-madsim-fork.git?rev=e4693500118d5e79ce098ee6dfc2c48f3ef19e45#e4693500118d5e79ce098ee6dfc2c48f3ef19e45" -dependencies = [ - "bytes", - "futures-core", - "futures-io", - "futures-sink", - "futures-util", - "hashbrown 0.12.3", - "pin-project-lite", - "real_tokio", - "slab", - "tracing", + "webpki-roots 0.25.4", ] [[package]] name = "tokio-util" -version = "0.7.9" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] @@ -8823,14 +8743,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.13" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.22.14", ] [[package]] @@ -8844,72 +8764,76 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.13" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap 2.2.6", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ - "indexmap 2.0.0", + "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.13", ] [[package]] name = "tonic" -version = "0.8.3" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f219fad3b929bef19b1f86fbc0358d35daed8f2cac972037ac0dc10bbb8d5fb" +checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" dependencies = [ - "async-stream", "async-trait", "axum 0.6.20", - "base64 0.13.1", + "base64 0.21.7", "bytes", "futures-core", "futures-util", - "h2 0.3.24", - "http 0.2.9", - "http-body 0.4.5", - "hyper 0.14.27", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.29", "hyper-timeout", "percent-encoding", "pin-project", "prost 0.11.9", - "prost-derive 0.11.9", "tokio", "tokio-stream", - "tokio-util 0.7.9", "tower", "tower-layer", "tower-service", "tracing", - "tracing-futures", ] [[package]] name = "tonic" -version = "0.10.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e" +checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" dependencies = [ "async-stream", "async-trait", "axum 0.6.20", - "base64 0.21.4", + "base64 0.21.7", "bytes", - "h2 0.3.24", - "http 0.2.9", - "http-body 0.4.5", - "hyper 0.14.27", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.29", "hyper-timeout", "percent-encoding", "pin-project", "prost 0.12.6", - "rustls 0.21.7", - "rustls-pemfile 1.0.3", "tokio", - "tokio-rustls 0.24.1", "tokio-stream", "tower", "tower-layer", @@ -8924,23 +8848,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bf5e9b9c0f7e0a7c027dcfaba7b2c60816c7049171f679d99ee2ff65d0de8c4" dependencies = [ "prettyplease 0.1.25", - "proc-macro2 1.0.67", + "proc-macro2 1.0.85", "prost-build", - "quote 1.0.33", + "quote 1.0.36", "syn 1.0.109", ] [[package]] name = "tonic-health" -version = "0.10.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f80db390246dfb46553481f6024f0082ba00178ea495dbb99e70ba9a4fafb5e1" +checksum = "2cef6e24bc96871001a7e48e820ab240b3de2201e59b517cf52835df2f1d2350" dependencies = [ "async-stream", "prost 0.12.6", "tokio", "tokio-stream", - "tonic 0.10.2", + "tonic 0.11.0", ] [[package]] @@ -8958,7 +8882,7 @@ dependencies = [ "rand", "slab", "tokio", - "tokio-util 0.7.9", + "tokio-util", "tower-layer", "tower-service", "tracing", @@ -8976,8 +8900,8 @@ dependencies = [ "bytes", "futures-core", "futures-util", - "http 0.2.9", - "http-body 0.4.5", + "http 0.2.12", + "http-body 0.4.6", "http-range-header", "httpdate", "iri-string", @@ -8986,12 +8910,12 @@ dependencies = [ "percent-encoding", "pin-project-lite", "tokio", - "tokio-util 0.7.9", + "tokio-util", "tower", "tower-layer", "tower-service", "tracing", - "uuid 1.4.1", + "uuid 1.8.0", ] [[package]] @@ -9008,11 +8932,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", @@ -9021,20 +8944,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", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[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", @@ -9052,12 +8975,12 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ - "lazy_static", "log", + "once_cell", "tracing-core", ] @@ -9075,9 +8998,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 = [ "nu-ansi-term", "serde", @@ -9098,37 +9021,31 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b79e2e9c9ab44c6d7c20d5976961b47e8f49ac199154daa514b77cd1ab536625" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", ] -[[package]] -name = "treeline" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" - [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e862a1c4128df0112ab625f55cd5c934bcb4312ba80b39ae4b4835a3fd58e649" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" dependencies = [ "byteorder", "bytes", "data-encoding", - "http 0.2.9", + "http 0.2.12", "httparse", "log", "rand", - "rustls 0.21.7", + "rustls 0.21.12", "sha1", "thiserror", "url", @@ -9150,37 +9067,18 @@ version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f718dfaf347dcb5b983bfc87608144b0bad87970aebcbea5ce44d2a30c08e63" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] -name = "typed-store" +name = "typed-store-error" version = "0.4.0" -source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.14.2#299cbeafbb6aa5601e08f00ac24bd647c61a63e2" +source = "git+https://github.com/mystenlabs/sui?tag=mainnet-v1.26.2#f531168c745260b60a4ec4906c9f2b22240d872d" dependencies = [ - "async-trait", - "bcs", - "bincode", - "collectable", - "eyre", - "fdlimit", - "hdrhistogram", - "itertools 0.10.5", - "msim", - "once_cell", - "ouroboros", - "prometheus", - "rand", - "rocksdb", "serde", - "sui-macros", - "tap", "thiserror", - "tokio", - "tracing", - "workspace-hack", ] [[package]] @@ -9222,12 +9120,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - [[package]] name = "unicode-ident" version = "1.0.12" @@ -9243,17 +9135,11 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-segmentation" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" - [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unicode-xid" @@ -9289,11 +9175,28 @@ 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 = "upon" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fe29601d1624f104fa9a35ea71a5f523dd8bd1cfc8c31f8124ad2b829f013c0" +dependencies = [ + "serde", + "unicode-ident", + "unicode-width", +] + [[package]] name = "url" -version = "2.4.1" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56" dependencies = [ "form_urlencoded", "idna", @@ -9306,27 +9209,35 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" -dependencies = [ - "getrandom", - "serde", -] [[package]] name = "uuid" -version = "1.4.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" dependencies = [ "getrandom", "rand", @@ -9347,8 +9258,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d44690c645190cfce32f91a1582281654b2338c6073fa250b0949fd25c55b32" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", + "proc-macro2 1.0.85", + "quote 1.0.36", "syn 1.0.109", ] @@ -9368,7 +9279,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae2faf80ac463422992abf4de234731279c058aaf33171ca70277c98406b124" dependencies = [ - "quote 1.0.33", + "quote 1.0.36", "syn 1.0.109", ] @@ -9384,20 +9295,28 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vfs" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e4fe92cfc1bad19c19925d5eee4b30584dbbdee4ff10183b261acccbef74e2d" + [[package]] name = "voting-verifier" -version = "0.4.0" +version = "1.0.0" dependencies = [ + "alloy-primitives", "axelar-wasm-std", - "axelar-wasm-std-derive", "client", "cosmwasm-schema", "cosmwasm-std", "cw-multi-test", "cw-storage-plus 1.2.0", - "cw2 1.1.0", + "cw2 1.1.2", "error-stack", "integration-tests", + "itertools 0.11.0", + "msgs-derive", "multisig", "rand", "report", @@ -9405,7 +9324,7 @@ dependencies = [ "router-api", "serde_json", "service-registry", - "sha3 0.10.8", + "sha3", "thiserror", ] @@ -9420,9 +9339,9 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -9445,9 +9364,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -9455,24 +9374,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.37" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -9482,38 +9401,38 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ - "quote 1.0.33", + "quote 1.0.36", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -9525,8 +9444,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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" +dependencies = [ + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] @@ -9535,23 +9464,23 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" dependencies = [ - "webpki", + "webpki 0.21.4", ] [[package]] name = "webpki-roots" -version = "0.23.1" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" dependencies = [ - "rustls-webpki 0.100.3", + "webpki 0.22.4", ] [[package]] name = "webpki-roots" -version = "0.25.2" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "which" @@ -9565,22 +9494,6 @@ dependencies = [ "rustix", ] -[[package]] -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 = "widestring" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" - [[package]] name = "winapi" version = "0.3.9" @@ -9599,11 +9512,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -9613,21 +9526,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.42.2", + "windows-targets 0.52.5", ] [[package]] @@ -9640,18 +9544,12 @@ dependencies = [ ] [[package]] -name = "windows-targets" -version = "0.42.2" +name = "windows-sys" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets 0.52.5", ] [[package]] @@ -9670,10 +9568,20 @@ dependencies = [ ] [[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" +name = "windows-targets" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", +] [[package]] name = "windows_aarch64_gnullvm" @@ -9682,10 +9590,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" +name = "windows_aarch64_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -9694,10 +9602,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] -name = "windows_i686_gnu" -version = "0.42.2" +name = "windows_aarch64_msvc" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -9706,10 +9614,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] -name = "windows_i686_msvc" -version = "0.42.2" +name = "windows_i686_gnu" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -9718,10 +9632,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" +name = "windows_i686_msvc" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -9730,10 +9644,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" +name = "windows_x86_64_gnu" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -9742,10 +9656,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" +name = "windows_x86_64_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -9753,11 +9667,26 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + [[package]] name = "winnow" -version = "0.6.8" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] @@ -9783,10 +9712,16 @@ dependencies = [ ] [[package]] -name = "workspace-hack" -version = "0.1.0" +name = "write16" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beffa227304dbaea3ad6a06ac674f9bc83a3dec3b7f63eeb442de37e7cb6bb01" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "ws_stream_wasm" @@ -9799,7 +9734,7 @@ dependencies = [ "js-sys", "log", "pharos", - "rustc_version", + "rustc_version 0.4.0", "send_wrapper 0.6.0", "thiserror", "wasm-bindgen", @@ -9855,6 +9790,12 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "yasna" version = "0.5.2" @@ -9865,130 +9806,108 @@ dependencies = [ ] [[package]] -name = "zeroize" -version = "1.6.0" +name = "yoke" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" dependencies = [ - "zeroize_derive", + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", ] [[package]] -name = "zeroize_derive" -version = "1.4.2" +name = "yoke-derive" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" dependencies = [ - "proc-macro2 1.0.67", - "quote 1.0.33", - "syn 2.0.37", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", + "synstructure 0.13.1", ] [[package]] -name = "zip" -version = "0.6.6" +name = "zerocopy" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ - "aes", - "byteorder", - "bzip2", - "constant_time_eq 0.1.5", - "crc32fast", - "crossbeam-utils", - "flate2", - "hmac", - "pbkdf2 0.11.0", - "sha1", - "time", - "zstd 0.11.2+zstd.1.5.2", + "zerocopy-derive", ] [[package]] -name = "zip" -version = "2.0.0" +name = "zerocopy-derive" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fccb210625924ecbbe92f9bb497d04b167b64fe5540cec75f10b16e0c51ee92b" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ - "aes", - "arbitrary", - "bzip2", - "constant_time_eq 0.3.0", - "crc32fast", - "crossbeam-utils", - "deflate64", - "displaydoc", - "flate2", - "hmac", - "indexmap 2.0.0", - "lzma-rs", - "pbkdf2 0.12.2", - "rand", - "sha1", - "thiserror", - "time", - "zeroize", - "zopfli", - "zstd 0.13.1", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] -name = "zopfli" -version = "0.8.1" +name = "zerofrom" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" dependencies = [ - "bumpalo", - "crc32fast", - "lockfree-object-pool", - "log", - "once_cell", - "simd-adler32", + "zerofrom-derive", ] [[package]] -name = "zstd" -version = "0.11.2+zstd.1.5.2" +name = "zerofrom-derive" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" dependencies = [ - "zstd-safe 5.0.2+zstd.1.5.2", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", + "synstructure 0.13.1", ] [[package]] -name = "zstd" -version = "0.13.1" +name = "zeroize" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ - "zstd-safe 7.1.0", + "zeroize_derive", ] [[package]] -name = "zstd-safe" -version = "5.0.2+zstd.1.5.2" +name = "zeroize_derive" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "libc", - "zstd-sys", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] -name = "zstd-safe" -version = "7.1.0" +name = "zerovec" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" +checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c" dependencies = [ - "zstd-sys", + "yoke", + "zerofrom", + "zerovec-derive", ] [[package]] -name = "zstd-sys" -version = "2.0.10+zstd.1.5.6" +name = "zerovec-derive" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" +checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7" dependencies = [ - "cc", - "pkg-config", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.68", ] diff --git a/Cargo.toml b/Cargo.toml index 01387b218..f8833bb3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,45 +1,71 @@ [workspace] -members = ["ampd", "contracts/*", "integration-tests", "packages/*"] +members = ["ampd", "contracts/*", "external-gateways/*", "integration-tests", "interchain-token-service", "packages/*"] resolver = "2" [workspace.package] -rust-version = "1.75.0" # be sure there is an optimizer release supporting this version before updating. See https://github.com/CosmWasm/optimizer +rust-version = "1.78.0" # be sure there is an optimizer release supporting this version before updating. See https://github.com/CosmWasm/optimizer +edition = "2021" [workspace.dependencies] -router = { version = "^0.3.2", path = "contracts/router" } +router = { version = "^1.0.0", path = "contracts/router" } cosmwasm-std = "1.5.5" cosmwasm-schema = "1.5.5" -cw-storage-plus = "1.2.0" +cw-storage-plus = { version = "1.2.0", features = ["iterator", "macro"] } cw2 = "1.1.0" +ed25519-dalek = { version = "2.1.1", default-features = false } error-stack = { version = "0.4.0", features = ["eyre"] } -events = { version = "^0.1.0", path = "packages/events" } -events-derive = { version = "^0.1.0", path = "packages/events-derive" } -evm-gateway = { version = "^0.1.0", path = "packages/evm-gateway" } -axelar-wasm-std = { version = "^0.1.0", path = "packages/axelar-wasm-std" } -axelar-wasm-std-derive = { version = "^0.1.0", path = "packages/axelar-wasm-std-derive" } -integration-tests = { version = "^0.1.0", path = "integration-tests" } +events = { version = "^1.0.0", path = "packages/events" } +events-derive = { version = "^1.0.0", path = "packages/events-derive" } +evm-gateway = { version = "^1.0.0", path = "packages/evm-gateway" } +sui-types = { version = "^1.0.0", path = "packages/sui-types" } +sui-gateway = { version = "^1.0.0", path = "packages/sui-gateway" } +stellar = { version = "^1.0.0", path = "external-gateways/stellar" } +axelar-wasm-std = { version = "^1.0.0", path = "packages/axelar-wasm-std" } +axelar-wasm-std-derive = { version = "^1.0.0", path = "packages/axelar-wasm-std-derive" } +hex = "0.4.3" +integration-tests = { version = "^1.0.0", path = "integration-tests" } +into-inner-derive = { version = "^1.0.0", path = "packages/into-inner-derive" } itertools = "0.11.0" -voting-verifier = { version = "^0.4.0", path = "contracts/voting-verifier" } -coordinator = { version = "^0.1.2", path = "contracts/coordinator" } -multisig = { version = "^0.3.0", path = "contracts/multisig" } -multisig-prover = { version = "^0.5.0", path = "contracts/multisig-prover" } +voting-verifier = { version = "^1.0.0", path = "contracts/voting-verifier" } +coordinator = { version = "^1.0.0", path = "contracts/coordinator" } +multisig = { version = "^1.0.0", path = "contracts/multisig" } +msgs-derive = { version = "^1.0.0", path = "packages/msgs-derive" } +multisig-prover = { version = "^1.0.0", path = "contracts/multisig-prover" } num-traits = { version = "0.2.14", default-features = false } -service-registry = { version = "^0.3.0", path = "contracts/service-registry" } -gateway = { version = "^0.2.2", path = "contracts/gateway" } -gateway-api = { version = "^0.1.0", path = "packages/gateway-api" } -router-api = { version = "^0.1.0", path = "packages/router-api" } -report = { version = "^0.1.0", path = "packages/report" } -client = { version = "^0.1.0", path = "packages/client" } -rewards = { version = "^0.3.0", path = "contracts/rewards" } -thiserror = "1.0.47" +service-registry = { version = "^1.0.0", path = "contracts/service-registry" } +k256 = { version = "0.13.1", features = ["ecdsa"] } +gateway = { version = "^1.0.0", path = "contracts/gateway" } +gateway-api = { version = "^1.0.0", path = "packages/gateway-api" } +router-api = { version = "^1.0.0", path = "packages/router-api" } +report = { version = "^1.0.0", path = "packages/report" } +client = { version = "^1.0.0", path = "packages/client" } +quote = "1.0.36" +bcs = "0.1.5" +rewards = { version = "^1.0.0", path = "contracts/rewards" } +thiserror = "1.0.61" +mockall = "0.12.1" serde = { version = "1.0.145", default-features = false, features = ["derive"] } serde_json = "1.0.89" schemars = "0.8.10" sha3 = { version = "0.10.8", default-features = false, features = [] } -signature-verifier-api = { version = "^0.1.0", path = "packages/signature-verifier-api" } +signature-verifier-api = { version = "^1.0.0", path = "packages/signature-verifier-api" } +syn = "2.0.68" +ethers-contract = { version = "2.0.14", default-features = false, features = ["abigen"] } +ethers-core = "2.0.14" +tokio = "1.38.0" +tokio-stream = "0.1.11" +tokio-util = "0.7.11" +tofn = { version = "1.1" } +alloy-primitives = { version = "0.7.6", default-features = false, features = ["std"] } +alloy-sol-types = { version = "0.7.6", default-features = false, features = ["std"] } +strum = { version = "0.25", default-features = false, features = ["derive"] } +interchain-token-service = { version = "^0.1.0", path = "interchain-token-service" } +goldie = { version = "0.5" } +axelarnet-gateway = { version = "^0.1.0", path = "contracts/axelarnet-gateway" } [workspace.lints.clippy] arithmetic_side_effects = "deny" +cast_possible_truncation = "deny" [profile.release] opt-level = 3 diff --git a/README.md b/README.md index 788c1f9d4..75d0ac4db 100644 --- a/README.md +++ b/README.md @@ -35,3 +35,24 @@ The basic rules are as follows: - if no changes are detected in the watched directories, the release will not bump the version. For example, if since last release for the gateway contract no changes were made in the `contracts/gateway` or `packages/` directory. A new release will not bump the version. + +### Compatibility + +For the amplifier preview with version numbers < 1.0.0, please refer to the following compatibility table to select +versions of +contracts and `ampd` that work well together. + +| Binary | Version | +|--------------------------------------------------------------------------------|---------| +| ampd | 0.6.0 | +| coordinator | 0.2.0 | +| gateway | 0.2.3 | +| multisig-prover | 0.6.0 | +| multisig | 0.4.1 | +| nexus-gateway | 0.3.0 | +| rewards | 0.4.0 | +| router | 0.4.0 | +| service-registry | 0.4.1 | +| voting-verifier | 0.5.0 | +| [tofnd](https://github.com/axelarnetwork/tofnd) | 1.0.1 | +| [solidity-contracts](https://github.com/axelarnetwork/axelar-gmp-sdk-solidity) | 5.9.0 | diff --git a/ampd/Cargo.lock b/ampd/Cargo.lock deleted file mode 100644 index cd74ae803..000000000 --- a/ampd/Cargo.lock +++ /dev/null @@ -1,5672 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "Inflector" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" -dependencies = [ - "lazy_static", - "regex", -] - -[[package]] -name = "addr2line" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "aes" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - -[[package]] -name = "aho-corasick" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" -dependencies = [ - "memchr", -] - -[[package]] -name = "ampd" -version = "0.1.0" -dependencies = [ - "async-trait", - "axelar-wasm-std", - "base64 0.21.2", - "clap", - "config", - "router", - "cosmos-sdk-proto 0.16.0", - "cosmrs", - "cosmwasm-std", - "deref-derive", - "ecdsa 0.16.7", - "enum-display-derive", - "error-stack", - "ethers", - "futures", - "hex", - "humantime-serde", - "k256 0.13.1", - "mockall", - "multisig", - "prost", - "rand", - "random-string", - "serde", - "serde_json", - "serde_with", - "tendermint 0.33.0", - "tendermint-rpc", - "thiserror", - "tokio", - "tokio-stream", - "tokio-util", - "toml 0.5.11", - "tonic", - "tonic-build", - "tracing", - "tracing-core", - "tracing-subscriber", - "url", - "valuable", - "voting-verifier", -] - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anstream" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is-terminal", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" - -[[package]] -name = "anstyle-parse" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" -dependencies = [ - "windows-sys 0.48.0", -] - -[[package]] -name = "anstyle-wincon" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" -dependencies = [ - "anstyle", - "windows-sys 0.48.0", -] - -[[package]] -name = "anyhow" -version = "1.0.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" - -[[package]] -name = "arrayvec" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" - -[[package]] -name = "ascii-canvas" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" -dependencies = [ - "term", -] - -[[package]] -name = "async-stream" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" -dependencies = [ - "async-stream-impl", - "futures-core", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "async-trait" -version = "0.1.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.27", -] - -[[package]] -name = "async_io_stream" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" -dependencies = [ - "futures", - "pharos", - "rustc_version", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "auto_impl" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "axelar-wasm-std" -version = "0.1.0" -source = "git+https://github.com/axelarnetwork/axelar-amplifier.git#a314da5d2d40702d5f20ef9ab0e697a3b88f48b3" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cosmwasm-storage", - "cw-storage-plus 1.1.0", - "flagset", - "num-traits", - "schemars", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "axum" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13d8068b6ccb8b34db9de397c7043f91db8b4c66414952c6db944f238c4d3db3" -dependencies = [ - "async-trait", - "axum-core", - "bitflags", - "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.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2f958c80c248b34b9a877a643811be8dbca03ca5ba827f2b63baf3a81e5fc4e" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "mime", - "rustversion", - "tower-layer", - "tower-service", -] - -[[package]] -name = "backtrace" -version = "0.3.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[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 = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" - -[[package]] -name = "base64ct" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" - -[[package]] -name = "bech32" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dabbe35f96fb9507f7330793dc490461b2962659ac5d427181e451a623751d1" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bip32" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e40748d60a3296653e45e87e64c6989aebfad607bccce59cc4156c5d81b2f70" -dependencies = [ - "bs58", - "hmac", - "k256 0.13.1", - "once_cell", - "pbkdf2 0.12.1", - "rand_core 0.6.4", - "ripemd", - "sha2 0.10.6", - "subtle", - "zeroize", -] - -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitvec" -version = "0.17.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41262f11d771fd4a61aa3ce019fca363b4b6c282fca9da2a31186d3965a47a5c" -dependencies = [ - "either", - "radium 0.3.0", -] - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium 0.7.0", - "tap", - "wyz", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-buffer" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bnum" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" - -[[package]] -name = "bs58" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" -dependencies = [ - "sha2 0.9.9", -] - -[[package]] -name = "bumpalo" -version = "3.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" - -[[package]] -name = "byte-slice-cast" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" -dependencies = [ - "serde", -] - -[[package]] -name = "bzip2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" -dependencies = [ - "bzip2-sys", - "libc", -] - -[[package]] -name = "bzip2-sys" -version = "0.1.11+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "camino" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo-platform" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cfa25e60aea747ec7e1124f238816749faa93759c6ff5b31f1ccdda137f4479" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.15.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" -dependencies = [ - "camino", - "cargo-platform", - "semver", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" -dependencies = [ - "jobserver", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "num-traits", - "serde", - "winapi", -] - -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - -[[package]] -name = "clap" -version = "4.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938" -dependencies = [ - "clap_builder", - "clap_derive", - "once_cell", -] - -[[package]] -name = "clap_builder" -version = "4.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd" -dependencies = [ - "anstream", - "anstyle", - "bitflags", - "clap_lex", - "once_cell", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.27", -] - -[[package]] -name = "clap_lex" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" - -[[package]] -name = "coins-bip32" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30a84aab436fcb256a2ab3c80663d8aec686e6bae12827bb05fef3e1e439c9f" -dependencies = [ - "bincode", - "bs58", - "coins-core", - "digest 0.10.6", - "getrandom", - "hmac", - "k256 0.13.1", - "lazy_static", - "serde", - "sha2 0.10.6", - "thiserror", -] - -[[package]] -name = "coins-bip39" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f4d04ee18e58356accd644896aeb2094ddeafb6a713e056cef0c0a8e468c15" -dependencies = [ - "bitvec 0.17.4", - "coins-bip32", - "getrandom", - "hmac", - "once_cell", - "pbkdf2 0.12.1", - "rand", - "sha2 0.10.6", - "thiserror", -] - -[[package]] -name = "coins-core" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b949a1c63fb7eb591eb7ba438746326aedf0ae843e51ec92ba6bec5bb382c4f" -dependencies = [ - "base64 0.21.2", - "bech32", - "bs58", - "digest 0.10.6", - "generic-array", - "hex", - "ripemd", - "serde", - "serde_derive", - "sha2 0.10.6", - "sha3", - "thiserror", -] - -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - -[[package]] -name = "config" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d379af7f68bfc21714c6c7dea883544201741d2ce8274bb12fa54f89507f52a7" -dependencies = [ - "async-trait", - "json5", - "lazy_static", - "nom", - "pathdiff", - "ron", - "rust-ini", - "serde", - "serde_json", - "toml 0.5.11", - "yaml-rust", -] - -[[package]] -name = "router" -version = "0.1.0" -source = "git+https://github.com/axelarnetwork/axelar-amplifier.git#a314da5d2d40702d5f20ef9ab0e697a3b88f48b3" -dependencies = [ - "axelar-wasm-std", - "cosmwasm-schema", - "cosmwasm-std", - "cosmwasm-storage", - "cw-storage-plus 1.1.0", - "cw2", - "flagset", - "schemars", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "const-oid" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" - -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "cosmos-sdk-proto" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4776e787b24d9568dd61d3237eeb4eb321d622fb881b858c7b82806420e87d4" -dependencies = [ - "prost", - "prost-types", - "tendermint-proto 0.27.0", - "tonic", -] - -[[package]] -name = "cosmos-sdk-proto" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73c9d2043a9e617b0d602fbc0a0ecd621568edbf3a9774890a6d562389bd8e1c" -dependencies = [ - "prost", - "prost-types", - "tendermint-proto 0.32.0", -] - -[[package]] -name = "cosmrs" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af13955d6f356272e6def9ff5e2450a7650df536d8934f47052a20c76513d2f6" -dependencies = [ - "bip32", - "cosmos-sdk-proto 0.19.0", - "ecdsa 0.16.7", - "eyre", - "getrandom", - "k256 0.13.1", - "rand_core 0.6.4", - "serde", - "serde_json", - "subtle-encoding", - "tendermint 0.32.0", - "thiserror", -] - -[[package]] -name = "cosmwasm-crypto" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d076a08ec01ed23c4396aca98ec73a38fa1fee5f310465add52b4108181c7a8" -dependencies = [ - "digest 0.10.6", - "ed25519-zebra", - "k256 0.11.6", - "rand_core 0.6.4", - "thiserror", -] - -[[package]] -name = "cosmwasm-derive" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec361f3c09d7b41221948fc17be9b3c96cb58e55a02f82da36f888a651f2584" -dependencies = [ - "syn 1.0.107", -] - -[[package]] -name = "cosmwasm-schema" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb6b2fb76758ef59cddc77f2e2ae91c22f77da49037e9f182e9c2833f0e959b1" -dependencies = [ - "cosmwasm-schema-derive", - "schemars", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "cosmwasm-schema-derive" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bfa39422f0d9f1c9a6fd3711573258495314dfa3aae738ea825ecd9964bc659" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "cosmwasm-std" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f6dc2ee23313add5ecacc3ccac217b9967ad9d2d11bd56e5da6aa65a9da6138" -dependencies = [ - "base64 0.13.1", - "bnum", - "cosmwasm-crypto", - "cosmwasm-derive", - "derivative", - "forward_ref", - "hex", - "schemars", - "serde", - "serde-json-wasm", - "sha2 0.10.6", - "thiserror", -] - -[[package]] -name = "cosmwasm-storage" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18b4c99c6479e554ef1516950f1fa3d88bd7d0a8fc2367321c07ca0a33997dfc" -dependencies = [ - "cosmwasm-std", - "serde", -] - -[[package]] -name = "cpufeatures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-bigint" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-bigint" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "ct-logs" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1a816186fa68d9e426e3cb4ae4dff1fcd8e4a2c34b781bf7a822574a0d0aac8" -dependencies = [ - "sct 0.6.1", -] - -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] - -[[package]] -name = "curve25519-dalek" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" -dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "subtle", - "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 = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6cf70ef7686e2da9ad7b067c5942cd3e88dd9453f7af42f54557f8af300fb0" -dependencies = [ - "cosmwasm-std", - "schemars", - "serde", -] - -[[package]] -name = "cw-storage-plus" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f0e92a069d62067f3472c62e30adedb4cab1754725c0f2a682b3128d2bf3c79" -dependencies = [ - "cosmwasm-std", - "schemars", - "serde", -] - -[[package]] -name = "cw0" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae676b6cced78a3d38ad4b01ab4ed66fc78ac191c3c0d6bfd5372cb2efd473b" -dependencies = [ - "cosmwasm-std", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "cw2" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5abb8ecea72e09afff830252963cb60faf945ce6cef2c20a43814516082653da" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.15.1", - "schemars", - "serde", -] - -[[package]] -name = "darling" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.27", -] - -[[package]] -name = "darling_macro" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" -dependencies = [ - "darling_core", - "quote", - "syn 2.0.27", -] - -[[package]] -name = "data-encoding" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" - -[[package]] -name = "der" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "der" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05e58dffcdcc8ee7b22f0c1f71a69243d7c2d9ad87b5a14361f2424a1565c219" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "deref-derive" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4255bb7dd538590188bd0aea52e48bd699b19bd90b0d069ec2ced8461fe23273" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "diff" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" - -[[package]] -name = "difflib" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "digest" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" -dependencies = [ - "block-buffer 0.10.3", - "const-oid", - "crypto-common", - "subtle", -] - -[[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-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dlv-list" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" - -[[package]] -name = "downcast" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" - -[[package]] -name = "dunce" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" - -[[package]] -name = "dyn-clone" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9b0705efd4599c15a38151f4721f7bc388306f61084d3bfd50bd07fbca5cb60" - -[[package]] -name = "ecdsa" -version = "0.14.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" -dependencies = [ - "der 0.6.1", - "elliptic-curve 0.12.3", - "rfc6979 0.3.1", - "signature 1.6.4", -] - -[[package]] -name = "ecdsa" -version = "0.16.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0997c976637b606099b9985693efa3581e84e41f5c11ba5255f88711058ad428" -dependencies = [ - "der 0.7.5", - "digest 0.10.6", - "elliptic-curve 0.13.4", - "rfc6979 0.4.0", - "signature 2.1.0", - "spki 0.7.2", -] - -[[package]] -name = "ed25519" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb04eee5d9d907f29e80ee6b0e78f7e2c82342c63e3580d8c4f69d9d5aad963" -dependencies = [ - "pkcs8 0.10.2", - "signature 2.1.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", - "rand_core 0.6.4", - "sha2 0.9.9", - "zeroize", -] - -[[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", - "rand_core 0.6.4", - "serde", - "sha2 0.9.9", - "zeroize", -] - -[[package]] -name = "either" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" - -[[package]] -name = "elliptic-curve" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" -dependencies = [ - "base16ct 0.1.1", - "crypto-bigint 0.4.9", - "der 0.6.1", - "digest 0.10.6", - "ff 0.12.1", - "generic-array", - "group 0.12.1", - "pkcs8 0.9.0", - "rand_core 0.6.4", - "sec1 0.3.0", - "subtle", - "zeroize", -] - -[[package]] -name = "elliptic-curve" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75c71eaa367f2e5d556414a8eea812bc62985c879748d6403edabd9cb03f16e7" -dependencies = [ - "base16ct 0.2.0", - "crypto-bigint 0.5.2", - "digest 0.10.6", - "ff 0.13.0", - "generic-array", - "group 0.13.0", - "pkcs8 0.10.2", - "rand_core 0.6.4", - "sec1 0.7.1", - "subtle", - "zeroize", -] - -[[package]] -name = "ena" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" -dependencies = [ - "log", -] - -[[package]] -name = "encoding_rs" -version = "0.8.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "enr" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf56acd72bb22d2824e66ae8e9e5ada4d0de17a69c7fd35569dde2ada8ec9116" -dependencies = [ - "base64 0.13.1", - "bytes", - "hex", - "k256 0.13.1", - "log", - "rand", - "rlp", - "serde", - "sha3", - "zeroize", -] - -[[package]] -name = "enum-display-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16ef37b2a9b242295d61a154ee91ae884afff6b8b933b486b12481cc58310ca" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "error-stack" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d224e04b2d93d974c08e375dac9b8d1a513846e44c6666450a57b1ed963f9" -dependencies = [ - "anyhow", - "eyre", - "owo-colors", - "rustc_version", -] - -[[package]] -name = "eth-keystore" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" -dependencies = [ - "aes", - "ctr", - "digest 0.10.6", - "hex", - "hmac", - "pbkdf2 0.11.0", - "rand", - "scrypt", - "serde", - "serde_json", - "sha2 0.10.6", - "sha3", - "thiserror", - "uuid", -] - -[[package]] -name = "ethabi" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" -dependencies = [ - "ethereum-types", - "hex", - "once_cell", - "regex", - "serde", - "serde_json", - "sha3", - "thiserror", - "uint", -] - -[[package]] -name = "ethbloom" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" -dependencies = [ - "crunchy", - "fixed-hash", - "impl-codec", - "impl-rlp", - "impl-serde", - "scale-info", - "tiny-keccak", -] - -[[package]] -name = "ethereum-types" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" -dependencies = [ - "ethbloom", - "fixed-hash", - "impl-codec", - "impl-rlp", - "impl-serde", - "primitive-types", - "scale-info", - "uint", -] - -[[package]] -name = "ethers" -version = "2.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b4026b97da8281276744741fac7eb385da905f6093c583331fa2953fdd4253" -dependencies = [ - "ethers-addressbook", - "ethers-contract", - "ethers-core", - "ethers-etherscan", - "ethers-middleware", - "ethers-providers", - "ethers-signers", - "ethers-solc", -] - -[[package]] -name = "ethers-addressbook" -version = "2.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edcb6ffefc230d8c42874c51b28dc11dbb8de50b27a8fdf92648439d6baa68dc" -dependencies = [ - "ethers-core", - "once_cell", - "serde", - "serde_json", -] - -[[package]] -name = "ethers-contract" -version = "2.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4719a44c3d37ab07c6dea99ab174068d8c35e441b60b6c20ce4e48357273e8" -dependencies = [ - "ethers-contract-abigen", - "ethers-contract-derive", - "ethers-core", - "ethers-providers", - "ethers-signers", - "futures-util", - "hex", - "once_cell", - "pin-project", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "ethers-contract-abigen" -version = "2.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "155ea1b84d169d231317ed86e307af6f2bed6b40dd17e5e94bc84da21cadb21c" -dependencies = [ - "Inflector", - "dunce", - "ethers-core", - "ethers-etherscan", - "eyre", - "hex", - "prettyplease 0.2.12", - "proc-macro2", - "quote", - "regex", - "reqwest", - "serde", - "serde_json", - "syn 2.0.27", - "toml 0.7.6", - "walkdir", -] - -[[package]] -name = "ethers-contract-derive" -version = "2.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8567ff196c4a37c1a8c90ec73bda0ad2062e191e4f0a6dc4d943e2ec4830fc88" -dependencies = [ - "Inflector", - "ethers-contract-abigen", - "ethers-core", - "hex", - "proc-macro2", - "quote", - "serde_json", - "syn 2.0.27", -] - -[[package]] -name = "ethers-core" -version = "2.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60ca2514feb98918a0a31de7e1983c29f2267ebf61b2dc5d4294f91e5b866623" -dependencies = [ - "arrayvec", - "bytes", - "cargo_metadata", - "chrono", - "elliptic-curve 0.13.4", - "ethabi", - "generic-array", - "hex", - "k256 0.13.1", - "num_enum", - "once_cell", - "open-fastrlp", - "rand", - "rlp", - "serde", - "serde_json", - "strum", - "syn 2.0.27", - "tempfile", - "thiserror", - "tiny-keccak", - "unicode-xid", -] - -[[package]] -name = "ethers-etherscan" -version = "2.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22b3a8269d3df0ed6364bc05b4735b95f4bf830ce3aef87d5e760fb0e93e5b91" -dependencies = [ - "ethers-core", - "reqwest", - "semver", - "serde", - "serde_json", - "thiserror", - "tracing", -] - -[[package]] -name = "ethers-middleware" -version = "2.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c339aad74ae5c451d27e0e49c7a3c7d22620b119b4f9291d7aa21f72d7f366" -dependencies = [ - "async-trait", - "auto_impl", - "ethers-contract", - "ethers-core", - "ethers-etherscan", - "ethers-providers", - "ethers-signers", - "futures-channel", - "futures-locks", - "futures-util", - "instant", - "reqwest", - "serde", - "serde_json", - "thiserror", - "tokio", - "tracing", - "tracing-futures", - "url", -] - -[[package]] -name = "ethers-providers" -version = "2.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b411b119f1cf0efb69e2190883dee731251882bb21270f893ee9513b3a697c48" -dependencies = [ - "async-trait", - "auto_impl", - "base64 0.21.2", - "bytes", - "enr", - "ethers-core", - "futures-core", - "futures-timer", - "futures-util", - "hashers", - "hex", - "http", - "instant", - "once_cell", - "pin-project", - "reqwest", - "serde", - "serde_json", - "thiserror", - "tokio", - "tokio-tungstenite", - "tracing", - "tracing-futures", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "ws_stream_wasm", -] - -[[package]] -name = "ethers-signers" -version = "2.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4864d387456a9c09a1157fa10e1528b29d90f1d859443acf06a1b23365fb518c" -dependencies = [ - "async-trait", - "coins-bip32", - "coins-bip39", - "elliptic-curve 0.13.4", - "eth-keystore", - "ethers-core", - "hex", - "rand", - "sha2 0.10.6", - "thiserror", - "tracing", -] - -[[package]] -name = "ethers-solc" -version = "2.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6c2b9625a2c639d46625f88acc2092a3cb35786c37f7c2128b3ca20f639b3c" -dependencies = [ - "cfg-if", - "dunce", - "ethers-core", - "glob", - "hex", - "home", - "md-5", - "num_cpus", - "once_cell", - "path-slash", - "rayon", - "regex", - "semver", - "serde", - "serde_json", - "solang-parser", - "svm-rs", - "thiserror", - "tiny-keccak", - "tokio", - "tracing", - "walkdir", - "yansi", -] - -[[package]] -name = "eyre" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" -dependencies = [ - "indenter", - "once_cell", -] - -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - -[[package]] -name = "ff" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" -dependencies = [ - "rand_core 0.6.4", - "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 = "fixed-hash" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" -dependencies = [ - "byteorder", - "rand", - "rustc-hex", - "static_assertions", -] - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "flagset" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda653ca797810c02f7ca4b804b40b8b95ae046eb989d356bce17919a8c25499" -dependencies = [ - "serde", -] - -[[package]] -name = "flate2" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "flex-error" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c606d892c9de11507fa0dcffc116434f94e105d0bbdc4e405b61519464c49d7b" -dependencies = [ - "eyre", - "paste", -] - -[[package]] -name = "float-cmp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" -dependencies = [ - "num-traits", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "form_urlencoded" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" -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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" - -[[package]] -name = "fs2" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" - -[[package]] -name = "futures-executor" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-locks" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45ec6fe3675af967e67c5536c0b9d44e34e6c52f86bedc4ea49c5317b8e94d06" -dependencies = [ - "futures-channel", - "futures-task", -] - -[[package]] -name = "futures-macro" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.27", -] - -[[package]] -name = "futures-sink" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" - -[[package]] -name = "futures-task" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" - -[[package]] -name = "futures-timer" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" -dependencies = [ - "gloo-timers", - "send_wrapper 0.4.0", -] - -[[package]] -name = "futures-util" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", - "zeroize", -] - -[[package]] -name = "getrandom" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "gimli" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "gloo-timers" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "group" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" -dependencies = [ - "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.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap 1.9.2", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashbrown" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" - -[[package]] -name = "hashers" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2bca93b15ea5a746f220e56587f71e73c6165eab783df9e26590069953e3c30" -dependencies = [ - "fxhash", -] - -[[package]] -name = "headers" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" -dependencies = [ - "base64 0.13.1", - "bitflags", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha1", -] - -[[package]] -name = "headers-core" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" -dependencies = [ - "http", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -dependencies = [ - "serde", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest 0.10.6", -] - -[[package]] -name = "home" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" -dependencies = [ - "windows-sys 0.48.0", -] - -[[package]] -name = "http" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "humantime-serde" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" -dependencies = [ - "humantime", - "serde", -] - -[[package]] -name = "hyper" -version = "0.14.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "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", - "tokio", - "tokio-rustls 0.22.0", - "tower-service", - "webpki 0.21.4", -] - -[[package]] -name = "hyper-rustls" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64" -dependencies = [ - "ct-logs", - "futures-util", - "hyper", - "log", - "rustls 0.19.1", - "rustls-native-certs", - "tokio", - "tokio-rustls 0.22.0", - "webpki 0.21.4", - "webpki-roots 0.21.1", -] - -[[package]] -name = "hyper-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" -dependencies = [ - "futures-util", - "http", - "hyper", - "rustls 0.21.5", - "tokio", - "tokio-rustls 0.24.1", -] - -[[package]] -name = "hyper-timeout" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" -dependencies = [ - "hyper", - "pin-project-lite", - "tokio", - "tokio-io-timeout", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "impl-codec" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" -dependencies = [ - "parity-scale-codec", -] - -[[package]] -name = "impl-rlp" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" -dependencies = [ - "rlp", -] - -[[package]] -name = "impl-serde" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" -dependencies = [ - "serde", -] - -[[package]] -name = "impl-trait-for-tuples" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "indenter" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" - -[[package]] -name = "indexmap" -version = "1.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "indexmap" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" -dependencies = [ - "equivalent", - "hashbrown 0.14.0", - "serde", -] - -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "generic-array", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" -dependencies = [ - "libc", - "windows-sys 0.42.0", -] - -[[package]] -name = "ipnet" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" - -[[package]] -name = "is-terminal" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" -dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix", - "windows-sys 0.48.0", -] - -[[package]] -name = "is_ci" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb" - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" - -[[package]] -name = "jobserver" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" -dependencies = [ - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "json5" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" -dependencies = [ - "pest", - "pest_derive", - "serde", -] - -[[package]] -name = "k256" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" -dependencies = [ - "cfg-if", - "ecdsa 0.14.8", - "elliptic-curve 0.12.3", - "sha2 0.10.6", -] - -[[package]] -name = "k256" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" -dependencies = [ - "cfg-if", - "ecdsa 0.16.7", - "elliptic-curve 0.13.4", - "once_cell", - "sha2 0.10.6", - "signature 2.1.0", -] - -[[package]] -name = "keccak" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "lalrpop" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da4081d44f4611b66c6dd725e6de3169f9f63905421e8626fcb86b6a898998b8" -dependencies = [ - "ascii-canvas", - "bit-set", - "diff", - "ena", - "is-terminal", - "itertools", - "lalrpop-util", - "petgraph", - "regex", - "regex-syntax", - "string_cache", - "term", - "tiny-keccak", - "unicode-xid", -] - -[[package]] -name = "lalrpop-util" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f35c735096c0293d313e8f2a641627472b83d01b937177fe76e5e2708d31e0d" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" - -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - -[[package]] -name = "linux-raw-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" - -[[package]] -name = "lock_api" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "matchit" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" - -[[package]] -name = "md-5" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" -dependencies = [ - "digest 0.10.6", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", -] - -[[package]] -name = "mio" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.48.0", -] - -[[package]] -name = "mockall" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e4a1c770583dac7ab5e2f6c139153b783a53a1bbee9729613f193e59828326" -dependencies = [ - "cfg-if", - "downcast", - "fragile", - "lazy_static", - "mockall_derive", - "predicates", - "predicates-tree", -] - -[[package]] -name = "mockall_derive" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "832663583d5fa284ca8810bf7015e46c9fff9622d3cf34bd1eea5003fec06dd0" -dependencies = [ - "cfg-if", - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "multimap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" - -[[package]] -name = "multisig" -version = "0.1.0" -source = "git+https://github.com/axelarnetwork/axelar-amplifier.git#a314da5d2d40702d5f20ef9ab0e697a3b88f48b3" -dependencies = [ - "axelar-wasm-std", - "cosmwasm-crypto", - "cosmwasm-schema", - "cosmwasm-std", - "cosmwasm-storage", - "cw-storage-plus 1.1.0", - "cw2", - "getrandom", - "schemars", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "new_debug_unreachable" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "nom8" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" -dependencies = [ - "memchr", -] - -[[package]] -name = "normalize-line-endings" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num-derive" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" -dependencies = [ - "hermit-abi 0.2.6", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.27", -] - -[[package]] -name = "object" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "open-fastrlp" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce" -dependencies = [ - "arrayvec", - "auto_impl", - "bytes", - "ethereum-types", - "open-fastrlp-derive", -] - -[[package]] -name = "open-fastrlp-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" -dependencies = [ - "bytes", - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "ordered-multimap" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" -dependencies = [ - "dlv-list", - "hashbrown 0.12.3", -] - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "owo-colors" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" -dependencies = [ - "supports-color", -] - -[[package]] -name = "parity-scale-codec" -version = "3.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8e946cc0cc711189c0b0249fb8b599cbeeab9784d83c415719368bb8d4ac64" -dependencies = [ - "arrayvec", - "bitvec 1.0.1", - "byte-slice-cast", - "impl-trait-for-tuples", - "parity-scale-codec-derive", - "serde", -] - -[[package]] -name = "parity-scale-codec-derive" -version = "3.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a296c3079b5fefbc499e1de58dc26c09b1b9a5952d26694ee89f04a43ebbb3e" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "windows-sys 0.42.0", -] - -[[package]] -name = "password-hash" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" -dependencies = [ - "base64ct", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "paste" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" - -[[package]] -name = "path-slash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" - -[[package]] -name = "pathdiff" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" - -[[package]] -name = "pbkdf2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" -dependencies = [ - "digest 0.10.6", - "hmac", - "password-hash", - "sha2 0.10.6", -] - -[[package]] -name = "pbkdf2" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0ca0b5a68607598bf3bad68f32227a8164f6254833f84eafaac409cd6746c31" -dependencies = [ - "digest 0.10.6", - "hmac", -] - -[[package]] -name = "peg" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07c0b841ea54f523f7aa556956fbd293bcbe06f2e67d2eb732b7278aaf1d166a" -dependencies = [ - "peg-macros", - "peg-runtime", -] - -[[package]] -name = "peg-macros" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aa52829b8decbef693af90202711348ab001456803ba2a98eb4ec8fb70844c" -dependencies = [ - "peg-runtime", - "proc-macro2", - "quote", -] - -[[package]] -name = "peg-runtime" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c719dcf55f09a3a7e764c6649ab594c18a177e3599c467983cdf644bfc0a4088" - -[[package]] -name = "percent-encoding" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" - -[[package]] -name = "pest" -version = "2.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ab62d2fa33726dbe6321cc97ef96d8cde531e3eeaf858a058de53a8a6d40d8f" -dependencies = [ - "thiserror", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf026e2d0581559db66d837fe5242320f525d85c76283c61f4d51a1238d65ea" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b27bd18aa01d91c8ed2b61ea23406a676b42d82609c6e2581fba42f0c15f17f" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "pest_meta" -version = "2.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f02b677c1859756359fc9983c2e56a0237f18624a3789528804406b7e915e5d" -dependencies = [ - "once_cell", - "pest", - "sha2 0.10.6", -] - -[[package]] -name = "petgraph" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" -dependencies = [ - "fixedbitset", - "indexmap 1.9.2", -] - -[[package]] -name = "pharos" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" -dependencies = [ - "futures", - "rustc_version", -] - -[[package]] -name = "phf" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" -dependencies = [ - "phf_macros", - "phf_shared 0.11.2", -] - -[[package]] -name = "phf_generator" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" -dependencies = [ - "phf_shared 0.11.2", - "rand", -] - -[[package]] -name = "phf_macros" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" -dependencies = [ - "phf_generator", - "phf_shared 0.11.2", - "proc-macro2", - "quote", - "syn 2.0.27", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - -[[package]] -name = "phf_shared" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.27", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs8" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" -dependencies = [ - "der 0.6.1", - "spki 0.6.0", -] - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der 0.7.5", - "spki 0.7.2", -] - -[[package]] -name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - -[[package]] -name = "predicates" -version = "2.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" -dependencies = [ - "difflib", - "float-cmp", - "itertools", - "normalize-line-endings", - "predicates-core", - "regex", -] - -[[package]] -name = "predicates-core" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f883590242d3c6fc5bf50299011695fa6590c2c70eac95ee1bdb9a733ad1a2" - -[[package]] -name = "predicates-tree" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54ff541861505aabf6ea722d2131ee980b8276e10a1297b94e896dd8b621850d" -dependencies = [ - "predicates-core", - "termtree", -] - -[[package]] -name = "prettyplease" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" -dependencies = [ - "proc-macro2", - "syn 1.0.107", -] - -[[package]] -name = "prettyplease" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" -dependencies = [ - "proc-macro2", - "syn 2.0.27", -] - -[[package]] -name = "primitive-types" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" -dependencies = [ - "fixed-hash", - "impl-codec", - "impl-rlp", - "impl-serde", - "scale-info", - "uint", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34" -dependencies = [ - "once_cell", - "toml_edit 0.18.1", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.107", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" -dependencies = [ - "unicode-ident", -] - -[[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-build" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" -dependencies = [ - "bytes", - "heck", - "itertools", - "lazy_static", - "log", - "multimap", - "petgraph", - "prettyplease 0.1.25", - "prost", - "prost-types", - "regex", - "syn 1.0.107", - "tempfile", - "which", -] - -[[package]] -name = "prost-derive" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" -dependencies = [ - "anyhow", - "itertools", - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "prost-types" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" -dependencies = [ - "prost", -] - -[[package]] -name = "quote" -version = "1.0.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "random-string" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4e63111ec5292d8af9c220f06fe3bb87991cc78b6f1f7e291d1ae6b8a60817" -dependencies = [ - "fastrand", -] - -[[package]] -name = "rayon" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_users" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" -dependencies = [ - "getrandom", - "redox_syscall 0.2.16", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" - -[[package]] -name = "reqwest" -version = "0.11.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" -dependencies = [ - "base64 0.21.2", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-rustls 0.24.1", - "ipnet", - "js-sys", - "log", - "mime", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls 0.21.5", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-rustls 0.24.1", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots 0.22.6", - "winreg", -] - -[[package]] -name = "rfc6979" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" -dependencies = [ - "crypto-bigint 0.4.9", - "hmac", - "zeroize", -] - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi", -] - -[[package]] -name = "ripemd" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" -dependencies = [ - "digest 0.10.6", -] - -[[package]] -name = "rlp" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" -dependencies = [ - "bytes", - "rlp-derive", - "rustc-hex", -] - -[[package]] -name = "rlp-derive" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "ron" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a" -dependencies = [ - "base64 0.13.1", - "bitflags", - "serde", -] - -[[package]] -name = "rust-ini" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" -dependencies = [ - "cfg-if", - "ordered-multimap", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustc-hex" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b24138615de35e32031d041a09032ef3487a616d901ca4db224e7d557efae2" -dependencies = [ - "bitflags", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys 0.45.0", -] - -[[package]] -name = "rustls" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" -dependencies = [ - "base64 0.13.1", - "log", - "ring", - "sct 0.6.1", - "webpki 0.21.4", -] - -[[package]] -name = "rustls" -version = "0.21.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79ea77c539259495ce8ca47f53e66ae0330a8819f67e23ac96ca02f50e7b7d36" -dependencies = [ - "log", - "ring", - "rustls-webpki 0.101.1", - "sct 0.7.0", -] - -[[package]] -name = "rustls-native-certs" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a07b7c1885bd8ed3831c289b7870b13ef46fe0e856d288c30d9cc17d75a2092" -dependencies = [ - "openssl-probe", - "rustls 0.19.1", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" -dependencies = [ - "base64 0.21.2", -] - -[[package]] -name = "rustls-webpki" -version = "0.100.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "rustls-webpki" -version = "0.101.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f36a6828982f422756984e47912a7a51dcbc2a197aa791158f8ca61cd8204e" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" - -[[package]] -name = "ryu" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" - -[[package]] -name = "salsa20" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" -dependencies = [ - "cipher", -] - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "scale-info" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0a159d0c45c12b20c5a844feb1fe4bea86e28f17b92a5f0c42193634d3782" -dependencies = [ - "cfg-if", - "derive_more", - "parity-scale-codec", - "scale-info-derive", -] - -[[package]] -name = "scale-info-derive" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "schannel" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" -dependencies = [ - "windows-sys 0.42.0", -] - -[[package]] -name = "schemars" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307" -dependencies = [ - "dyn-clone", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn 1.0.107", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "scrypt" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f9e24d2b632954ded8ab2ef9fea0a0c769ea56ea98bddbafbad22caeeadf45d" -dependencies = [ - "hmac", - "pbkdf2 0.11.0", - "salsa20", - "sha2 0.10.6", -] - -[[package]] -name = "sct" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "sct" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "sec1" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" -dependencies = [ - "base16ct 0.1.1", - "der 0.6.1", - "generic-array", - "pkcs8 0.9.0", - "subtle", - "zeroize", -] - -[[package]] -name = "sec1" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48518a2b5775ba8ca5b46596aae011caa431e6ce7e4a67ead66d92f08884220e" -dependencies = [ - "base16ct 0.2.0", - "der 0.7.5", - "generic-array", - "pkcs8 0.10.2", - "subtle", - "zeroize", -] - -[[package]] -name = "security-framework" -version = "2.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" -dependencies = [ - "serde", -] - -[[package]] -name = "send_wrapper" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" - -[[package]] -name = "send_wrapper" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" - -[[package]] -name = "serde" -version = "1.0.174" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b88756493a5bd5e5395d53baa70b194b05764ab85b59e43e4b8f4e1192fa9b1" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-json-wasm" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15bee9b04dd165c3f4e142628982ddde884c2022a89e8ddf99c4829bf2c3a58" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_bytes" -version = "0.11.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718dc5fff5b36f99093fc49b280cfc96ce6fc824317783bff5a1fed0c7a64819" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.174" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e5c3a298c7f978e53536f95a63bdc4c4a64550582f31a0359a9afda6aede62e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.27", -] - -[[package]] -name = "serde_derive_internals" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "serde_json" -version = "1.0.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_repr" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a5ec9fa74a20ebbe5d9ac23dac1fc96ba0ecfe9f50f2843b52e537b10fbcb4e" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "serde_spanned" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_with" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1402f54f9a3b9e2efe71c1cea24e648acce55887983553eeb858cf3115acfd49" -dependencies = [ - "base64 0.21.2", - "chrono", - "hex", - "indexmap 1.9.2", - "indexmap 2.0.0", - "serde", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9197f1ad0e3c173a0222d3c4404fb04c3afe87e962bcb327af73e8301fa203c7" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.27", -] - -[[package]] -name = "service-registry" -version = "0.1.0" -source = "git+https://github.com/axelarnetwork/axelar-amplifier.git#a314da5d2d40702d5f20ef9ab0e697a3b88f48b3" -dependencies = [ - "axelar-wasm-std", - "cosmwasm-schema", - "cosmwasm-std", - "cosmwasm-storage", - "cw-storage-plus 0.15.1", - "cw2", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "sha1" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.6", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - -[[package]] -name = "sha2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.6", -] - -[[package]] -name = "sha3" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c2bb1a323307527314a36bfb73f24febb08ce2b8a554bf4ffd6f51ad15198c" -dependencies = [ - "digest 0.10.6", - "keccak", -] - -[[package]] -name = "sharded-slab" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - -[[package]] -name = "signature" -version = "1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" -dependencies = [ - "digest 0.10.6", - "rand_core 0.6.4", -] - -[[package]] -name = "signature" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" -dependencies = [ - "digest 0.10.6", - "rand_core 0.6.4", -] - -[[package]] -name = "siphasher" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" - -[[package]] -name = "slab" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - -[[package]] -name = "socket2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "solang-parser" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c792fe9fae2a2f716846f214ca10d5a1e21133e0bf36cef34bcc4a852467b21" -dependencies = [ - "itertools", - "lalrpop", - "lalrpop-util", - "phf", - "thiserror", - "unicode-xid", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "spki" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" -dependencies = [ - "base64ct", - "der 0.6.1", -] - -[[package]] -name = "spki" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" -dependencies = [ - "base64ct", - "der 0.7.5", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot", - "phf_shared 0.10.0", - "precomputed-hash", -] - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "strum" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6069ca09d878a33f883cc06aaa9718ede171841d3832450354410b718b097232" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.27", -] - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[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 = "supports-color" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ba6faf2ca7ee42fdd458f4347ae0a9bd6bcc445ad7cb57ad82b383f18870d6f" -dependencies = [ - "atty", - "is_ci", -] - -[[package]] -name = "svm-rs" -version = "0.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a04fc4f5cd35c700153b233f5575ccb3237e0f941fa5049d9e98254d10bf2fe" -dependencies = [ - "fs2", - "hex", - "home", - "once_cell", - "reqwest", - "semver", - "serde", - "serde_json", - "sha2 0.10.6", - "thiserror", - "url", - "zip", -] - -[[package]] -name = "syn" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8" - -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", - "unicode-xid", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tempfile" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" -dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall 0.3.5", - "rustix", - "windows-sys 0.45.0", -] - -[[package]] -name = "tendermint" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a46ec6b25b028097ab682ffae11d09d64fe1e2535833b902f26a278a0f88a705" -dependencies = [ - "bytes", - "digest 0.10.6", - "ed25519", - "ed25519-consensus", - "flex-error", - "futures", - "k256 0.13.1", - "num-traits", - "once_cell", - "prost", - "prost-types", - "ripemd", - "serde", - "serde_bytes", - "serde_json", - "serde_repr", - "sha2 0.10.6", - "signature 2.1.0", - "subtle", - "subtle-encoding", - "tendermint-proto 0.32.0", - "time", - "zeroize", -] - -[[package]] -name = "tendermint" -version = "0.33.0" -source = "git+https://github.com/axelarnetwork/tendermint-rs.git?branch=v0.33.x#e97033e20e660a7e707ea86db174ec047bbba50d" -dependencies = [ - "bytes", - "digest 0.10.6", - "ed25519", - "ed25519-consensus", - "flex-error", - "futures", - "num-traits", - "once_cell", - "prost", - "prost-types", - "serde", - "serde_bytes", - "serde_json", - "serde_repr", - "sha2 0.10.6", - "signature 2.1.0", - "subtle", - "subtle-encoding", - "tendermint-proto 0.33.0", - "time", - "zeroize", -] - -[[package]] -name = "tendermint-config" -version = "0.33.0" -source = "git+https://github.com/axelarnetwork/tendermint-rs.git?branch=v0.33.x#e97033e20e660a7e707ea86db174ec047bbba50d" -dependencies = [ - "flex-error", - "serde", - "serde_json", - "tendermint 0.33.0", - "toml 0.5.11", - "url", -] - -[[package]] -name = "tendermint-proto" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5895470f28c530f8ae8c4071bf8190304ce00bd131d25e81730453124a3375c" -dependencies = [ - "bytes", - "flex-error", - "num-derive", - "num-traits", - "prost", - "prost-types", - "serde", - "serde_bytes", - "subtle-encoding", - "time", -] - -[[package]] -name = "tendermint-proto" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23c8ff0e6634eb4c3c4aeed45076dc97dac91aac5501a905a67fa222e165b" -dependencies = [ - "bytes", - "flex-error", - "num-derive", - "num-traits", - "prost", - "prost-types", - "serde", - "serde_bytes", - "subtle-encoding", - "time", -] - -[[package]] -name = "tendermint-proto" -version = "0.33.0" -source = "git+https://github.com/axelarnetwork/tendermint-rs.git?branch=v0.33.x#e97033e20e660a7e707ea86db174ec047bbba50d" -dependencies = [ - "bytes", - "flex-error", - "num-derive", - "num-traits", - "prost", - "prost-types", - "serde", - "serde_bytes", - "subtle-encoding", - "time", -] - -[[package]] -name = "tendermint-rpc" -version = "0.33.0" -source = "git+https://github.com/axelarnetwork/tendermint-rs.git?branch=v0.33.x#e97033e20e660a7e707ea86db174ec047bbba50d" -dependencies = [ - "async-trait", - "bytes", - "flex-error", - "futures", - "getrandom", - "http", - "hyper", - "hyper-proxy", - "hyper-rustls 0.22.1", - "peg", - "pin-project", - "semver", - "serde", - "serde_bytes", - "serde_json", - "subtle", - "subtle-encoding", - "tendermint 0.33.0", - "tendermint-config", - "tendermint-proto 0.33.0", - "thiserror", - "time", - "tokio", - "tracing", - "url", - "uuid", - "walkdir", -] - -[[package]] -name = "term" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" -dependencies = [ - "dirs-next", - "rustversion", - "winapi", -] - -[[package]] -name = "termtree" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8" - -[[package]] -name = "thiserror" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "thread_local" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" -dependencies = [ - "once_cell", -] - -[[package]] -name = "time" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" -dependencies = [ - "itoa", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" - -[[package]] -name = "time-macros" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" -dependencies = [ - "time-core", -] - -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" - -[[package]] -name = "tokio" -version = "1.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" -dependencies = [ - "autocfg", - "backtrace", - "bytes", - "libc", - "mio", - "num_cpus", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-io-timeout" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" -dependencies = [ - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.27", -] - -[[package]] -name = "tokio-rustls" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" -dependencies = [ - "rustls 0.19.1", - "tokio", - "webpki 0.21.4", -] - -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls 0.21.5", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", - "tokio-util", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec509ac96e9a0c43427c74f003127d953a265737636129424288d27cb5c4b12c" -dependencies = [ - "futures-util", - "log", - "rustls 0.21.5", - "tokio", - "tokio-rustls 0.24.1", - "tungstenite", - "webpki-roots 0.23.1", -] - -[[package]] -name = "tokio-util" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", -] - -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - -[[package]] -name = "toml" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime 0.6.3", - "toml_edit 0.19.14", -] - -[[package]] -name = "toml_datetime" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5" - -[[package]] -name = "toml_datetime" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b" -dependencies = [ - "indexmap 1.9.2", - "nom8", - "toml_datetime 0.5.1", -] - -[[package]] -name = "toml_edit" -version = "0.19.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" -dependencies = [ - "indexmap 2.0.0", - "serde", - "serde_spanned", - "toml_datetime 0.6.3", - "winnow", -] - -[[package]] -name = "tonic" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f219fad3b929bef19b1f86fbc0358d35daed8f2cac972037ac0dc10bbb8d5fb" -dependencies = [ - "async-stream", - "async-trait", - "axum", - "base64 0.13.1", - "bytes", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-timeout", - "percent-encoding", - "pin-project", - "prost", - "prost-derive", - "tokio", - "tokio-stream", - "tokio-util", - "tower", - "tower-layer", - "tower-service", - "tracing", - "tracing-futures", -] - -[[package]] -name = "tonic-build" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf5e9b9c0f7e0a7c027dcfaba7b2c60816c7049171f679d99ee2ff65d0de8c4" -dependencies = [ - "prettyplease 0.1.25", - "proc-macro2", - "prost-build", - "quote", - "syn 1.0.107", -] - -[[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.2", - "pin-project", - "pin-project-lite", - "rand", - "slab", - "tokio", - "tokio-util", - "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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if", - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "tracing-core" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - -[[package]] -name = "tracing-log" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" -dependencies = [ - "lazy_static", - "log", - "tracing-core", -] - -[[package]] -name = "tracing-serde" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" -dependencies = [ - "serde", - "tracing-core", - "valuable", - "valuable-serde", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" -dependencies = [ - "nu-ansi-term", - "serde", - "serde_json", - "sharded-slab", - "smallvec", - "thread_local", - "tracing-core", - "tracing-log", - "tracing-serde", - "valuable", - "valuable-serde", -] - -[[package]] -name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - -[[package]] -name = "tungstenite" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15fba1a6d6bb030745759a9a2a588bfe8490fc8b4751a277db3a0be1c9ebbf67" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand", - "rustls 0.21.5", - "sha1", - "thiserror", - "url", - "utf-8", - "webpki 0.22.0", -] - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "ucd-trie" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" - -[[package]] -name = "uint" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" - -[[package]] -name = "unicode-ident" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "url" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - -[[package]] -name = "uuid" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" -dependencies = [ - "getrandom", - "serde", -] - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -dependencies = [ - "valuable-derive", -] - -[[package]] -name = "valuable-derive" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d44690c645190cfce32f91a1582281654b2338c6073fa250b0949fd25c55b32" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "valuable-serde" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5285cfff30cdabe26626736a54d989687dd9cab84f51f4048b61d6d0ae8b0907" -dependencies = [ - "serde", - "valuable", -] - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "voting-verifier" -version = "0.1.0" -source = "git+https://github.com/axelarnetwork/axelar-amplifier.git#a314da5d2d40702d5f20ef9ab0e697a3b88f48b3" -dependencies = [ - "axelar-wasm-std", - "router", - "cosmwasm-schema", - "cosmwasm-std", - "cosmwasm-storage", - "cw-storage-plus 1.1.0", - "cw0", - "cw2", - "either", - "schemars", - "serde", - "serde_json", - "service-registry", - "thiserror", -] - -[[package]] -name = "walkdir" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" -dependencies = [ - "same-file", - "winapi", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 1.0.107", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" - -[[package]] -name = "web-sys" -version = "0.3.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki-roots" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" -dependencies = [ - "webpki 0.21.4", -] - -[[package]] -name = "webpki-roots" -version = "0.22.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" -dependencies = [ - "webpki 0.22.0", -] - -[[package]] -name = "webpki-roots" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" -dependencies = [ - "rustls-webpki 0.100.1", -] - -[[package]] -name = "which" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" -dependencies = [ - "either", - "libc", - "once_cell", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" -dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.1", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm 0.42.1", - "windows_x86_64_msvc 0.42.1", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.1", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-targets" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.1", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm 0.42.1", - "windows_x86_64_msvc 0.42.1", -] - -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - -[[package]] -name = "winnow" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7" -dependencies = [ - "memchr", -] - -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi", -] - -[[package]] -name = "ws_stream_wasm" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7999f5f4217fe3818726b66257a4475f71e74ffd190776ad053fa159e50737f5" -dependencies = [ - "async_io_stream", - "futures", - "js-sys", - "log", - "pharos", - "rustc_version", - "send_wrapper 0.6.0", - "thiserror", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - -[[package]] -name = "yansi" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" - -[[package]] -name = "zeroize" -version = "1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", - "synstructure", -] - -[[package]] -name = "zip" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" -dependencies = [ - "aes", - "byteorder", - "bzip2", - "constant_time_eq", - "crc32fast", - "crossbeam-utils", - "flate2", - "hmac", - "pbkdf2 0.11.0", - "sha1", - "time", - "zstd", -] - -[[package]] -name = "zstd" -version = "0.11.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "5.0.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.8+zstd.1.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" -dependencies = [ - "cc", - "libc", - "pkg-config", -] \ No newline at end of file diff --git a/ampd/Cargo.toml b/ampd/Cargo.toml index d9fa5e2fc..6dd31cc18 100644 --- a/ampd/Cargo.toml +++ b/ampd/Cargo.toml @@ -1,7 +1,7 @@ [package] -edition = "2021" name = "ampd" -version = "0.4.0" +edition = { workspace = true } +version = "1.0.0" rust-version = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -10,18 +10,21 @@ async-trait = "0.1.59" axelar-wasm-std = { workspace = true } axum = "0.7.5" base64 = "0.21.2" -bcs = "0.1.5" +bcs = { workspace = true } clap = { version = "4.2.7", features = ["derive", "cargo"] } config = "0.13.2" -cosmos-sdk-proto = "0.16.0" -cosmrs = { version = "0.14.0", features = ["cosmwasm"] } +cosmrs = { version = "0.14.0", features = ["cosmwasm", "grpc"] } cosmwasm-std = { workspace = true, features = ["stargate"] } +der = { version = "0.7.9", features = ["derive"] } deref-derive = "0.1.0" dirs = "5.0.1" ecdsa = { version = "0.16.6" } +ed25519 = { version = "2.2.3", default-features = false } enum-display-derive = "0.1.1" error-stack = { workspace = true } -ethers = { version = "2.0.8", features = ["rustls"] } +ethers-contract = { workspace = true } +ethers-core = { workspace = true } +ethers-providers = { version = "2.0.13", default-features = false, features = ["rustls"] } events = { workspace = true } events-derive = { workspace = true } evm-gateway = { workspace = true } @@ -29,10 +32,11 @@ futures = "0.3.25" hex = { version = "0.4.3", features = ["serde"] } humantime-serde = "1.1.1" itertools = { workspace = true } -k256 = { version = "0.13.1", features = ["ecdsa"] } +k256 = { workspace = true } mockall = "0.11.3" -move-core-types = { git = "https://github.com/mystenlabs/sui", tag = "mainnet-v1.14.2" } +move-core-types = { git = "https://github.com/mystenlabs/sui", tag = "mainnet-v1.26.2" } multisig = { workspace = true, features = ["library"] } +multiversx-sdk = "0.4.1" num-traits = { workspace = true } prost = "0.11.9" prost-types = "0.11.9" @@ -44,8 +48,9 @@ serde_json = { workspace = true } serde_with = "3.2.0" service-registry = { workspace = true } sha3 = { workspace = true } -sui-json-rpc-types = { git = "https://github.com/mystenlabs/sui", tag = "mainnet-v1.14.2" } -sui-types = { git = "https://github.com/mystenlabs/sui", features = ["test-utils"], tag = "mainnet-v1.14.2" } +sui-gateway = { workspace = true } +sui-json-rpc-types = { git = "https://github.com/mystenlabs/sui", tag = "mainnet-v1.26.2" } +sui-types = { git = "https://github.com/mystenlabs/sui", features = ["test-utils"], tag = "mainnet-v1.26.2" } # Need to switch to our own fork of tendermint and tendermint-rpc due to event attribute value being nullable. # Can switch back once https://github.com/informalsystems/tendermint-rs/issues/1216 is resolved. # The fix for the issue is at https://github.com/axelarnetwork/tendermint-rs/commit/e97033e20e660a7e707ea86db174ec047bbba50d. @@ -54,11 +59,11 @@ tendermint-rpc = { git = "https://github.com/axelarnetwork/tendermint-rs.git", b "http-client", ] } thiserror = { workspace = true } -tokio = { version = "1.22.0", features = ["signal"] } -tokio-stream = { version = "0.1.11", features = ["sync"] } -tokio-util = "0.7.8" +tokio = { workspace = true, features = ["signal"] } +tokio-stream = { workspace = true, features = ["sync"] } +tokio-util = { workspace = true } toml = "0.5.9" -tonic = "0.8.3" +tonic = "0.9.2" tracing = { version = "0.1.37", features = ["valuable", "log"] } tracing-core = { version = "0.1.30", features = ["valuable"] } tracing-subscriber = { version = "0.3.16", features = ["json", "valuable"] } @@ -74,9 +79,9 @@ generic-array = "0.14.7" multisig = { workspace = true, features = ["test", "library"] } rand = "0.8.5" random-string = "1.0.0" +tokio = { workspace = true, features = ["test-util"] } [build-dependencies] -ethers = "2.0.8" tonic-build = "0.8.3" [lints] diff --git a/ampd/Dockerfile b/ampd/Dockerfile index 305cbfca1..e778c783f 100644 --- a/ampd/Dockerfile +++ b/ampd/Dockerfile @@ -1,13 +1,15 @@ -FROM rust:1.75-bookworm as builder +FROM rust:1.78-bookworm as builder RUN apt-get update && apt-get install -y clang protobuf-compiler cmake WORKDIR /ampd COPY ./Cargo.toml ./Cargo.toml +COPY ./Cargo.lock ./Cargo.lock COPY ./ampd/Cargo.toml ./ampd/Cargo.toml -COPY ./ampd/Cargo.lock ./ampd/Cargo.lock COPY ./packages ./packages COPY ./contracts ./contracts +COPY ./interchain-token-service ./interchain-token-service COPY ./integration-tests ./integration-tests +COPY ./external-gateways ./external-gateways COPY ./.cargo ./.cargo # build dependencies separately @@ -21,6 +23,7 @@ COPY ./ampd/build.rs ./ampd/build.rs RUN cargo install --locked --path ./ampd FROM debian:bookworm-slim AS runner +RUN apt update && apt install libssl3 RUN addgroup --system --gid 1001 axelard && adduser --home /home/axelard --system --uid 1000 --ingroup axelard axelard WORKDIR /home/axelard RUN mkdir /.ampd && chown axelard /.ampd diff --git a/ampd/build.rs b/ampd/build.rs index 7581fa013..cd5acc3b9 100644 --- a/ampd/build.rs +++ b/ampd/build.rs @@ -8,5 +8,13 @@ fn main() -> Result<(), Box> { .build_client(true) .compile(&["proto/ampd.proto"], &["proto"])?; + tonic_build::configure() + .build_server(false) + .build_client(false) + .compile( + &["proto/axelar/auxiliary/v1beta1/tx.proto"], + &["proto", "proto/third_party"], + )?; + Ok(()) } diff --git a/ampd/proto/ampd.proto b/ampd/proto/ampd.proto index 1da3612b5..fa477adda 100644 --- a/ampd/proto/ampd.proto +++ b/ampd/proto/ampd.proto @@ -4,15 +4,20 @@ import "google/protobuf/any.proto"; package ampd; +enum Algorithm { + ALGORITHM_ECDSA = 0; + ALGORITHM_ED25519 = 1; +} + message SubscribeRequest { // the subscription will return all events that match ANY of these filters repeated Event event_filters = 1; bool include_block_begin_end = 2; } -message EventBlockBegin { uint64 height = 1; } +message EventBlockBegin {uint64 height = 1;} -message EventBlockEnd { uint64 height = 1; } +message EventBlockEnd {uint64 height = 1;} message Event { string event_type = 1; @@ -27,7 +32,7 @@ message SubscribeResponse { } } -message BroadcastRequest { google.protobuf.Any msg = 1; } +message BroadcastRequest {google.protobuf.Any msg = 1;} message BroadcastResponse {} @@ -35,3 +40,23 @@ service Ampd { rpc Subscribe(SubscribeRequest) returns (stream SubscribeResponse) {} rpc Broadcast(BroadcastRequest) returns (BroadcastResponse) {} } + +message SignRequest { + string key_id = 1; + bytes msg = 2; + Algorithm algorithm = 3; +} + +message SignResponse {bytes signature = 1;} + +message KeyRequest { + string key_id = 1; + Algorithm algorithm = 2; +} + +message KeyResponse {bytes pub_key = 1;} + +service Crypto { + rpc Sign(SignRequest) returns (SignResponse) {} + rpc Key(KeyRequest) returns (KeyResponse) {} +} diff --git a/ampd/proto/axelar/README.md b/ampd/proto/axelar/README.md new file mode 100644 index 000000000..519187b2b --- /dev/null +++ b/ampd/proto/axelar/README.md @@ -0,0 +1,11 @@ +# Axelar Protobufs + +This folder defines protobufs used by Axelar specific Cosmos SDK msg, event, and query types. + +## REST API + +The REST API (LCD) gets generated automatically from the gRPC service definitions. +The request/response types are defined in `query.proto` for the respective modules, and the query is defined in the `service.proto`. + +Note: The request types cannot make use of custom types encoded as bytes as that would be awkward +for REST-based calls. Instead, primitive types such as string is used (for e.g. when specifying addresses, instead of using sdk.AccAddress). diff --git a/ampd/proto/axelar/auxiliary/v1beta1/events.proto b/ampd/proto/axelar/auxiliary/v1beta1/events.proto new file mode 100644 index 000000000..90956b702 --- /dev/null +++ b/ampd/proto/axelar/auxiliary/v1beta1/events.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; +package axelar.auxiliary.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/auxiliary/types"; +option (gogoproto.messagename_all) = true; + +import "gogoproto/gogo.proto"; + +message BatchedMessageFailed { + int32 index = 1; + string error = 2; +} diff --git a/ampd/proto/axelar/auxiliary/v1beta1/genesis.proto b/ampd/proto/axelar/auxiliary/v1beta1/genesis.proto new file mode 100644 index 000000000..0d766e10f --- /dev/null +++ b/ampd/proto/axelar/auxiliary/v1beta1/genesis.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; +package axelar.auxiliary.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/auxiliary/types"; + +import "gogoproto/gogo.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// GenesisState represents the genesis state +message GenesisState {} diff --git a/ampd/proto/axelar/auxiliary/v1beta1/service.proto b/ampd/proto/axelar/auxiliary/v1beta1/service.proto new file mode 100644 index 000000000..6d2a08e9b --- /dev/null +++ b/ampd/proto/axelar/auxiliary/v1beta1/service.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; +package axelar.auxiliary.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/auxiliary/types"; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "axelar/auxiliary/v1beta1/tx.proto"; + +option (gogoproto.goproto_registration) = true; + +// Msg defines the nexus Msg service. +service MsgService { + rpc Batch(BatchRequest) returns (BatchResponse) { + option (google.api.http) = { + post : "/axelar/auxiliary/batch" + body : "*" + }; + } +} diff --git a/ampd/proto/axelar/auxiliary/v1beta1/tx.proto b/ampd/proto/axelar/auxiliary/v1beta1/tx.proto new file mode 100644 index 000000000..065016da4 --- /dev/null +++ b/ampd/proto/axelar/auxiliary/v1beta1/tx.proto @@ -0,0 +1,30 @@ +syntax = "proto3"; +package axelar.auxiliary.v1beta1; + +import "google/protobuf/any.proto"; +import "gogoproto/gogo.proto"; + +import "axelar/permission/exported/v1beta1/types.proto"; +import "cosmos/base/abci/v1beta1/abci.proto"; +import "cosmos_proto/cosmos.proto"; + +option go_package = "github.com/axelarnetwork/axelar-core/x/auxiliary/types"; + +message BatchRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + repeated google.protobuf.Any messages = 2 + [ (gogoproto.nullable) = false, (cosmos_proto.accepts_interface) = "cosmos.base.v1beta1.Msg" ]; +} + +message BatchResponse { + message Response { + oneof res { + cosmos.base.abci.v1beta1.Result result = 1; + string err = 2; + } + } + repeated Response responses = 1 [ (gogoproto.nullable) = false ]; +} diff --git a/ampd/proto/axelar/axelarnet/v1beta1/events.proto b/ampd/proto/axelar/axelarnet/v1beta1/events.proto new file mode 100644 index 000000000..a5e9e0a67 --- /dev/null +++ b/ampd/proto/axelar/axelarnet/v1beta1/events.proto @@ -0,0 +1,131 @@ +syntax = "proto3"; +package axelar.axelarnet.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/axelarnet/types"; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option (gogoproto.messagename_all) = true; + +message IBCTransferSent { + uint64 id = 1 [ + (gogoproto.customname) = "ID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.TransferID" + ]; + string receipient = 2 [ deprecated = true ]; + cosmos.base.v1beta1.Coin asset = 3 [ (gogoproto.nullable) = false ]; + uint64 sequence = 4; + string port_id = 5 [ (gogoproto.customname) = "PortID" ]; + string channel_id = 6 [ (gogoproto.customname) = "ChannelID" ]; + string recipient = 7; +} + +message IBCTransferCompleted { + uint64 id = 1 [ + (gogoproto.customname) = "ID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.TransferID" + ]; + uint64 sequence = 2; + string port_id = 3 [ (gogoproto.customname) = "PortID" ]; + string channel_id = 4 [ (gogoproto.customname) = "ChannelID" ]; +} + +message IBCTransferFailed { + uint64 id = 1 [ + (gogoproto.customname) = "ID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.TransferID" + ]; + uint64 sequence = 2; + string port_id = 3 [ (gogoproto.customname) = "PortID" ]; + string channel_id = 4 [ (gogoproto.customname) = "ChannelID" ]; +} + +message IBCTransferRetried { + uint64 id = 1 [ + (gogoproto.customname) = "ID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.TransferID" + ]; + string receipient = 2 [ deprecated = true ]; + cosmos.base.v1beta1.Coin asset = 3 [ (gogoproto.nullable) = false ]; + uint64 sequence = 4; + string port_id = 5 [ (gogoproto.customname) = "PortID" ]; + string channel_id = 6 [ (gogoproto.customname) = "ChannelID" ]; + string recipient = 7; +} + +message AxelarTransferCompleted { + uint64 id = 1 [ + (gogoproto.customname) = "ID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.TransferID" + ]; + string receipient = 2 [ deprecated = true ]; + cosmos.base.v1beta1.Coin asset = 3 [ (gogoproto.nullable) = false ]; + string recipient = 4; +} + +message FeeCollected { + bytes collector = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + cosmos.base.v1beta1.Coin fee = 2 [ (gogoproto.nullable) = false ]; +} + +message FeePaid { + string message_id = 1 [ (gogoproto.customname) = "MessageID" ]; + bytes recipient = 2 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + cosmos.base.v1beta1.Coin fee = 3 [ (gogoproto.nullable) = false ]; + string refund_recipient = 4; + string asset = 5; // registered asset name in nexus +} + +message ContractCallSubmitted { + string message_id = 1 [ (gogoproto.customname) = "MessageID" ]; + string sender = 2; + string source_chain = 3 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string destination_chain = 4 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string contract_address = 5; + bytes payload = 6; + bytes payload_hash = 7; +} + +message ContractCallWithTokenSubmitted { + string message_id = 1 [ (gogoproto.customname) = "MessageID" ]; + string sender = 2; + string source_chain = 3 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string destination_chain = 4 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string contract_address = 5; + bytes payload = 6; + bytes payload_hash = 7; + cosmos.base.v1beta1.Coin asset = 8 [ (gogoproto.nullable) = false ]; +} + +message TokenSent { + uint64 transfer_id = 1 [ + (gogoproto.customname) = "TransferID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.TransferID" + ]; + string sender = 2; + string source_chain = 3 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string destination_chain = 4 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string destination_address = 5; + cosmos.base.v1beta1.Coin asset = 6 [ (gogoproto.nullable) = false ]; +} diff --git a/ampd/proto/axelar/axelarnet/v1beta1/genesis.proto b/ampd/proto/axelar/axelarnet/v1beta1/genesis.proto new file mode 100644 index 000000000..8cfea9cd5 --- /dev/null +++ b/ampd/proto/axelar/axelarnet/v1beta1/genesis.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; +package axelar.axelarnet.v1beta1; +option go_package = "github.com/axelarnetwork/axelar-core/x/axelarnet/types"; + +import "gogoproto/gogo.proto"; +import "axelar/axelarnet/v1beta1/params.proto"; +import "axelar/axelarnet/v1beta1/types.proto"; +import "axelar/utils/v1beta1/queuer.proto"; + +option (gogoproto.goproto_getters_all) = false; + +message GenesisState { + option (gogoproto.stable_marshaler) = true; + + Params params = 1 [ (gogoproto.nullable) = false ]; + bytes collector_address = 2 + [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + repeated CosmosChain chains = 3 [ (gogoproto.nullable) = false ]; + reserved 4; // pending_transfers was removed in v0.20 + utils.v1beta1.QueueState transfer_queue = 5 [ (gogoproto.nullable) = false ]; + reserved 6; // failed_transfers was removed in v0.22 + repeated IBCTransfer ibc_transfers = 7 + [ (gogoproto.nullable) = false, (gogoproto.customname) = "IBCTransfers" ]; + map seq_id_mapping = 8 + [ (gogoproto.nullable) = false, (gogoproto.customname) = "SeqIDMapping" ]; +} diff --git a/ampd/proto/axelar/axelarnet/v1beta1/params.proto b/ampd/proto/axelar/axelarnet/v1beta1/params.proto new file mode 100644 index 000000000..5d669fbc6 --- /dev/null +++ b/ampd/proto/axelar/axelarnet/v1beta1/params.proto @@ -0,0 +1,33 @@ +syntax = "proto3"; +package axelar.axelarnet.v1beta1; +option go_package = "github.com/axelarnetwork/axelar-core/x/axelarnet/types"; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// Params represent the genesis parameters for the module +message Params { + // IBC packet route timeout window + uint64 route_timeout_window = 1; + reserved 2; // transaction_fee_rate was removed in v0.15 + uint64 transfer_limit = 3; + uint64 end_blocker_limit = 4; + repeated CallContractProposalMinDeposit call_contracts_proposal_min_deposits = + 5 [ + (gogoproto.castrepeated) = "CallContractProposalMinDeposits", + (gogoproto.nullable) = false + ]; +} + +message CallContractProposalMinDeposit { + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string contract_address = 2; + repeated cosmos.base.v1beta1.Coin min_deposits = 3 [ + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", + (gogoproto.nullable) = false + ]; +} diff --git a/ampd/proto/axelar/axelarnet/v1beta1/proposal.proto b/ampd/proto/axelar/axelarnet/v1beta1/proposal.proto new file mode 100644 index 000000000..2994ca8c8 --- /dev/null +++ b/ampd/proto/axelar/axelarnet/v1beta1/proposal.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; + +package axelar.axelarnet.v1beta1; + +import "gogoproto/gogo.proto"; +import "axelar/nexus/exported/v1beta1/types.proto"; + +option go_package = "github.com/axelarnetwork/axelar-core/x/axelarnet/types"; +option (gogoproto.goproto_getters_all) = false; + +// CallContractsProposal is a gov Content type for calling contracts on other +// chains +message CallContractsProposal { + option (gogoproto.goproto_stringer) = false; + + string title = 1; + string description = 2; + repeated ContractCall contract_calls = 3 [ (gogoproto.nullable) = false ]; +} + +message ContractCall { + option (gogoproto.goproto_stringer) = false; + + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string contract_address = 2; + bytes payload = 3; +} diff --git a/ampd/proto/axelar/axelarnet/v1beta1/query.proto b/ampd/proto/axelar/axelarnet/v1beta1/query.proto new file mode 100644 index 000000000..64fc7088d --- /dev/null +++ b/ampd/proto/axelar/axelarnet/v1beta1/query.proto @@ -0,0 +1,39 @@ +syntax = "proto3"; +package axelar.axelarnet.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/axelarnet/types"; + +import "gogoproto/gogo.proto"; +import "axelar/axelarnet/v1beta1/types.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; +import "axelar/nexus/v1beta1/query.proto"; +import "axelar/axelarnet/v1beta1/params.proto"; + +option (gogoproto.goproto_getters_all) = false; + +message PendingIBCTransferCountRequest {} + +message PendingIBCTransferCountResponse { + map transfers_by_chain = 1 [ (gogoproto.nullable) = false ]; +} + +// ParamsRequest represents a message that queries the params +message ParamsRequest {} + +message ParamsResponse { Params params = 1 [ (gogoproto.nullable) = false ]; } + +// IBCPathRequest represents a message that queries the IBC path registered for +// a given chain +message IBCPathRequest { string chain = 1; } + +message IBCPathResponse { string ibc_path = 1 [ (gogoproto.customname) = "IBCPath" ]; } + +// ChainByIBCPathRequest represents a message that queries the chain that an IBC +// path is registered to +message ChainByIBCPathRequest { string ibc_path = 1; } + +message ChainByIBCPathResponse { + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; +} diff --git a/ampd/proto/axelar/axelarnet/v1beta1/service.proto b/ampd/proto/axelar/axelarnet/v1beta1/service.proto new file mode 100644 index 000000000..c2ed583e8 --- /dev/null +++ b/ampd/proto/axelar/axelarnet/v1beta1/service.proto @@ -0,0 +1,118 @@ +syntax = "proto3"; +package axelar.axelarnet.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/axelarnet/types"; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "axelar/axelarnet/v1beta1/tx.proto"; +import "axelar/axelarnet/v1beta1/query.proto"; + +option (gogoproto.goproto_registration) = true; + +// Msg defines the axelarnet Msg service. +service MsgService { + rpc Link(LinkRequest) returns (LinkResponse) { + option (google.api.http) = { + post : "/axelar/axelarnet/link" + body : "*" + }; + } + + rpc ConfirmDeposit(ConfirmDepositRequest) returns (ConfirmDepositResponse) { + option (google.api.http) = { + post : "/axelar/axelarnet/confirm_deposit" + body : "*" + }; + } + + rpc ExecutePendingTransfers(ExecutePendingTransfersRequest) + returns (ExecutePendingTransfersResponse) { + option (google.api.http) = { + post : "/axelar/axelarnet/execute_pending_transfers" + body : "*" + }; + } + + rpc AddCosmosBasedChain(AddCosmosBasedChainRequest) + returns (AddCosmosBasedChainResponse) { + option (google.api.http) = { + post : "/axelar/axelarnet/add_cosmos_based_chain" + body : "*" + }; + } + + rpc RegisterAsset(RegisterAssetRequest) returns (RegisterAssetResponse) { + option (google.api.http) = { + post : "/axelar/axelarnet/register_asset" + body : "*" + }; + } + + rpc RouteIBCTransfers(RouteIBCTransfersRequest) + returns (RouteIBCTransfersResponse) { + option (google.api.http) = { + post : "/axelar/axelarnet/route_ibc_transfers" + body : "*" + }; + } + + rpc RegisterFeeCollector(RegisterFeeCollectorRequest) + returns (RegisterFeeCollectorResponse) { + option (google.api.http) = { + post : "/axelar/axelarnet/register_fee_collector" + body : "*" + }; + } + + rpc RetryIBCTransfer(RetryIBCTransferRequest) + returns (RetryIBCTransferResponse) { + option (google.api.http) = { + post : "/axelar/axelarnet/retry_ibc_transfer" + body : "*" + }; + } + + rpc RouteMessage(RouteMessageRequest) returns (RouteMessageResponse) { + option (google.api.http) = { + post : "/axelar/axelarnet/route_message" + body : "*" + }; + } + + rpc CallContract(CallContractRequest) returns (CallContractResponse) { + option (google.api.http) = { + post : "/axelar/axelarnet/call_contract" + body : "*" + }; + } +} + +// QueryService defines the gRPC querier service. +service QueryService { + + // PendingIBCTransferCount queries the pending ibc transfers for all chains + rpc PendingIBCTransferCount(PendingIBCTransferCountRequest) + returns (PendingIBCTransferCountResponse) { + option (google.api.http).get = + "/axelar/axelarnet/v1beta1/ibc_transfer_count"; + } + + rpc Params(ParamsRequest) returns (ParamsResponse) { + option (google.api.http) = { + get : "/axelar/axelarnet/v1beta1/params" + }; + } + + rpc IBCPath(IBCPathRequest) returns (IBCPathResponse) { + option (google.api.http) = { + get : "/axelar/axelarnet/v1beta1/ibc_path/{chain}" + }; + } + + rpc ChainByIBCPath(ChainByIBCPathRequest) returns (ChainByIBCPathResponse) { + option (google.api.http) = { + get : "/axelar/axelarnet/v1beta1/chain_by_ibc_path/{ibc_path}" + }; + } +} diff --git a/ampd/proto/axelar/axelarnet/v1beta1/tx.proto b/ampd/proto/axelar/axelarnet/v1beta1/tx.proto new file mode 100644 index 000000000..95c5e5c7c --- /dev/null +++ b/ampd/proto/axelar/axelarnet/v1beta1/tx.proto @@ -0,0 +1,188 @@ +syntax = "proto3"; +package axelar.axelarnet.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/axelarnet/types"; + +import "google/protobuf/any.proto"; +import "google/protobuf/duration.proto"; +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos_proto/cosmos.proto"; +import "axelar/nexus/exported/v1beta1/types.proto"; +import "axelar/axelarnet/v1beta1/types.proto"; +import "axelar/permission/exported/v1beta1/types.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// MsgLink represents a message to link a cross-chain address to an Axelar +// address +message LinkRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string recipient_addr = 2; + string recipient_chain = 3 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string asset = 4; +} + +message LinkResponse { string deposit_addr = 1; }; + +// MsgConfirmDeposit represents a deposit confirmation message +message ConfirmDepositRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + + reserved 2; // tx_id was removed in v0.14 + + reserved 3; // token was removed in v0.15 + + bytes deposit_address = 4 + [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + + string denom = 5; +} + +message ConfirmDepositResponse {} + +// MsgExecutePendingTransfers represents a message to trigger transfer all +// pending transfers +message ExecutePendingTransfersRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; +} + +message ExecutePendingTransfersResponse {} + +// MSgRegisterIBCPath represents a message to register an IBC tracing path for +// a cosmos chain +message RegisterIBCPathRequest { + option deprecated = true; + + option (permission.exported.v1beta1.permission_role) = ROLE_CHAIN_MANAGEMENT; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string path = 3; +} + +message RegisterIBCPathResponse {} + +// MsgAddCosmosBasedChain represents a message to register a cosmos based chain +// to nexus +message AddCosmosBasedChainRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_ACCESS_CONTROL; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + nexus.exported.v1beta1.Chain chain = 2 [ + deprecated = true, + (gogoproto.nullable) = false + ]; // chain was deprecated in v0.27 + string addr_prefix = 3; + reserved 4; // min_amount was removed in v0.15 + repeated nexus.exported.v1beta1.Asset native_assets = 5 [ + deprecated = true, + (gogoproto.nullable) = false + ]; // native_assets was deprecated in v0.27 + // TODO: Rename this to `chain` after v1beta1 -> v1 version bump + string cosmos_chain = 6 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string ibc_path = 7 [ (gogoproto.customname) = "IBCPath" ]; +} + +message AddCosmosBasedChainResponse {} + +// RegisterAssetRequest represents a message to register an asset to a cosmos +// based chain +message RegisterAssetRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_CHAIN_MANAGEMENT; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + nexus.exported.v1beta1.Asset asset = 3 [ (gogoproto.nullable) = false ]; + bytes limit = 4 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + (gogoproto.nullable) = false + ]; + google.protobuf.Duration window = 5 + [ (gogoproto.stdduration) = true, (gogoproto.nullable) = false ]; +} + +message RegisterAssetResponse {} + +// RouteIBCTransfersRequest represents a message to route pending transfers to +// cosmos based chains +message RouteIBCTransfersRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; +} + +message RouteIBCTransfersResponse {} + +// RegisterFeeCollectorRequest represents a message to register axelarnet fee +// collector account +message RegisterFeeCollectorRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_ACCESS_CONTROL; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + bytes fee_collector = 2 + [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; +} + +message RegisterFeeCollectorResponse {} + +message RetryIBCTransferRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string chain = 2 [ + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName", + deprecated = true + ]; + uint64 id = 3 [ + (gogoproto.customname) = "ID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.TransferID" + ]; +} + +message RetryIBCTransferResponse {} + +message RouteMessageRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string id = 2 [ (gogoproto.customname) = "ID" ]; + bytes payload = 3; + bytes feegranter = 4 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; +} + +message RouteMessageResponse {} + +message CallContractRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string contract_address = 3; + bytes payload = 4; + Fee fee = 5; +} + +message CallContractResponse {} diff --git a/ampd/proto/axelar/axelarnet/v1beta1/types.proto b/ampd/proto/axelar/axelarnet/v1beta1/types.proto new file mode 100644 index 000000000..b20ae2799 --- /dev/null +++ b/ampd/proto/axelar/axelarnet/v1beta1/types.proto @@ -0,0 +1,62 @@ +syntax = "proto3"; +package axelar.axelarnet.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/axelarnet/types"; +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option (gogoproto.goproto_getters_all) = false; + +message IBCTransfer { + enum Status { + option (gogoproto.goproto_enum_prefix) = false; + option (gogoproto.goproto_enum_stringer) = true; + + STATUS_UNSPECIFIED = 0 + [ (gogoproto.enumvalue_customname) = "TransferNonExistent" ]; + STATUS_PENDING = 1 [ (gogoproto.enumvalue_customname) = "TransferPending" ]; + STATUS_COMPLETED = 2 + [ (gogoproto.enumvalue_customname) = "TransferCompleted" ]; + STATUS_FAILED = 3 [ (gogoproto.enumvalue_customname) = "TransferFailed" ]; + } + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string receiver = 2; + cosmos.base.v1beta1.Coin token = 3 [ (gogoproto.nullable) = false ]; + string port_id = 4 [ (gogoproto.customname) = "PortID" ]; + string channel_id = 5 [ (gogoproto.customname) = "ChannelID" ]; + uint64 sequence = 6 [ deprecated = true ]; + uint64 id = 7 [ + (gogoproto.customname) = "ID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.TransferID" + ]; + Status status = 8; +} + +message CosmosChain { + string name = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string ibc_path = 2 [ (gogoproto.customname) = "IBCPath" ]; + repeated Asset assets = 3 [ (gogoproto.nullable) = false, deprecated = true ]; + string addr_prefix = 4; +} + +message Asset { + option deprecated = true; + string denom = 1; + bytes min_amount = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; +} + +message Fee { + cosmos.base.v1beta1.Coin amount = 1 [ (gogoproto.nullable) = false ]; + bytes recipient = 2 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + bytes refund_recipient = 3 + [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; +} diff --git a/ampd/proto/axelar/evm/v1beta1/events.proto b/ampd/proto/axelar/evm/v1beta1/events.proto new file mode 100644 index 000000000..bcdd74e99 --- /dev/null +++ b/ampd/proto/axelar/evm/v1beta1/events.proto @@ -0,0 +1,340 @@ +syntax = "proto3"; +package axelar.evm.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/evm/types"; +option (gogoproto.messagename_all) = true; + +import "gogoproto/gogo.proto"; +import "axelar/vote/exported/v1beta1/types.proto"; +import "axelar/evm/v1beta1/types.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +message PollFailed { + bytes tx_id = 1 [ + (gogoproto.nullable) = false, + (gogoproto.customtype) = "Hash", + (gogoproto.customname) = "TxID" + ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + uint64 poll_id = 3 [ + (gogoproto.customname) = "PollID", + (gogoproto.customtype) = + "github.com/axelarnetwork/axelar-core/x/vote/exported.PollID", + (gogoproto.nullable) = false + ]; +} + +message PollExpired { + bytes tx_id = 1 [ + (gogoproto.nullable) = false, + (gogoproto.customtype) = "Hash", + (gogoproto.customname) = "TxID" + ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + uint64 poll_id = 3 [ + (gogoproto.customname) = "PollID", + (gogoproto.customtype) = + "github.com/axelarnetwork/axelar-core/x/vote/exported.PollID", + (gogoproto.nullable) = false + ]; +} + +message PollCompleted { + bytes tx_id = 1 [ + (gogoproto.nullable) = false, + (gogoproto.customtype) = "Hash", + (gogoproto.customname) = "TxID" + ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + uint64 poll_id = 3 [ + (gogoproto.customname) = "PollID", + (gogoproto.customtype) = + "github.com/axelarnetwork/axelar-core/x/vote/exported.PollID", + (gogoproto.nullable) = false + ]; +} + +message NoEventsConfirmed { + bytes tx_id = 1 [ + (gogoproto.nullable) = false, + (gogoproto.customtype) = "Hash", + (gogoproto.customname) = "TxID" + ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + uint64 poll_id = 3 [ + (gogoproto.customname) = "PollID", + (gogoproto.customtype) = + "github.com/axelarnetwork/axelar-core/x/vote/exported.PollID", + (gogoproto.nullable) = false + ]; +} + +message ConfirmKeyTransferStarted { + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + bytes tx_id = 2 [ + (gogoproto.nullable) = false, + (gogoproto.customtype) = "Hash", + (gogoproto.customname) = "TxID" + ]; + bytes gateway_address = 3 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; + uint64 confirmation_height = 4; + vote.exported.v1beta1.PollParticipants participants = 5 + [ (gogoproto.nullable) = false, (gogoproto.embed) = true ]; +} + +message ConfirmGatewayTxStarted { + option deprecated = true; + + bytes tx_id = 1 [ + (gogoproto.nullable) = false, + (gogoproto.customtype) = "Hash", + (gogoproto.customname) = "TxID" + ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + bytes gateway_address = 3 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; + uint64 confirmation_height = 4; + vote.exported.v1beta1.PollParticipants participants = 5 + [ (gogoproto.nullable) = false, (gogoproto.embed) = true ]; +} + +message PollMapping { + bytes tx_id = 1 [ + (gogoproto.nullable) = false, + (gogoproto.customtype) = "Hash", + (gogoproto.customname) = "TxID" + ]; + uint64 poll_id = 2 [ + (gogoproto.nullable) = false, + (gogoproto.customname) = "PollID", + (gogoproto.customtype) = + "github.com/axelarnetwork/axelar-core/x/vote/exported.PollID" + ]; +} + +message ConfirmGatewayTxsStarted { + repeated PollMapping poll_mappings = 1 [ + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "poll_mappings,omitempty" + ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + bytes gateway_address = 3 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; + uint64 confirmation_height = 4; + repeated bytes participants = 5 + [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.ValAddress" ]; +} + +message ConfirmDepositStarted { + bytes tx_id = 1 [ + (gogoproto.nullable) = false, + (gogoproto.customtype) = "Hash", + (gogoproto.customname) = "TxID" + ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + bytes deposit_address = 3 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; + bytes token_address = 4 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; + uint64 confirmation_height = 5; + vote.exported.v1beta1.PollParticipants participants = 6 + [ (gogoproto.nullable) = false, (gogoproto.embed) = true ]; + string asset = 7; +} + +message ConfirmTokenStarted { + bytes tx_id = 1 [ + (gogoproto.nullable) = false, + (gogoproto.customtype) = "Hash", + (gogoproto.customname) = "TxID" + ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + bytes gateway_address = 3 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; + bytes token_address = 4 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; + evm.v1beta1.TokenDetails token_details = 5 [ (gogoproto.nullable) = false ]; + uint64 confirmation_height = 6; + vote.exported.v1beta1.PollParticipants participants = 7 + [ (gogoproto.nullable) = false, (gogoproto.embed) = true ]; +} + +message ChainAdded { + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; +} + +message CommandBatchSigned { + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + bytes command_batch_id = 3 [ (gogoproto.customname) = "CommandBatchID" ]; +} + +message CommandBatchAborted { + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + bytes command_batch_id = 3 [ (gogoproto.customname) = "CommandBatchID" ]; +} + +message EVMEventConfirmed { + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string event_id = 2 + [ (gogoproto.customname) = "EventID", (gogoproto.casttype) = "EventID" ]; + string type = 3; +} + +message EVMEventCompleted { + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string event_id = 2 + [ (gogoproto.customname) = "EventID", (gogoproto.casttype) = "EventID" ]; + string type = 3; +} + +message EVMEventFailed { + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string event_id = 2 + [ (gogoproto.customname) = "EventID", (gogoproto.casttype) = "EventID" ]; + string type = 3; +} + +message EVMEventRetryFailed { + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string event_id = 2 + [ (gogoproto.customname) = "EventID", (gogoproto.casttype) = "EventID" ]; + string type = 3; +} + +message ContractCallApproved { + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string event_id = 2 + [ (gogoproto.customname) = "EventID", (gogoproto.casttype) = "EventID" ]; + bytes command_id = 3 [ + (gogoproto.nullable) = false, + (gogoproto.customname) = "CommandID", + (gogoproto.customtype) = "CommandID" + ]; + string sender = 4; + string destination_chain = 5 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string contract_address = 6; + bytes payload_hash = 7 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Hash" ]; +} + +message ContractCallFailed { + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string msg_id = 2 [ (gogoproto.customname) = "MessageID" ]; +} + +message ContractCallWithMintApproved { + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string event_id = 2 + [ (gogoproto.customname) = "EventID", (gogoproto.casttype) = "EventID" ]; + bytes command_id = 3 [ + (gogoproto.nullable) = false, + (gogoproto.customname) = "CommandID", + (gogoproto.customtype) = "CommandID" + ]; + string sender = 4; + string destination_chain = 5 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string contract_address = 6; + bytes payload_hash = 7 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Hash" ]; + cosmos.base.v1beta1.Coin asset = 8 [ (gogoproto.nullable) = false ]; +} + +message TokenSent { + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string event_id = 2 + [ (gogoproto.customname) = "EventID", (gogoproto.casttype) = "EventID" ]; + uint64 transfer_id = 3 [ + (gogoproto.customname) = "TransferID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.TransferID" + ]; + string sender = 4; + string destination_chain = 5 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string destination_address = 6; + cosmos.base.v1beta1.Coin asset = 7 [ (gogoproto.nullable) = false ]; +} + +message MintCommand { + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + uint64 transfer_id = 2 [ + (gogoproto.customname) = "TransferID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.TransferID" + ]; + bytes command_id = 3 [ + (gogoproto.nullable) = false, + (gogoproto.customname) = "CommandID", + (gogoproto.customtype) = "CommandID" + ]; + string destination_chain = 4 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string destination_address = 5; + cosmos.base.v1beta1.Coin asset = 6 [ (gogoproto.nullable) = false ]; +} + +message BurnCommand { + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + bytes command_id = 2 [ + (gogoproto.nullable) = false, + (gogoproto.customname) = "CommandID", + (gogoproto.customtype) = "CommandID" + ]; + string destination_chain = 3 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string deposit_address = 4; + string asset = 5; +} diff --git a/ampd/proto/axelar/evm/v1beta1/genesis.proto b/ampd/proto/axelar/evm/v1beta1/genesis.proto new file mode 100644 index 000000000..3e0edc463 --- /dev/null +++ b/ampd/proto/axelar/evm/v1beta1/genesis.proto @@ -0,0 +1,39 @@ +syntax = "proto3"; +package axelar.evm.v1beta1; +option go_package = "github.com/axelarnetwork/axelar-core/x/evm/types"; + +import "axelar/utils/v1beta1/queuer.proto"; +import "gogoproto/gogo.proto"; +import "axelar/evm/v1beta1/params.proto"; +import "axelar/evm/v1beta1/types.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// GenesisState represents the genesis state +message GenesisState { + option (gogoproto.stable_marshaler) = true; + + message Chain { + Params params = 1 [ (gogoproto.nullable) = false ]; + repeated BurnerInfo burner_infos = 2 [ (gogoproto.nullable) = false ]; + utils.v1beta1.QueueState command_queue = 3 [ (gogoproto.nullable) = false ]; + repeated ERC20Deposit confirmed_deposits = 4 + [ (gogoproto.nullable) = false ]; + repeated ERC20Deposit burned_deposits = 5 [ (gogoproto.nullable) = false ]; + + repeated CommandBatchMetadata command_batches = 8 + [ (gogoproto.nullable) = false ]; + + Gateway gateway = 9 [ (gogoproto.nullable) = false ]; + repeated ERC20TokenMetadata tokens = 10 [ (gogoproto.nullable) = false ]; + repeated Event events = 11 [ (gogoproto.nullable) = false ]; + utils.v1beta1.QueueState confirmed_event_queue = 12 + [ (gogoproto.nullable) = false ]; + repeated ERC20Deposit legacy_confirmed_deposits = 13 + [ (gogoproto.nullable) = false ]; + repeated ERC20Deposit legacy_burned_deposits = 14 + [ (gogoproto.nullable) = false ]; + } + + repeated Chain chains = 3 [ (gogoproto.nullable) = false ]; +} diff --git a/ampd/proto/axelar/evm/v1beta1/params.proto b/ampd/proto/axelar/evm/v1beta1/params.proto new file mode 100644 index 000000000..6fc387be1 --- /dev/null +++ b/ampd/proto/axelar/evm/v1beta1/params.proto @@ -0,0 +1,37 @@ +syntax = "proto3"; +package axelar.evm.v1beta1; +option go_package = "github.com/axelarnetwork/axelar-core/x/evm/types"; + +import "axelar/utils/v1beta1/threshold.proto"; +import "axelar/evm/v1beta1/types.proto"; +import "gogoproto/gogo.proto"; +import "axelar/nexus/exported/v1beta1/types.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// Params is the parameter set for this module +message Params { + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + uint64 confirmation_height = 2; + string network = 3; + reserved 4; // gateway_code was removed in v0.16 + bytes token_code = 5; + bytes burnable = 6; + int64 revote_locking_period = 7; + repeated evm.v1beta1.NetworkInfo networks = 8 + [ (gogoproto.nullable) = false ]; + utils.v1beta1.Threshold voting_threshold = 9 [ (gogoproto.nullable) = false ]; + int64 min_voter_count = 10; + uint32 commands_gas_limit = 11; + reserved 12; // transaction_fee_rate was removed in v0.15 + int64 voting_grace_period = 13; + int64 end_blocker_limit = 14; + uint64 transfer_limit = 15; +} + +message PendingChain { + Params params = 1 [ (gogoproto.nullable) = false ]; + nexus.exported.v1beta1.Chain chain = 2 [ (gogoproto.nullable) = false ]; +} diff --git a/ampd/proto/axelar/evm/v1beta1/query.proto b/ampd/proto/axelar/evm/v1beta1/query.proto new file mode 100644 index 000000000..61de10b01 --- /dev/null +++ b/ampd/proto/axelar/evm/v1beta1/query.proto @@ -0,0 +1,243 @@ +syntax = "proto3"; +package axelar.evm.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/evm/types"; + +import "gogoproto/gogo.proto"; +import "axelar/evm/v1beta1/types.proto"; +import "axelar/evm/v1beta1/params.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// DepositQueryParams describe the parameters used to query for an EVM +// deposit address +message DepositQueryParams { + string address = 1; + string asset = 2; + string chain = 3 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; +} + +message BatchedCommandsRequest { + string chain = 1; + // id defines an optional id for the commandsbatch. If not specified the + // latest will be returned + string id = 2; +} + +message BatchedCommandsResponse { + string id = 1 [ (gogoproto.customname) = "ID" ]; + string data = 2; + BatchedCommandsStatus status = 3; + string key_id = 4 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; + reserved 5; // signature was removed in v0.20.0 + string execute_data = 6; + string prev_batched_commands_id = 7 + [ (gogoproto.customname) = "PrevBatchedCommandsID" ]; + repeated string command_ids = 8 [ (gogoproto.customname) = "CommandIDs" ]; + Proof proof = 9; +} + +message KeyAddressRequest { + reserved 2, 3; + + string chain = 1; + string key_id = 4 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; +} + +message KeyAddressResponse { + message WeightedAddress { + string address = 1; + string weight = 2; + }; + + string key_id = 1 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; + repeated WeightedAddress addresses = 2 [ (gogoproto.nullable) = false ]; + string threshold = 3; +} + +message QueryTokenAddressResponse { + option deprecated = true; // Deprecated in v19 + + string address = 1; + bool confirmed = 2; +} + +message QueryDepositStateParams { + option deprecated = true; + + bytes tx_id = 1 [ + (gogoproto.nullable) = false, + (gogoproto.customtype) = "Hash", + (gogoproto.customname) = "TxID" + ]; + bytes burner_address = 2 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; +} + +message DepositStateRequest { + option deprecated = true; + + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + QueryDepositStateParams params = 2; +} + +message DepositStateResponse { + option deprecated = true; + + DepositStatus status = 2; +} + +message EventRequest { + string chain = 1; + string event_id = 2; +} + +message EventResponse { Event event = 1; } + +message QueryBurnerAddressResponse { string address = 1; } + +enum ChainStatus { + option (gogoproto.goproto_enum_prefix) = false; + + CHAIN_STATUS_UNSPECIFIED = 0 + [ (gogoproto.enumvalue_customname) = "StatusUnspecified" ]; + CHAIN_STATUS_ACTIVATED = 1 [ (gogoproto.enumvalue_customname) = "Activated" ]; + CHAIN_STATUS_DEACTIVATED = 2 + [ (gogoproto.enumvalue_customname) = "Deactivated" ]; +} + +message ChainsRequest { ChainStatus status = 1; } + +message ChainsResponse { + repeated string chains = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; +} + +message CommandRequest { + string chain = 1; + string id = 2 [ (gogoproto.customname) = "ID" ]; +} + +message CommandResponse { + string id = 1 [ (gogoproto.customname) = "ID" ]; + string type = 2; + map params = 3 [ (gogoproto.nullable) = false ]; + string key_id = 4 [ (gogoproto.customname) = "KeyID" ]; + uint32 max_gas_cost = 5; +} + +message PendingCommandsRequest { string chain = 1; } + +message PendingCommandsResponse { + repeated QueryCommandResponse commands = 1 [ (gogoproto.nullable) = false ]; +} + +message QueryCommandResponse { + string id = 1 [ (gogoproto.customname) = "ID" ]; + string type = 2; + map params = 3 [ (gogoproto.nullable) = false ]; + string key_id = 4 [ (gogoproto.customname) = "KeyID" ]; + uint32 max_gas_cost = 5; +} + +message BurnerInfoRequest { + bytes address = 1 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; +} + +message BurnerInfoResponse { + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + BurnerInfo burner_info = 2; +} + +message ConfirmationHeightRequest { string chain = 1; } + +message ConfirmationHeightResponse { uint64 height = 1; } + +message GatewayAddressRequest { string chain = 1; } + +message GatewayAddressResponse { string address = 1; } + +message BytecodeRequest { + string chain = 1; + string contract = 2; +} + +message BytecodeResponse { string bytecode = 1; } + +enum TokenType { + option (gogoproto.goproto_enum_prefix) = false; + option (gogoproto.goproto_enum_stringer) = true; + + TOKEN_TYPE_UNSPECIFIED = 0 + [ (gogoproto.enumvalue_customname) = "Unspecified" ]; + TOKEN_TYPE_INTERNAL = 1 [ (gogoproto.enumvalue_customname) = "Internal" ]; + TOKEN_TYPE_EXTERNAL = 2 [ (gogoproto.enumvalue_customname) = "External" ]; +} + +// ERC20TokensRequest describes the chain for which the type of ERC20 tokens are +// requested. +message ERC20TokensRequest { + string chain = 1; + TokenType type = 2; +} + +// ERC20TokensResponse describes the asset and symbol for all +// ERC20 tokens requested for a chain +message ERC20TokensResponse { + message Token { + string asset = 1; + string symbol = 2; + } + + repeated Token tokens = 1 [ (gogoproto.nullable) = false ]; +} + +message TokenInfoRequest { + string chain = 1; + oneof find_by { + string asset = 2; + string symbol = 3; + string address = 4; + } +} + +message TokenInfoResponse { + string asset = 1; + TokenDetails details = 2 [ (gogoproto.nullable) = false ]; + string address = 3; + bool confirmed = 4; + bool is_external = 5; + string burner_code_hash = 6; +} + +message Proof { + repeated string addresses = 1; + repeated string weights = 2; + string threshold = 3; + repeated string signatures = 4; +} + +// ParamsRequest represents a message that queries the params +message ParamsRequest { string chain = 1; } + +message ParamsResponse { Params params = 1 [ (gogoproto.nullable) = false ]; } diff --git a/ampd/proto/axelar/evm/v1beta1/service.proto b/ampd/proto/axelar/evm/v1beta1/service.proto new file mode 100644 index 000000000..6cc9b02f0 --- /dev/null +++ b/ampd/proto/axelar/evm/v1beta1/service.proto @@ -0,0 +1,208 @@ +syntax = "proto3"; +package axelar.evm.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/evm/types"; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "axelar/evm/v1beta1/tx.proto"; +import "axelar/evm/v1beta1/query.proto"; + +option (gogoproto.goproto_registration) = true; + +// Msg defines the evm Msg service. +service MsgService { + rpc SetGateway(SetGatewayRequest) returns (SetGatewayResponse) { + option (google.api.http) = { + post : "/axelar/evm/set_gateway" + body : "*" + }; + } + + // Deprecated: use ConfirmGatewayTxs instead + rpc ConfirmGatewayTx(ConfirmGatewayTxRequest) + returns (ConfirmGatewayTxResponse) { + option (google.api.http) = { + post : "/axelar/evm/confirm_gateway_tx" + body : "*" + }; + } + + rpc ConfirmGatewayTxs(ConfirmGatewayTxsRequest) + returns (ConfirmGatewayTxsResponse) { + option (google.api.http) = { + post : "/axelar/evm/confirm_gateway_txs" + body : "*" + }; + } + + rpc Link(LinkRequest) returns (LinkResponse) { + option (google.api.http) = { + post : "/axelar/evm/link" + body : "*" + }; + } + + rpc ConfirmToken(ConfirmTokenRequest) returns (ConfirmTokenResponse) { + option (google.api.http) = { + post : "/axelar/evm/confirm_token" + body : "*" + }; + } + + rpc ConfirmDeposit(ConfirmDepositRequest) returns (ConfirmDepositResponse) { + option (google.api.http) = { + post : "/axelar/evm/confirm_deposit" + body : "*" + }; + } + + rpc ConfirmTransferKey(ConfirmTransferKeyRequest) + returns (ConfirmTransferKeyResponse) { + option (google.api.http) = { + post : "/axelar/evm/confirm_transfer_key" + body : "*" + }; + } + + rpc CreateDeployToken(CreateDeployTokenRequest) + returns (CreateDeployTokenResponse) { + option (google.api.http) = { + post : "/axelar/evm/create_deploy_token" + body : "*" + }; + } + + rpc CreateBurnTokens(CreateBurnTokensRequest) + returns (CreateBurnTokensResponse) { + option (google.api.http) = { + post : "/axelar/evm/create_burn_tokens" + body : "*" + }; + } + + rpc CreatePendingTransfers(CreatePendingTransfersRequest) + returns (CreatePendingTransfersResponse) { + option (google.api.http) = { + post : "/axelar/evm/create_pending_transfers" + body : "*" + }; + } + + rpc CreateTransferOperatorship(CreateTransferOperatorshipRequest) + returns (CreateTransferOperatorshipResponse) { + option (google.api.http) = { + post : "/axelar/evm/create_transfer_operatorship" + body : "*" + }; + } + + rpc SignCommands(SignCommandsRequest) returns (SignCommandsResponse) { + option (google.api.http) = { + post : "/axelar/evm/sign_commands" + body : "*" + }; + } + + rpc AddChain(AddChainRequest) returns (AddChainResponse) { + option (google.api.http) = { + post : "/axelar/evm/add_chain" + body : "*" + }; + } + + rpc RetryFailedEvent(RetryFailedEventRequest) + returns (RetryFailedEventResponse) { + option (google.api.http) = { + post : "/axelar/evm/retry-failed-event" + body : "*" + }; + } +} + +// QueryService defines the gRPC querier service. +service QueryService { + + // BatchedCommands queries the batched commands for a specified chain and + // BatchedCommandsID if no BatchedCommandsID is specified, then it returns the + // latest batched commands + rpc BatchedCommands(BatchedCommandsRequest) + returns (BatchedCommandsResponse) { + option (google.api.http).get = + "/axelar/evm/v1beta1/batched_commands/{chain}/{id}"; + } + + // BurnerInfo queries the burner info for the specified address + rpc BurnerInfo(BurnerInfoRequest) returns (BurnerInfoResponse) { + option (google.api.http).get = "/axelar/evm/v1beta1/burner_info"; + } + + // ConfirmationHeight queries the confirmation height for the specified chain + rpc ConfirmationHeight(ConfirmationHeightRequest) + returns (ConfirmationHeightResponse) { + option (google.api.http).get = + "/axelar/evm/v1beta1/confirmation_height/{chain}"; + } + + // DepositState queries the state of the specified deposit + rpc DepositState(DepositStateRequest) returns (DepositStateResponse) { + option deprecated = true; + option (google.api.http).get = "/axelar/evm/v1beta1/deposit_state"; + } + + // PendingCommands queries the pending commands for the specified chain + rpc PendingCommands(PendingCommandsRequest) + returns (PendingCommandsResponse) { + option (google.api.http).get = + "/axelar/evm/v1beta1/pending_commands/{chain}"; + } + + // Chains queries the available evm chains + rpc Chains(ChainsRequest) returns (ChainsResponse) { + option (google.api.http).get = "/axelar/evm/v1beta1/chains"; + } + + // Command queries the command of a chain provided the command id + rpc Command(CommandRequest) returns (CommandResponse) { + option (google.api.http).get = "/axelar/evm/v1beta1/command_request"; + } + + // KeyAddress queries the address of key of a chain + rpc KeyAddress(KeyAddressRequest) returns (KeyAddressResponse) { + option (google.api.http).get = "/axelar/evm/v1beta1/key_address/{chain}"; + } + + // GatewayAddress queries the address of axelar gateway at the specified + // chain + rpc GatewayAddress(GatewayAddressRequest) returns (GatewayAddressResponse) { + option (google.api.http).get = + "/axelar/evm/v1beta1/gateway_address/{chain}"; + } + + // Bytecode queries the bytecode of a specified gateway at the specified + // chain + rpc Bytecode(BytecodeRequest) returns (BytecodeResponse) { + option (google.api.http).get = + "/axelar/evm/v1beta1/bytecode/{chain}/{contract}"; + } + + // Event queries an event at the specified chain + rpc Event(EventRequest) returns (EventResponse) { + option (google.api.http).get = + "/axelar/evm/v1beta1/event/{chain}/{event_id}"; + } + + // ERC20Tokens queries the ERC20 tokens registered for a chain + rpc ERC20Tokens(ERC20TokensRequest) returns (ERC20TokensResponse) { + option (google.api.http).get = "/axelar/evm/v1beta1/erc20_tokens/{chain}"; + } + + // TokenInfo queries the token info for a registered ERC20 Token + rpc TokenInfo(TokenInfoRequest) returns (TokenInfoResponse) { + option (google.api.http).get = "/axelar/evm/v1beta1/token_info/{chain}"; + } + + rpc Params(ParamsRequest) returns (ParamsResponse) { + option (google.api.http).get = "/axelar/evm/v1beta1/params/{chain}"; + } +} diff --git a/ampd/proto/axelar/evm/v1beta1/tx.proto b/ampd/proto/axelar/evm/v1beta1/tx.proto new file mode 100644 index 000000000..9aaef12e4 --- /dev/null +++ b/ampd/proto/axelar/evm/v1beta1/tx.proto @@ -0,0 +1,259 @@ +syntax = "proto3"; +package axelar.evm.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/evm/types"; + +import "gogoproto/gogo.proto"; +import "axelar/vote/exported/v1beta1/types.proto"; +import "axelar/tss/exported/v1beta1/types.proto"; +import "axelar/evm/v1beta1/types.proto"; +import "axelar/permission/exported/v1beta1/types.proto"; + +option (gogoproto.goproto_getters_all) = false; + +message SetGatewayRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_ACCESS_CONTROL; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + bytes address = 3 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; +} + +message SetGatewayResponse {} + +message ConfirmGatewayTxRequest { + option deprecated = true; + + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + bytes tx_id = 3 [ + (gogoproto.nullable) = false, + (gogoproto.customtype) = "Hash", + (gogoproto.customname) = "TxID" + ]; +} + +message ConfirmGatewayTxResponse { option deprecated = true; } + +message ConfirmGatewayTxsRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + repeated bytes tx_ids = 3 [ + (gogoproto.nullable) = false, + (gogoproto.customtype) = "Hash", + (gogoproto.customname) = "TxIDs" + ]; +} + +message ConfirmGatewayTxsResponse {} + +// MsgConfirmDeposit represents an erc20 deposit confirmation message +message ConfirmDepositRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + bytes tx_id = 3 [ + (gogoproto.nullable) = false, + (gogoproto.customtype) = "Hash", + (gogoproto.customname) = "TxID" + ]; + bytes amount = 4 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + (gogoproto.nullable) = false, + deprecated = true + ]; + bytes burner_address = 5 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; +} + +message ConfirmDepositResponse {} + +// MsgConfirmToken represents a token deploy confirmation message +message ConfirmTokenRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + bytes tx_id = 3 [ + (gogoproto.nullable) = false, + (gogoproto.customtype) = "Hash", + (gogoproto.customname) = "TxID" + ]; + Asset asset = 4 [ (gogoproto.nullable) = false ]; +} + +message ConfirmTokenResponse {} + +message ConfirmTransferKeyRequest { + reserved 4, 5; // transfer_type and key_id were deleted in v0.20 + + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + bytes tx_id = 3 [ + (gogoproto.nullable) = false, + (gogoproto.customtype) = "Hash", + (gogoproto.customname) = "TxID" + ]; +} + +message ConfirmTransferKeyResponse {} + +// MsgLink represents the message that links a cross chain address to a burner +// address +message LinkRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string recipient_addr = 3; + string asset = 4; + string recipient_chain = 5 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; +} + +message LinkResponse { string deposit_addr = 1; } + +// CreateBurnTokensRequest represents the message to create commands to burn +// tokens with AxelarGateway +message CreateBurnTokensRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; +} + +message CreateBurnTokensResponse {} + +// CreateDeployTokenRequest represents the message to create a deploy token +// command for AxelarGateway +message CreateDeployTokenRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_CHAIN_MANAGEMENT; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + Asset asset = 3 [ (gogoproto.nullable) = false ]; + TokenDetails token_details = 4 [ (gogoproto.nullable) = false ]; + reserved 5; // min_amount was removed in v0.15 + bytes address = 6 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; + string daily_mint_limit = 7; +} + +message CreateDeployTokenResponse {} + +// CreatePendingTransfersRequest represents a message to trigger the creation of +// commands handling all pending transfers +message CreatePendingTransfersRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; +} + +message CreatePendingTransfersResponse {} + +message CreateTransferOwnershipRequest { + option deprecated = true; + option (permission.exported.v1beta1.permission_role) = ROLE_CHAIN_MANAGEMENT; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string key_id = 3 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; +} + +message CreateTransferOwnershipResponse { option deprecated = true; } + +message CreateTransferOperatorshipRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_CHAIN_MANAGEMENT; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string key_id = 3 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; +} + +message CreateTransferOperatorshipResponse {} + +message SignCommandsRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; +} + +message SignCommandsResponse { + bytes batched_commands_id = 1 + [ (gogoproto.customname) = "BatchedCommandsID" ]; + uint32 command_count = 2; +} + +message AddChainRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_ACCESS_CONTROL; + reserved 3; // native_asset was removed in v0.14 + + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string name = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + tss.exported.v1beta1.KeyType key_type = 4 [ deprecated = true ]; + bytes params = 5 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Params" ]; +} + +message AddChainResponse {} + +message RetryFailedEventRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string event_id = 3 + [ (gogoproto.customname) = "EventID", (gogoproto.casttype) = "EventID" ]; +} + +message RetryFailedEventResponse {} diff --git a/ampd/proto/axelar/evm/v1beta1/types.proto b/ampd/proto/axelar/evm/v1beta1/types.proto new file mode 100644 index 000000000..13a849cf9 --- /dev/null +++ b/ampd/proto/axelar/evm/v1beta1/types.proto @@ -0,0 +1,375 @@ +syntax = "proto3"; +package axelar.evm.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/evm/types"; + +import "google/protobuf/any.proto"; +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "axelar/nexus/exported/v1beta1/types.proto"; +import "axelar/multisig/exported/v1beta1/types.proto"; + +option (gogoproto.goproto_getters_all) = false; + +message VoteEvents { + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + repeated Event events = 2 [ (gogoproto.nullable) = false ]; +} + +message Event { + enum Status { + option (gogoproto.goproto_enum_prefix) = false; + option (gogoproto.goproto_enum_stringer) = true; + + STATUS_UNSPECIFIED = 0 + [ (gogoproto.enumvalue_customname) = "EventNonExistent" ]; + STATUS_CONFIRMED = 1 + [ (gogoproto.enumvalue_customname) = "EventConfirmed" ]; + STATUS_COMPLETED = 2 + [ (gogoproto.enumvalue_customname) = "EventCompleted" ]; + STATUS_FAILED = 3 [ (gogoproto.enumvalue_customname) = "EventFailed" ]; + } + + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + bytes tx_id = 2 [ + (gogoproto.customname) = "TxID", + (gogoproto.nullable) = false, + (gogoproto.customtype) = "Hash" + ]; + uint64 index = 3; + Status status = 4; + + oneof event { + EventTokenSent token_sent = 5; + EventContractCall contract_call = 6; + EventContractCallWithToken contract_call_with_token = 7; + EventTransfer transfer = 8; + EventTokenDeployed token_deployed = 9; + EventMultisigOwnershipTransferred multisig_ownership_transferred = 10 + [ deprecated = true ]; + EventMultisigOperatorshipTransferred multisig_operatorship_transferred = 11; + } + + reserved 12; // singlesig_ownership_transferred was removed in v0.23 + reserved 13; // singlesig_operatorship_transferred was removed in v0.23 +} + +message EventTokenSent { + bytes sender = 1 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; + string destination_chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string destination_address = 3; + string symbol = 4; + bytes amount = 5 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + (gogoproto.nullable) = false + ]; +} + +message EventContractCall { + bytes sender = 1 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; + string destination_chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string contract_address = 3; + bytes payload_hash = 4 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Hash" ]; +} + +message EventContractCallWithToken { + bytes sender = 1 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; + string destination_chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string contract_address = 3; + bytes payload_hash = 4 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Hash" ]; + string symbol = 5; + bytes amount = 6 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + (gogoproto.nullable) = false + ]; +} + +message EventTransfer { + bytes to = 1 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; + bytes amount = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + (gogoproto.nullable) = false + ]; +} + +message EventTokenDeployed { + string symbol = 1; + bytes token_address = 2 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; +} + +message EventMultisigOwnershipTransferred { + option deprecated = true; + + repeated bytes pre_owners = 1 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; + bytes prev_threshold = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + (gogoproto.nullable) = false + ]; + repeated bytes new_owners = 3 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; + bytes new_threshold = 4 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + (gogoproto.nullable) = false + ]; +} + +message EventMultisigOperatorshipTransferred { + reserved 1, 2; // pre_operators and prev_threshold were removed in v0.20 + + repeated bytes new_operators = 3 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; + bytes new_threshold = 4 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + (gogoproto.nullable) = false + ]; + repeated bytes new_weights = 5 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + (gogoproto.nullable) = false + ]; +} + +// NetworkInfo describes information about a network +message NetworkInfo { + string name = 1; + bytes id = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; +} + +// BurnerInfo describes information required to burn token at an burner address +// that is deposited by an user +message BurnerInfo { + bytes burner_address = 1 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; + bytes token_address = 2 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; + string destination_chain = 3 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string symbol = 4; + string asset = 5; + bytes salt = 6 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Hash" ]; +} + +// ERC20Deposit contains information for an ERC20 deposit +message ERC20Deposit { + bytes tx_id = 1 [ + (gogoproto.nullable) = false, + (gogoproto.customtype) = "Hash", + (gogoproto.customname) = "TxID" + ]; + bytes amount = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + (gogoproto.nullable) = false + ]; + string asset = 3; + string destination_chain = 4 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + bytes burner_address = 5 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; + uint64 log_index = 6; +} + +// ERC20TokenMetadata describes information about an ERC20 token +message ERC20TokenMetadata { + string asset = 1; + bytes chain_id = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.customname) = "ChainID", + (gogoproto.nullable) = false + ]; + TokenDetails details = 3 [ (gogoproto.nullable) = false ]; + string token_address = 4 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; + string tx_hash = 5 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Hash" ]; + reserved 6; // min_amount was removed in v0.15 + Status status = 7; + bool is_external = 8; + bytes burner_code = 9; +} + +enum Status { + option (gogoproto.goproto_enum_prefix) = false; + option (gogoproto.goproto_enum_stringer) = true; + + // these enum values are used for bitwise operations, therefore they need to + // be powers of 2 + STATUS_UNSPECIFIED = 0 [ (gogoproto.enumvalue_customname) = "NonExistent" ]; + STATUS_INITIALIZED = 1 [ (gogoproto.enumvalue_customname) = "Initialized" ]; + STATUS_PENDING = 2 [ (gogoproto.enumvalue_customname) = "Pending" ]; + STATUS_CONFIRMED = 4 [ (gogoproto.enumvalue_customname) = "Confirmed" ]; +} + +message TransactionMetadata { + bytes raw_tx = 1 [ (gogoproto.customname) = "RawTX" ]; + bytes pub_key = 2; +} + +enum CommandType { + option (gogoproto.goproto_enum_prefix) = false; + option (gogoproto.goproto_enum_stringer) = false; + + COMMAND_TYPE_UNSPECIFIED = 0; + COMMAND_TYPE_MINT_TOKEN = 1; + COMMAND_TYPE_DEPLOY_TOKEN = 2; + COMMAND_TYPE_BURN_TOKEN = 3; + COMMAND_TYPE_TRANSFER_OPERATORSHIP = 4; + COMMAND_TYPE_APPROVE_CONTRACT_CALL_WITH_MINT = 5; + COMMAND_TYPE_APPROVE_CONTRACT_CALL = 6; +} + +message Command { + bytes id = 1 [ + (gogoproto.nullable) = false, + (gogoproto.customname) = "ID", + (gogoproto.customtype) = "CommandID" + ]; + string command = 2 [ deprecated = true ]; + bytes params = 3; + string key_id = 4 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; + uint32 max_gas_cost = 5; + CommandType type = 6; +} + +enum BatchedCommandsStatus { + option (gogoproto.goproto_enum_prefix) = false; + + BATCHED_COMMANDS_STATUS_UNSPECIFIED = 0 + [ (gogoproto.enumvalue_customname) = "BatchNonExistent" ]; + BATCHED_COMMANDS_STATUS_SIGNING = 1 + [ (gogoproto.enumvalue_customname) = "BatchSigning" ]; + BATCHED_COMMANDS_STATUS_ABORTED = 2 + [ (gogoproto.enumvalue_customname) = "BatchAborted" ]; + BATCHED_COMMANDS_STATUS_SIGNED = 3 + [ (gogoproto.enumvalue_customname) = "BatchSigned" ]; +} + +message CommandBatchMetadata { + bytes id = 1 [ (gogoproto.customname) = "ID" ]; + repeated bytes command_ids = 2 [ + (gogoproto.nullable) = false, + (gogoproto.customname) = "CommandIDs", + (gogoproto.customtype) = "CommandID" + ]; + bytes data = 3; + bytes sig_hash = 4 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Hash" ]; + BatchedCommandsStatus status = 5; + string key_id = 6 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; + bytes prev_batched_commands_id = 7 + [ (gogoproto.customname) = "PrevBatchedCommandsID" ]; + google.protobuf.Any signature = 8 + [ (cosmos_proto.accepts_interface) = + "github.com/cosmos/codec/ProtoMarshaler" ]; +} + +// SigMetadata stores necessary information for external apps to map signature +// results to evm relay transaction types +message SigMetadata { + SigType type = 1; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + bytes command_batch_id = 3 [ (gogoproto.customname) = "CommandBatchID" ]; +} + +enum SigType { + option (gogoproto.goproto_enum_prefix) = false; + option (gogoproto.goproto_enum_stringer) = true; + + SIG_TYPE_UNSPECIFIED = 0 [ (gogoproto.enumvalue_customname) = "None" ]; + SIG_TYPE_TX = 1 [ (gogoproto.enumvalue_customname) = "SigTx" ]; + SIG_TYPE_COMMAND = 2 [ (gogoproto.enumvalue_customname) = "SigCommand" ]; +} + +// TransferKey contains information for a transfer operatorship +message TransferKey { + reserved 2; // type was deleted in v0.20 + + bytes tx_id = 1 [ + (gogoproto.nullable) = false, + (gogoproto.customtype) = "Hash", + (gogoproto.customname) = "TxID" + ]; + string next_key_id = 3 [ + (gogoproto.customname) = "NextKeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; +} + +enum DepositStatus { + option (gogoproto.goproto_enum_prefix) = true; + option (gogoproto.goproto_enum_stringer) = true; + + DEPOSIT_STATUS_UNSPECIFIED = 0 [ (gogoproto.enumvalue_customname) = "None" ]; + DEPOSIT_STATUS_PENDING = 1 [ (gogoproto.enumvalue_customname) = "Pending" ]; + DEPOSIT_STATUS_CONFIRMED = 2 + [ (gogoproto.enumvalue_customname) = "Confirmed" ]; + DEPOSIT_STATUS_BURNED = 3 [ (gogoproto.enumvalue_customname) = "Burned" ]; +} + +message Asset { + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string name = 2; +} + +message TokenDetails { + string token_name = 1; + string symbol = 2; + uint32 decimals = 3 [ (gogoproto.casttype) = "uint8" ]; + bytes capacity = 4 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; +} + +message Gateway { + reserved 2; // status was removed in v0.27 + + bytes address = 1 + [ (gogoproto.nullable) = false, (gogoproto.customtype) = "Address" ]; +} + +message PollMetadata { + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + bytes tx_id = 2 [ + (gogoproto.customname) = "TxID", + (gogoproto.nullable) = false, + (gogoproto.customtype) = "Hash" + ]; +} diff --git a/ampd/proto/axelar/multisig/exported/v1beta1/types.proto b/ampd/proto/axelar/multisig/exported/v1beta1/types.proto new file mode 100644 index 000000000..97014922f --- /dev/null +++ b/ampd/proto/axelar/multisig/exported/v1beta1/types.proto @@ -0,0 +1,28 @@ +syntax = "proto3"; +package axelar.multisig.exported.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/multisig/exported"; + +import "gogoproto/gogo.proto"; + +option (gogoproto.goproto_getters_all) = false; + +enum MultisigState { + option (gogoproto.goproto_enum_prefix) = false; + option (gogoproto.goproto_enum_stringer) = true; + + MULTISIG_STATE_UNSPECIFIED = 0 + [ (gogoproto.enumvalue_customname) = "NonExistent" ]; + MULTISIG_STATE_PENDING = 1 [ (gogoproto.enumvalue_customname) = "Pending" ]; + MULTISIG_STATE_COMPLETED = 2 + [ (gogoproto.enumvalue_customname) = "Completed" ]; +} + +enum KeyState { + option (gogoproto.goproto_enum_prefix) = false; + option (gogoproto.goproto_enum_stringer) = true; + + KEY_STATE_UNSPECIFIED = 0 [ (gogoproto.enumvalue_customname) = "Inactive" ]; + KEY_STATE_ASSIGNED = 1 [ (gogoproto.enumvalue_customname) = "Assigned" ]; + KEY_STATE_ACTIVE = 2 [ (gogoproto.enumvalue_customname) = "Active" ]; +} diff --git a/ampd/proto/axelar/multisig/v1beta1/events.proto b/ampd/proto/axelar/multisig/v1beta1/events.proto new file mode 100644 index 000000000..80607c48e --- /dev/null +++ b/ampd/proto/axelar/multisig/v1beta1/events.proto @@ -0,0 +1,127 @@ +syntax = "proto3"; +package axelar.multisig.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/multisig/types"; + +import "gogoproto/gogo.proto"; + +message KeygenStarted { + string module = 1; + string key_id = 2 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; + repeated bytes participants = 3 + [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.ValAddress" ]; +} + +message KeygenCompleted { + option (gogoproto.messagename) = true; + + string module = 1; + string key_id = 2 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; +} + +message KeygenExpired { + option (gogoproto.messagename) = true; + + string module = 1; + string key_id = 2 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; +} + +message PubKeySubmitted { + string module = 1; + string key_id = 2 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; + bytes participant = 3 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.ValAddress" ]; + bytes pub_key = 4 [ + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.PublicKey" + ]; +} + +message SigningStarted { + option (gogoproto.stable_marshaler) = true; + + string module = 1; + uint64 sig_id = 2 [ (gogoproto.customname) = "SigID" ]; + string key_id = 3 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; + map pub_keys = 4 [ + (gogoproto.castvalue) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.PublicKey" + ]; + bytes payload_hash = 5 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.Hash" ]; + string requesting_module = 6; +} + +message SigningCompleted { + string module = 1; + uint64 sig_id = 2 [ (gogoproto.customname) = "SigID" ]; +} + +message SigningExpired { + string module = 1; + uint64 sig_id = 2 [ (gogoproto.customname) = "SigID" ]; +} + +message SignatureSubmitted { + string module = 1; + uint64 sig_id = 2 [ (gogoproto.customname) = "SigID" ]; + bytes participant = 3 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.ValAddress" ]; + bytes signature = 4 [ (gogoproto.casttype) = "Signature" ]; +} + +message KeyAssigned { + string module = 1; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string key_id = 3 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; +} + +message KeyRotated { + string module = 1; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string key_id = 3 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; +} + +message KeygenOptOut { + bytes participant = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; +} + +message KeygenOptIn { + bytes participant = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; +} diff --git a/ampd/proto/axelar/multisig/v1beta1/genesis.proto b/ampd/proto/axelar/multisig/v1beta1/genesis.proto new file mode 100644 index 000000000..adbe82867 --- /dev/null +++ b/ampd/proto/axelar/multisig/v1beta1/genesis.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; +package axelar.multisig.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/multisig/types"; + +import "gogoproto/gogo.proto"; +import "axelar/multisig/v1beta1/params.proto"; +import "axelar/multisig/v1beta1/types.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// GenesisState represents the genesis state +message GenesisState { + Params params = 1 [ (gogoproto.nullable) = false ]; + + repeated KeygenSession keygen_sessions = 2 [ (gogoproto.nullable) = false ]; + repeated SigningSession signing_sessions = 3 [ (gogoproto.nullable) = false ]; + repeated Key keys = 4 [ (gogoproto.nullable) = false ]; + repeated KeyEpoch key_epochs = 5 [ (gogoproto.nullable) = false ]; +} diff --git a/ampd/proto/axelar/multisig/v1beta1/params.proto b/ampd/proto/axelar/multisig/v1beta1/params.proto new file mode 100644 index 000000000..da500f485 --- /dev/null +++ b/ampd/proto/axelar/multisig/v1beta1/params.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; +package axelar.multisig.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/multisig/types"; + +import "gogoproto/gogo.proto"; +import "axelar/utils/v1beta1/threshold.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// Params represent the genesis parameters for the module +message Params { + utils.v1beta1.Threshold keygen_threshold = 1 [ (gogoproto.nullable) = false ]; + utils.v1beta1.Threshold signing_threshold = 2 + [ (gogoproto.nullable) = false ]; + int64 keygen_timeout = 3; + int64 keygen_grace_period = 4; + int64 signing_timeout = 5; + int64 signing_grace_period = 6; + uint64 active_epoch_count = 7; +} diff --git a/ampd/proto/axelar/multisig/v1beta1/query.proto b/ampd/proto/axelar/multisig/v1beta1/query.proto new file mode 100644 index 000000000..d3b33c622 --- /dev/null +++ b/ampd/proto/axelar/multisig/v1beta1/query.proto @@ -0,0 +1,114 @@ +syntax = "proto3"; +package axelar.multisig.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/multisig/types"; + +import "gogoproto/gogo.proto"; +import "google/protobuf/timestamp.proto"; +import "axelar/multisig/exported/v1beta1/types.proto"; +import "axelar/multisig/v1beta1/types.proto"; +import "axelar/utils/v1beta1/threshold.proto"; +import "axelar/multisig/v1beta1/params.proto"; + +option (gogoproto.goproto_getters_all) = false; + +message KeyIDRequest { string chain = 1; } + +// KeyIDResponse contains the key ID of the key assigned to a given chain. +message KeyIDResponse { + string key_id = 1 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; +} + +message NextKeyIDRequest { string chain = 1; } + +// NextKeyIDResponse contains the key ID for the next rotation on the given +// chain +message NextKeyIDResponse { + string key_id = 1 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; +} + +message KeyRequest { + string key_id = 1 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; +} + +message KeygenParticipant { + string address = 1; + bytes weight = 2 [ + (gogoproto.nullable) = false, + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint" + ]; + string pub_key = 3; +} + +// KeyResponse contains the key corresponding to a given key id. +message KeyResponse { + string key_id = 1 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; + multisig.exported.v1beta1.KeyState state = 2; + int64 started_at = 3; + google.protobuf.Timestamp started_at_timestamp = 4 + [ (gogoproto.nullable) = false, (gogoproto.stdtime) = true ]; + bytes threshold_weight = 5 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + (gogoproto.nullable) = false + ]; + bytes bonded_weight = 6 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + (gogoproto.nullable) = false + ]; + // Keygen participants in descending order by weight + repeated KeygenParticipant participants = 7 [ (gogoproto.nullable) = false ]; +} + +message KeygenSessionRequest { + string key_id = 1 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; +} + +// KeygenSessionResponse contains the keygen session info for a given key ID. +message KeygenSessionResponse { + int64 started_at = 1; + google.protobuf.Timestamp started_at_timestamp = 2 + [ (gogoproto.nullable) = false, (gogoproto.stdtime) = true ]; + int64 expires_at = 3; + int64 completed_at = 4; + int64 grace_period = 5; + multisig.exported.v1beta1.MultisigState state = 6; + bytes keygen_threshold_weight = 7 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + (gogoproto.nullable) = false + ]; + bytes signing_threshold_weight = 8 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + (gogoproto.nullable) = false + ]; + bytes bonded_weight = 9 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + (gogoproto.nullable) = false + ]; + // Keygen candidates in descending order by weight + repeated KeygenParticipant participants = 10 [ (gogoproto.nullable) = false ]; +} + +// ParamsRequest represents a message that queries the params +message ParamsRequest {} + +message ParamsResponse { Params params = 1 [ (gogoproto.nullable) = false ]; } diff --git a/ampd/proto/axelar/multisig/v1beta1/service.proto b/ampd/proto/axelar/multisig/v1beta1/service.proto new file mode 100644 index 000000000..c7c39ae60 --- /dev/null +++ b/ampd/proto/axelar/multisig/v1beta1/service.proto @@ -0,0 +1,92 @@ +syntax = "proto3"; +package axelar.multisig.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/multisig/types"; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "axelar/multisig/v1beta1/tx.proto"; +import "axelar/multisig/v1beta1/query.proto"; + +option (gogoproto.goproto_registration) = true; + +// Msg defines the multisig Msg service. +service MsgService { + rpc StartKeygen(StartKeygenRequest) returns (StartKeygenResponse) { + option (google.api.http) = { + post : "/axelar/multisig/start_keygen" + body : "*" + }; + } + + rpc SubmitPubKey(SubmitPubKeyRequest) returns (SubmitPubKeyResponse) { + option (google.api.http) = { + post : "/axelar/multisig/submit_pub_key" + body : "*" + }; + } + + rpc SubmitSignature(SubmitSignatureRequest) + returns (SubmitSignatureResponse) { + option (google.api.http) = { + post : "/axelar/multisig/submit_signature" + body : "*" + }; + } + + rpc RotateKey(RotateKeyRequest) returns (RotateKeyResponse) { + option (google.api.http) = { + post : "/axelar/multisig/rotate_key" + body : "*" + }; + } + + rpc KeygenOptOut(KeygenOptOutRequest) returns (KeygenOptOutResponse) { + option (google.api.http) = { + post : "/axelar/multisig/v1beta1/keygen_opt_out" + body : "*" + }; + } + + rpc KeygenOptIn(KeygenOptInRequest) returns (KeygenOptInResponse) { + option (google.api.http) = { + post : "/axelar/multisig/v1beta1/keygen_opt_in" + body : "*" + }; + } +} + +// Query defines the gRPC querier service. +service QueryService { + // KeyID returns the key ID of a key assigned to a given chain. + // If no key is assigned, it returns the grpc NOT_FOUND error. + rpc KeyID(KeyIDRequest) returns (KeyIDResponse) { + option (google.api.http).get = "/axelar/multisig/v1beta1/key_id/{chain}"; + } + + // NextKeyID returns the key ID assigned for the next rotation on a given + // chain. If no key rotation is in progress, it returns the grpc NOT_FOUND + // error. + rpc NextKeyID(NextKeyIDRequest) returns (NextKeyIDResponse) { + option (google.api.http).get = + "/axelar/multisig/v1beta1/next_key_id/{chain}"; + } + + // Key returns the key corresponding to a given key ID. + // If no key is found, it returns the grpc NOT_FOUND error. + rpc Key(KeyRequest) returns (KeyResponse) { + option (google.api.http).get = "/axelar/multisig/v1beta1/key"; + } + + // KeygenSession returns the keygen session info for a given key ID. + // If no key is found, it returns the grpc NOT_FOUND error. + rpc KeygenSession(KeygenSessionRequest) returns (KeygenSessionResponse) { + option (google.api.http).get = "/axelar/multisig/v1beta1/keygen_session"; + } + + rpc Params(ParamsRequest) returns (ParamsResponse) { + option (google.api.http) = { + get : "/axelar/multisig/v1beta1/params" + }; + } +} diff --git a/ampd/proto/axelar/multisig/v1beta1/tx.proto b/ampd/proto/axelar/multisig/v1beta1/tx.proto new file mode 100644 index 000000000..f816389e3 --- /dev/null +++ b/ampd/proto/axelar/multisig/v1beta1/tx.proto @@ -0,0 +1,86 @@ +syntax = "proto3"; +package axelar.multisig.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/multisig/types"; +option (gogoproto.goproto_getters_all) = false; + +import "gogoproto/gogo.proto"; +import "axelar/permission/exported/v1beta1/types.proto"; + +message StartKeygenRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_CHAIN_MANAGEMENT; + + string sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string key_id = 2 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; +} + +message StartKeygenResponse {} + +message SubmitPubKeyRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + + string sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string key_id = 2 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; + bytes pub_key = 3 [ + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.PublicKey" + ]; + bytes signature = 4 [ (gogoproto.casttype) = "Signature" ]; +} + +message SubmitPubKeyResponse {} + +message SubmitSignatureRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + + string sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + uint64 sig_id = 2 [ (gogoproto.customname) = "SigID" ]; + bytes signature = 3 [ (gogoproto.casttype) = "Signature" ]; +} + +message SubmitSignatureResponse {} + +message RotateKeyRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_CHAIN_MANAGEMENT; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string key_id = 3 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; +} + +message RotateKeyResponse {} + +message KeygenOptOutRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; +} + +message KeygenOptOutResponse {} + +message KeygenOptInRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; +} + +message KeygenOptInResponse {} diff --git a/ampd/proto/axelar/multisig/v1beta1/types.proto b/ampd/proto/axelar/multisig/v1beta1/types.proto new file mode 100644 index 000000000..fe32c3da6 --- /dev/null +++ b/ampd/proto/axelar/multisig/v1beta1/types.proto @@ -0,0 +1,84 @@ +syntax = "proto3"; +package axelar.multisig.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/multisig/types"; + +import "google/protobuf/any.proto"; +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "axelar/utils/v1beta1/threshold.proto"; +import "axelar/snapshot/exported/v1beta1/types.proto"; +import "axelar/multisig/exported/v1beta1/types.proto"; + +message Key { + option (gogoproto.stable_marshaler) = true; + + string id = 1 [ + (gogoproto.customname) = "ID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; + snapshot.exported.v1beta1.Snapshot snapshot = 2 + [ (gogoproto.nullable) = false ]; + map pub_keys = 3 [ + (gogoproto.castvalue) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.PublicKey" + ]; + utils.v1beta1.Threshold signing_threshold = 4 + [ (gogoproto.nullable) = false ]; + multisig.exported.v1beta1.KeyState state = 5; +} + +message KeygenSession { + option (gogoproto.stable_marshaler) = true; + + Key key = 1 [ (gogoproto.nullable) = false ]; + multisig.exported.v1beta1.MultisigState state = 2; + utils.v1beta1.Threshold keygen_threshold = 3 [ (gogoproto.nullable) = false ]; + int64 expires_at = 4; + int64 completed_at = 5; + map is_pub_key_received = 6; + int64 grace_period = 7; +} + +message MultiSig { + option (gogoproto.stable_marshaler) = true; + + string key_id = 1 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; + bytes payload_hash = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.Hash" ]; + map sigs = 3 [ (gogoproto.castvalue) = "Signature" ]; +} + +message SigningSession { + option (gogoproto.stable_marshaler) = true; + + uint64 id = 1 [ (gogoproto.customname) = "ID" ]; + MultiSig multi_sig = 2 [ (gogoproto.nullable) = false ]; + multisig.exported.v1beta1.MultisigState state = 3; + Key key = 4 [ (gogoproto.nullable) = false ]; + int64 expires_at = 5; + int64 completed_at = 6; + int64 grace_period = 7; + string module = 8; + google.protobuf.Any module_metadata = 9 + [ (cosmos_proto.accepts_interface) = + "github.com/cosmos/codec/ProtoMarshaler" ]; +} + +message KeyEpoch { + uint64 epoch = 1; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string key_id = 3 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/multisig/exported.KeyID" + ]; +} diff --git a/ampd/proto/axelar/nexus/exported/v1beta1/types.proto b/ampd/proto/axelar/nexus/exported/v1beta1/types.proto new file mode 100644 index 000000000..f108c119f --- /dev/null +++ b/ampd/proto/axelar/nexus/exported/v1beta1/types.proto @@ -0,0 +1,129 @@ +syntax = "proto3"; +package axelar.nexus.exported.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/nexus/exported"; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "axelar/tss/exported/v1beta1/types.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// Chain represents the properties of a registered blockchain +message Chain { + reserved 2; // native_asset was removed in 0.14 + + string name = 1 [ (gogoproto.casttype) = "ChainName" ]; + bool supports_foreign_assets = 3; + tss.exported.v1beta1.KeyType key_type = 4; + string module = 5; +} + +// CrossChainAddress represents a generalized address on any registered chain +message CrossChainAddress { + Chain chain = 1 [ (gogoproto.nullable) = false ]; + string address = 2; +} + +// CrossChainTransfer represents a generalized transfer of some asset to a +// registered blockchain +message CrossChainTransfer { + CrossChainAddress recipient = 1 [ (gogoproto.nullable) = false ]; + cosmos.base.v1beta1.Coin asset = 2 [ (gogoproto.nullable) = false ]; + uint64 id = 3 + [ (gogoproto.customname) = "ID", (gogoproto.casttype) = "TransferID" ]; + TransferState state = 4; +} + +enum TransferState { + option (gogoproto.goproto_enum_prefix) = false; + option (gogoproto.goproto_enum_stringer) = true; + + TRANSFER_STATE_UNSPECIFIED = 0; + TRANSFER_STATE_PENDING = 1 [ (gogoproto.enumvalue_customname) = "Pending" ]; + TRANSFER_STATE_ARCHIVED = 2 [ (gogoproto.enumvalue_customname) = "Archived" ]; + TRANSFER_STATE_INSUFFICIENT_AMOUNT = 3 + [ (gogoproto.enumvalue_customname) = "InsufficientAmount" ]; +} + +// TransferFee represents accumulated fees generated by the network +message TransferFee { + repeated cosmos.base.v1beta1.Coin coins = 1 [ + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", + (gogoproto.nullable) = false + ]; +} + +message FeeInfo { + string chain = 1 [ (gogoproto.casttype) = "ChainName" ]; + string asset = 2; + bytes fee_rate = 3 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + bytes min_fee = 4 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + bytes max_fee = 5 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; +} + +message Asset { + string denom = 1; + reserved 2; // min_amount was removed in v0.15 + bool is_native_asset = 3; +} + +enum TransferDirection { + option (gogoproto.goproto_enum_prefix) = false; + + TRANSFER_DIRECTION_UNSPECIFIED = 0 + [ (gogoproto.enumvalue_customname) = "Unspecified" ]; + TRANSFER_DIRECTION_FROM = 1 + [ (gogoproto.enumvalue_customname) = "TransferDirectionFrom" ]; + TRANSFER_DIRECTION_TO = 2 + [ (gogoproto.enumvalue_customname) = "TransferDirectionTo" ]; +} + +message GeneralMessage { + enum Status { + option (gogoproto.goproto_enum_prefix) = false; + option (gogoproto.goproto_enum_stringer) = true; + + STATUS_UNSPECIFIED = 0 [ (gogoproto.enumvalue_customname) = "NonExistent" ]; + STATUS_APPROVED = 1 [ (gogoproto.enumvalue_customname) = "Approved" ]; + STATUS_PROCESSING = 2 [ (gogoproto.enumvalue_customname) = "Processing" ]; + STATUS_EXECUTED = 3 [ (gogoproto.enumvalue_customname) = "Executed" ]; + STATUS_FAILED = 4 [ (gogoproto.enumvalue_customname) = "Failed" ]; + } + + string id = 1 [ (gogoproto.customname) = "ID" ]; + CrossChainAddress sender = 2 [ (gogoproto.nullable) = false ]; + CrossChainAddress recipient = 3 [ (gogoproto.nullable) = false ]; + bytes payload_hash = 4; + Status status = 5; + cosmos.base.v1beta1.Coin asset = 6; + bytes source_tx_id = 7 [ (gogoproto.customname) = "SourceTxID" ]; + uint64 source_tx_index = 8; +} + +message WasmMessage { + string source_chain = 1 [ (gogoproto.casttype) = "ChainName" ]; + string source_address = 2; + string destination_chain = 3 [ (gogoproto.casttype) = "ChainName" ]; + string destination_address = 4; + bytes payload_hash = 5 [ (gogoproto.casttype) = "WasmBytes" ]; + bytes source_tx_id = 6 [ + (gogoproto.customname) = "SourceTxID", + (gogoproto.casttype) = "WasmBytes" + ]; + uint64 source_tx_index = 7 [ (gogoproto.jsontag) = "source_tx_index" ]; + bytes sender = 8 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string id = 9 [ + (gogoproto.customname) = "ID" + ]; +} diff --git a/ampd/proto/axelar/nexus/v1beta1/events.proto b/ampd/proto/axelar/nexus/v1beta1/events.proto new file mode 100644 index 000000000..7ec5ec829 --- /dev/null +++ b/ampd/proto/axelar/nexus/v1beta1/events.proto @@ -0,0 +1,66 @@ +syntax = "proto3"; +package axelar.nexus.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/nexus/types"; +option (gogoproto.messagename_all) = true; + +import "google/protobuf/duration.proto"; +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "axelar/nexus/exported/v1beta1/types.proto"; + +message FeeDeducted { + uint64 transfer_id = 1 [ + (gogoproto.customname) = "TransferID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.TransferID" + ]; + string recipient_chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string recipient_address = 3; + cosmos.base.v1beta1.Coin amount = 4 [ (gogoproto.nullable) = false ]; + cosmos.base.v1beta1.Coin fee = 5 [ (gogoproto.nullable) = false ]; +} + +message InsufficientFee { + uint64 transfer_id = 1 [ + (gogoproto.customname) = "TransferID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.TransferID" + ]; + string recipient_chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + string recipient_address = 3; + cosmos.base.v1beta1.Coin amount = 4 [ (gogoproto.nullable) = false ]; + cosmos.base.v1beta1.Coin fee = 5 [ (gogoproto.nullable) = false ]; +} + +message RateLimitUpdated { + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + cosmos.base.v1beta1.Coin limit = 2 [ (gogoproto.nullable) = false ]; + google.protobuf.Duration window = 3 + [ (gogoproto.stdduration) = true, (gogoproto.nullable) = false ]; +} + +message MessageReceived { + string id = 1 [ (gogoproto.customname) = "ID" ]; + bytes payload_hash = 2; + exported.v1beta1.CrossChainAddress sender = 3 + [ (gogoproto.nullable) = false ]; + exported.v1beta1.CrossChainAddress recipient = 4 + [ (gogoproto.nullable) = false ]; +} + +message MessageProcessing { string id = 1 [ (gogoproto.customname) = "ID" ]; } + +message MessageExecuted { string id = 1 [ (gogoproto.customname) = "ID" ]; } + +message MessageFailed { string id = 1 [ (gogoproto.customname) = "ID" ]; } + +message WasmMessageRouted { + exported.v1beta1.WasmMessage message = 1 [ (gogoproto.nullable) = false ]; +} diff --git a/ampd/proto/axelar/nexus/v1beta1/genesis.proto b/ampd/proto/axelar/nexus/v1beta1/genesis.proto new file mode 100644 index 000000000..78011ebd7 --- /dev/null +++ b/ampd/proto/axelar/nexus/v1beta1/genesis.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; +package axelar.nexus.v1beta1; +option go_package = "github.com/axelarnetwork/axelar-core/x/nexus/types"; + +import "gogoproto/gogo.proto"; +import "axelar/nexus/v1beta1/params.proto"; +import "axelar/nexus/exported/v1beta1/types.proto"; +import "axelar/nexus/v1beta1/types.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// GenesisState represents the genesis state +message GenesisState { + Params params = 1 [ (gogoproto.nullable) = false ]; + + uint64 nonce = 2; + repeated nexus.exported.v1beta1.Chain chains = 3 + [ (gogoproto.nullable) = false ]; + repeated ChainState chain_states = 4 [ (gogoproto.nullable) = false ]; + repeated LinkedAddresses linked_addresses = 5 + [ (gogoproto.nullable) = false ]; + repeated nexus.exported.v1beta1.CrossChainTransfer transfers = 6 + [ (gogoproto.nullable) = false ]; + nexus.exported.v1beta1.TransferFee fee = 7 [ (gogoproto.nullable) = false ]; + repeated nexus.exported.v1beta1.FeeInfo fee_infos = 8 + [ (gogoproto.nullable) = false ]; + repeated RateLimit rate_limits = 9 [ (gogoproto.nullable) = false ]; + repeated TransferEpoch transfer_epochs = 10 [ (gogoproto.nullable) = false ]; + repeated nexus.exported.v1beta1.GeneralMessage messages = 11 + [ (gogoproto.nullable) = false ]; + uint64 message_nonce = 12; +} diff --git a/ampd/proto/axelar/nexus/v1beta1/params.proto b/ampd/proto/axelar/nexus/v1beta1/params.proto new file mode 100644 index 000000000..30c9074fa --- /dev/null +++ b/ampd/proto/axelar/nexus/v1beta1/params.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; +package axelar.nexus.v1beta1; +option go_package = "github.com/axelarnetwork/axelar-core/x/nexus/types"; + +import "gogoproto/gogo.proto"; +import "axelar/utils/v1beta1/threshold.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// Params represent the genesis parameters for the module +message Params { + utils.v1beta1.Threshold chain_activation_threshold = 1 + [ (gogoproto.nullable) = false ]; + utils.v1beta1.Threshold chain_maintainer_missing_vote_threshold = 2 + [ (gogoproto.nullable) = false ]; + utils.v1beta1.Threshold chain_maintainer_incorrect_vote_threshold = 3 + [ (gogoproto.nullable) = false ]; + int32 chain_maintainer_check_window = 4; + bytes gateway = 5 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + uint64 end_blocker_limit = 6; +} diff --git a/ampd/proto/axelar/nexus/v1beta1/query.proto b/ampd/proto/axelar/nexus/v1beta1/query.proto new file mode 100644 index 000000000..b6371f4a8 --- /dev/null +++ b/ampd/proto/axelar/nexus/v1beta1/query.proto @@ -0,0 +1,175 @@ +syntax = "proto3"; +package axelar.nexus.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/nexus/types"; + +import "google/protobuf/duration.proto"; +import "gogoproto/gogo.proto"; +import "axelar/nexus/exported/v1beta1/types.proto"; +import "axelar/nexus/v1beta1/types.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "axelar/nexus/v1beta1/params.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// ChainMaintainersRequest represents a message that queries +// the chain maintainers for the specified chain +message ChainMaintainersRequest { string chain = 1; } + +message ChainMaintainersResponse { + repeated bytes maintainers = 1 + [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.ValAddress" ]; +} + +// LatestDepositAddressRequest represents a message that queries a deposit +// address by recipient address +message LatestDepositAddressRequest { + string recipient_addr = 1; + string recipient_chain = 2; + string deposit_chain = 3; +} + +message LatestDepositAddressResponse { string deposit_addr = 1; }; + +// TransfersForChainRequest represents a message that queries the +// transfers for the specified chain +message TransfersForChainRequest { + string chain = 1; + exported.v1beta1.TransferState state = 2; + cosmos.base.query.v1beta1.PageRequest pagination = 3; +} + +message TransfersForChainResponse { + repeated exported.v1beta1.CrossChainTransfer transfers = 1 + [ (gogoproto.nullable) = false ]; + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// FeeInfoRequest represents a message that queries the transfer fees associated +// to an asset on a chain +message FeeInfoRequest { + string chain = 1; + string asset = 2; +} + +message FeeInfoResponse { exported.v1beta1.FeeInfo fee_info = 1; } + +// TransferFeeRequest represents a message that queries the fees charged by +// the network for a cross-chain transfer +message TransferFeeRequest { + string source_chain = 1; + string destination_chain = 2; + string amount = 3; +} + +message TransferFeeResponse { + cosmos.base.v1beta1.Coin fee = 1 [ (gogoproto.nullable) = false ]; +} + +enum ChainStatus { + option (gogoproto.goproto_enum_prefix) = false; + + CHAIN_STATUS_UNSPECIFIED = 0 + [ (gogoproto.enumvalue_customname) = "Unspecified" ]; + CHAIN_STATUS_ACTIVATED = 1 [ (gogoproto.enumvalue_customname) = "Activated" ]; + CHAIN_STATUS_DEACTIVATED = 2 + [ (gogoproto.enumvalue_customname) = "Deactivated" ]; +} + +// ChainsRequest represents a message that queries the chains +// registered on the network +message ChainsRequest { ChainStatus status = 1; } + +message ChainsResponse { + repeated string chains = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; +} + +// AssetsRequest represents a message that queries the registered assets of a +// chain +message AssetsRequest { string chain = 1; } + +message AssetsResponse { repeated string assets = 1; } + +// ChainStateRequest represents a message that queries the state of a chain +// registered on the network +message ChainStateRequest { string chain = 1; } + +message ChainStateResponse { + ChainState state = 1 [ (gogoproto.nullable) = false ]; +} + +// ChainsByAssetRequest represents a message that queries the chains +// that support an asset on the network +message ChainsByAssetRequest { string asset = 1; } + +message ChainsByAssetResponse { + repeated string chains = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; +} + +// RecipientAddressRequest represents a message that queries the registered +// recipient address for a given deposit address +message RecipientAddressRequest { + string deposit_addr = 1; + string deposit_chain = 2; +} + +message RecipientAddressResponse { + string recipient_addr = 1; + string recipient_chain = 2; +}; + +// TransferRateLimitRequest represents a message that queries the registered +// transfer rate limit and current transfer amounts for a given chain and asset +message TransferRateLimitRequest { + string chain = 1; + string asset = 2; +} + +message TransferRateLimitResponse { TransferRateLimit transfer_rate_limit = 1; } + +message TransferRateLimit { + bytes limit = 1 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + google.protobuf.Duration window = 2 + [ (gogoproto.stdduration) = true, (gogoproto.nullable) = false ]; + bytes incoming = 3 [ + deprecated = true, + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + bytes outgoing = 4 [ + deprecated = true, + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + // time_left indicates the time left in the rate limit window + google.protobuf.Duration time_left = 5 + [ (gogoproto.stdduration) = true, (gogoproto.nullable) = false ]; + bytes from = 6 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + bytes to = 7 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; +} + +message MessageRequest { string id = 1 [ (gogoproto.customname) = "ID" ]; } + +message MessageResponse { + exported.v1beta1.GeneralMessage message = 1 [ (gogoproto.nullable) = false ]; +} + +// ParamsRequest represents a message that queries the params +message ParamsRequest {} + +message ParamsResponse { Params params = 1 [ (gogoproto.nullable) = false ]; } diff --git a/ampd/proto/axelar/nexus/v1beta1/service.proto b/ampd/proto/axelar/nexus/v1beta1/service.proto new file mode 100644 index 000000000..881d2a4d7 --- /dev/null +++ b/ampd/proto/axelar/nexus/v1beta1/service.proto @@ -0,0 +1,150 @@ +syntax = "proto3"; +package axelar.nexus.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/nexus/types"; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "axelar/nexus/v1beta1/tx.proto"; +import "axelar/nexus/v1beta1/query.proto"; + +option (gogoproto.goproto_registration) = true; + +// Msg defines the nexus Msg service. +service MsgService { + rpc RegisterChainMaintainer(RegisterChainMaintainerRequest) + returns (RegisterChainMaintainerResponse) { + option (google.api.http) = { + post : "/axelar/nexus/register_chain_maintainer" + body : "*" + }; + } + + rpc DeregisterChainMaintainer(DeregisterChainMaintainerRequest) + returns (DeregisterChainMaintainerResponse) { + option (google.api.http) = { + post : "/axelar/nexus/deregister_chain_maintainer" + body : "*" + }; + } + + rpc ActivateChain(ActivateChainRequest) returns (ActivateChainResponse) { + option (google.api.http) = { + post : "/axelar/nexus/activate_chain" + body : "*" + }; + } + + rpc DeactivateChain(axelar.nexus.v1beta1.DeactivateChainRequest) + returns (axelar.nexus.v1beta1.DeactivateChainResponse) { + option (google.api.http) = { + post : "/axelar/nexus/deactivate_chain" + body : "*" + }; + } + + rpc RegisterAssetFee(RegisterAssetFeeRequest) + returns (RegisterAssetFeeResponse) { + option (google.api.http) = { + post : "/axelar/nexus/register_asset_fee" + body : "*" + }; + } + + rpc SetTransferRateLimit(SetTransferRateLimitRequest) + returns (SetTransferRateLimitResponse) { + option (google.api.http) = { + post : "/axelar/nexus/set_transfer_rate_limit" + body : "*" + }; + } +} + +// QueryService defines the gRPC querier service. +service QueryService { + // LatestDepositAddress queries the a deposit address by recipient + rpc LatestDepositAddress(LatestDepositAddressRequest) + returns (LatestDepositAddressResponse) { + option (google.api.http).get = + "/axelar/nexus/v1beta1/latest_deposit_address/" + "{recipient_addr}/{recipient_chain}/{deposit_chain}"; + } + + // TransfersForChain queries transfers by chain + rpc TransfersForChain(TransfersForChainRequest) + returns (TransfersForChainResponse) { + option (google.api.http).get = + "/axelar/nexus/v1beta1/transfers_for_chain/{chain}/{state}"; + } + + // FeeInfo queries the fee info by chain and asset + rpc FeeInfo(FeeInfoRequest) returns (FeeInfoResponse) { + option (google.api.http) = { + get : "/axelar/nexus/v1beta1/fee_info/{chain}/{asset}" + additional_bindings : {get : "/axelar/nexus/v1beta1/fee"} + }; + } + + // TransferFee queries the transfer fee by the source, destination chain, + // and amount. If amount is 0, the min fee is returned + rpc TransferFee(TransferFeeRequest) returns (TransferFeeResponse) { + option (google.api.http) = { + get : "/axelar/nexus/v1beta1/transfer_fee/{source_chain}/" + "{destination_chain}/{amount}" + additional_bindings : {get : "/axelar/nexus/v1beta1/transfer_fee"} + }; + } + + // Chains queries the chains registered on the network + rpc Chains(ChainsRequest) returns (ChainsResponse) { + option (google.api.http).get = "/axelar/nexus/v1beta1/chains"; + } + + // Assets queries the assets registered for a chain + rpc Assets(AssetsRequest) returns (AssetsResponse) { + option (google.api.http).get = "/axelar/nexus/v1beta1/assets/{chain}"; + } + + // ChainState queries the state of a registered chain on the network + rpc ChainState(ChainStateRequest) returns (ChainStateResponse) { + option (google.api.http).get = "/axelar/nexus/v1beta1/chain_state/{chain}"; + } + + // ChainsByAsset queries the chains that support an asset on the network + rpc ChainsByAsset(ChainsByAssetRequest) returns (ChainsByAssetResponse) { + option (google.api.http).get = + "/axelar/nexus/v1beta1/chains_by_asset/{asset}"; + } + + // RecipientAddress queries the recipient address for a given deposit address + rpc RecipientAddress(RecipientAddressRequest) + returns (RecipientAddressResponse) { + option (google.api.http).get = "/axelar/nexus/v1beta1/recipient_address/" + "{deposit_chain}/{deposit_addr}"; + } + + // ChainMaintainers queries the chain maintainers for a given chain + rpc ChainMaintainers(ChainMaintainersRequest) + returns (ChainMaintainersResponse) { + option (google.api.http).get = + "/axelar/nexus/v1beta1/chain_maintainers/{chain}"; + } + + // TransferRateLimit queries the transfer rate limit for a given chain and + // asset. If a rate limit is not set, nil is returned. + rpc TransferRateLimit(TransferRateLimitRequest) + returns (TransferRateLimitResponse) { + option (google.api.http).get = "/axelar/nexus/v1beta1/transfer_rate_limit/" + "{chain}/{asset}"; + } + + rpc Message(MessageRequest) returns (MessageResponse) { + option (google.api.http).get = "/axelar/nexus/v1beta1/message"; + } + + rpc Params(ParamsRequest) returns (ParamsResponse) { + option (google.api.http) = { + get : "/axelar/nexus/v1beta1/params" + }; + } +} diff --git a/ampd/proto/axelar/nexus/v1beta1/tx.proto b/ampd/proto/axelar/nexus/v1beta1/tx.proto new file mode 100644 index 000000000..555930043 --- /dev/null +++ b/ampd/proto/axelar/nexus/v1beta1/tx.proto @@ -0,0 +1,87 @@ +syntax = "proto3"; +package axelar.nexus.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/nexus/types"; + +import "google/api/annotations.proto"; +import "google/protobuf/duration.proto"; +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "axelar/nexus/exported/v1beta1/types.proto"; +import "axelar/permission/exported/v1beta1/types.proto"; + +option (gogoproto.goproto_getters_all) = false; + +message RegisterChainMaintainerRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + repeated string chains = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; +} + +message RegisterChainMaintainerResponse {} + +message DeregisterChainMaintainerRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + repeated string chains = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; +} + +message DeregisterChainMaintainerResponse {} + +// ActivateChainRequest represents a message to activate chains +message ActivateChainRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_ACCESS_CONTROL; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + repeated string chains = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; +} + +message ActivateChainResponse {} + +// DeactivateChainRequest represents a message to deactivate chains +message DeactivateChainRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_ACCESS_CONTROL; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + repeated string chains = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; +} + +message DeactivateChainResponse {} + +// RegisterAssetFeeRequest represents a message to register the transfer fee +// info associated to an asset on a chain +message RegisterAssetFeeRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_CHAIN_MANAGEMENT; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + nexus.exported.v1beta1.FeeInfo fee_info = 2 [ (gogoproto.nullable) = false ]; +} + +message RegisterAssetFeeResponse {} + +// SetTransferRateLimitRequest represents a message to set rate limits on +// transfers +message SetTransferRateLimitRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_ACCESS_CONTROL; + + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + cosmos.base.v1beta1.Coin limit = 3 [ (gogoproto.nullable) = false ]; + google.protobuf.Duration window = 4 + [ (gogoproto.stdduration) = true, (gogoproto.nullable) = false ]; +} + +message SetTransferRateLimitResponse {} diff --git a/ampd/proto/axelar/nexus/v1beta1/types.proto b/ampd/proto/axelar/nexus/v1beta1/types.proto new file mode 100644 index 000000000..14503683f --- /dev/null +++ b/ampd/proto/axelar/nexus/v1beta1/types.proto @@ -0,0 +1,65 @@ +syntax = "proto3"; +package axelar.nexus.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/nexus/types"; + +import "google/protobuf/duration.proto"; +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "axelar/nexus/exported/v1beta1/types.proto"; +import "axelar/utils/v1beta1/bitmap.proto"; + +option (gogoproto.goproto_getters_all) = false; + +message MaintainerState { + bytes address = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.ValAddress" ]; + axelar.utils.v1beta1.Bitmap missing_votes = 2 + [ (gogoproto.nullable) = false ]; + axelar.utils.v1beta1.Bitmap incorrect_votes = 3 + [ (gogoproto.nullable) = false ]; + string chain = 4 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; +} + +// ChainState represents the state of a registered blockchain +message ChainState { + reserved 4; // total was removed in v0.13 + reserved 2; // maintainers was removed in v0.24 + + axelar.nexus.exported.v1beta1.Chain chain = 1 + [ (gogoproto.nullable) = false ]; + bool activated = 3; + repeated axelar.nexus.exported.v1beta1.Asset assets = 5 + [ (gogoproto.nullable) = false ]; + repeated MaintainerState maintainer_states = 6 + [ (gogoproto.nullable) = false, deprecated = true ]; +} + +message LinkedAddresses { + axelar.nexus.exported.v1beta1.CrossChainAddress deposit_address = 1 + [ (gogoproto.nullable) = false ]; + axelar.nexus.exported.v1beta1.CrossChainAddress recipient_address = 2 + [ (gogoproto.nullable) = false ]; +} + +message RateLimit { + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + cosmos.base.v1beta1.Coin limit = 2 [ (gogoproto.nullable) = false ]; + google.protobuf.Duration window = 3 + [ (gogoproto.stdduration) = true, (gogoproto.nullable) = false ]; +} + +message TransferEpoch { + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + cosmos.base.v1beta1.Coin amount = 2 [ (gogoproto.nullable) = false ]; + uint64 epoch = 3; + axelar.nexus.exported.v1beta1.TransferDirection direction = + 4; // indicates whether the rate tracking is for transfers going + // to that chain or coming from it +} diff --git a/ampd/proto/axelar/permission/exported/v1beta1/types.proto b/ampd/proto/axelar/permission/exported/v1beta1/types.proto new file mode 100644 index 000000000..7251daa8e --- /dev/null +++ b/ampd/proto/axelar/permission/exported/v1beta1/types.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; +package axelar.permission.exported.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/permission/exported"; + +import "gogoproto/gogo.proto"; +import "google/protobuf/descriptor.proto"; + +option (gogoproto.goproto_getters_all) = false; + +enum Role { + option (gogoproto.goproto_enum_prefix) = false; + option (gogoproto.goproto_enum_stringer) = true; + + ROLE_UNSPECIFIED = 0; + ROLE_UNRESTRICTED = 1; + ROLE_CHAIN_MANAGEMENT = 2; + ROLE_ACCESS_CONTROL = 3; +} + +extend google.protobuf.MessageOptions { + permission.exported.v1beta1.Role permission_role = + 50000; // 50000-99999 reserved for use withing individual organizations +} diff --git a/ampd/proto/axelar/permission/v1beta1/genesis.proto b/ampd/proto/axelar/permission/v1beta1/genesis.proto new file mode 100644 index 000000000..7eef9a82e --- /dev/null +++ b/ampd/proto/axelar/permission/v1beta1/genesis.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; +package axelar.permission.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/permission/types"; + +import "gogoproto/gogo.proto"; +import "cosmos/crypto/multisig/keys.proto"; +import "axelar/permission/v1beta1/types.proto"; +import "axelar/permission/v1beta1/params.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// GenesisState represents the genesis state +message GenesisState { + Params params = 1 [ (gogoproto.nullable) = false ]; + + cosmos.crypto.multisig.LegacyAminoPubKey governance_key = 2; + repeated GovAccount gov_accounts = 3 [ (gogoproto.nullable) = false ]; +} diff --git a/ampd/proto/axelar/permission/v1beta1/params.proto b/ampd/proto/axelar/permission/v1beta1/params.proto new file mode 100644 index 000000000..232900e1b --- /dev/null +++ b/ampd/proto/axelar/permission/v1beta1/params.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; +package axelar.permission.v1beta1; +option go_package = "github.com/axelarnetwork/axelar-core/x/permission/types"; + +import "gogoproto/gogo.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// Params represent the genesis parameters for the module +message Params {} diff --git a/ampd/proto/axelar/permission/v1beta1/query.proto b/ampd/proto/axelar/permission/v1beta1/query.proto new file mode 100644 index 000000000..997d72a8a --- /dev/null +++ b/ampd/proto/axelar/permission/v1beta1/query.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; +package axelar.permission.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/permission/types"; + +import "gogoproto/gogo.proto"; +import "google/protobuf/timestamp.proto"; +import "cosmos/crypto/multisig/keys.proto"; +import "axelar/permission/v1beta1/params.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// QueryGovernanceKeyRequest is the request type for the +// Query/GovernanceKey RPC method +message QueryGovernanceKeyRequest {} + +// QueryGovernanceKeyResponse is the response type for the +// Query/GovernanceKey RPC method +message QueryGovernanceKeyResponse { + cosmos.crypto.multisig.LegacyAminoPubKey governance_key = 1 + [ (gogoproto.nullable) = false ]; +} + +// ParamsRequest represents a message that queries the params +message ParamsRequest {} + +message ParamsResponse { Params params = 1 [ (gogoproto.nullable) = false ]; } diff --git a/ampd/proto/axelar/permission/v1beta1/service.proto b/ampd/proto/axelar/permission/v1beta1/service.proto new file mode 100644 index 000000000..02767573f --- /dev/null +++ b/ampd/proto/axelar/permission/v1beta1/service.proto @@ -0,0 +1,54 @@ +syntax = "proto3"; +package axelar.permission.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/permission/types"; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "axelar/permission/v1beta1/tx.proto"; +import "axelar/permission/v1beta1/query.proto"; + +option (gogoproto.goproto_registration) = true; + +// Msg defines the gov Msg service. +service Msg { + rpc RegisterController(axelar.permission.v1beta1.RegisterControllerRequest) + returns (axelar.permission.v1beta1.RegisterControllerResponse) { + option (google.api.http) = { + post : "/axelar/permission/register_controller" + body : "*" + }; + } + + rpc DeregisterController( + axelar.permission.v1beta1.DeregisterControllerRequest) + returns (axelar.permission.v1beta1.DeregisterControllerResponse) { + option (google.api.http) = { + post : "/axelar/permission/deregister_controller" + body : "*" + }; + } + + rpc UpdateGovernanceKey(axelar.permission.v1beta1.UpdateGovernanceKeyRequest) + returns (axelar.permission.v1beta1.UpdateGovernanceKeyResponse) { + option (google.api.http) = { + post : "/axelar/permission/update_governance_key" + body : "*" + }; + } +} + +// Query defines the gRPC querier service. +service Query { + // GovernanceKey returns the multisig governance key + rpc GovernanceKey(QueryGovernanceKeyRequest) + returns (QueryGovernanceKeyResponse) { + option (google.api.http).get = "/axelar/permission/v1beta1/governance_key"; + } + + rpc Params(ParamsRequest) returns (ParamsResponse) { + option (google.api.http) = { + get : "/axelar/permission/v1beta1/params" + }; + } +} diff --git a/ampd/proto/axelar/permission/v1beta1/tx.proto b/ampd/proto/axelar/permission/v1beta1/tx.proto new file mode 100644 index 000000000..c06b467f3 --- /dev/null +++ b/ampd/proto/axelar/permission/v1beta1/tx.proto @@ -0,0 +1,41 @@ +syntax = "proto3"; +package axelar.permission.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/permission/types"; + +import "gogoproto/gogo.proto"; +import "cosmos/crypto/multisig/keys.proto"; +import "axelar/permission/exported/v1beta1/types.proto"; + +option (gogoproto.goproto_getters_all) = false; + +message UpdateGovernanceKeyRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_ACCESS_CONTROL; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + cosmos.crypto.multisig.LegacyAminoPubKey governance_key = 2 + [ (gogoproto.nullable) = false ]; +} + +message UpdateGovernanceKeyResponse {} + +// MsgRegisterController represents a message to register a controller account +message RegisterControllerRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_ACCESS_CONTROL; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + bytes controller = 2 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; +} + +message RegisterControllerResponse {} + +// DeregisterController represents a message to deregister a controller account +message DeregisterControllerRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_ACCESS_CONTROL; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + bytes controller = 2 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; +} +message DeregisterControllerResponse {} diff --git a/ampd/proto/axelar/permission/v1beta1/types.proto b/ampd/proto/axelar/permission/v1beta1/types.proto new file mode 100644 index 000000000..beb401ed0 --- /dev/null +++ b/ampd/proto/axelar/permission/v1beta1/types.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; +package axelar.permission.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/permission/types"; + +import "gogoproto/gogo.proto"; +import "axelar/permission/exported/v1beta1/types.proto"; + +option (gogoproto.goproto_getters_all) = false; + +message GovAccount { + bytes address = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + permission.exported.v1beta1.Role role = 2; +} diff --git a/ampd/proto/axelar/reward/v1beta1/genesis.proto b/ampd/proto/axelar/reward/v1beta1/genesis.proto new file mode 100644 index 000000000..a805e07e9 --- /dev/null +++ b/ampd/proto/axelar/reward/v1beta1/genesis.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; +package axelar.reward.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/reward/types"; + +import "gogoproto/gogo.proto"; +import "axelar/reward/v1beta1/params.proto"; +import "axelar/reward/v1beta1/types.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// GenesisState represents the genesis state +message GenesisState { + Params params = 1 [ (gogoproto.nullable) = false ]; + + repeated Pool pools = 2 [ (gogoproto.nullable) = false ]; +} diff --git a/ampd/proto/axelar/reward/v1beta1/params.proto b/ampd/proto/axelar/reward/v1beta1/params.proto new file mode 100644 index 000000000..8fba73ecf --- /dev/null +++ b/ampd/proto/axelar/reward/v1beta1/params.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; +package axelar.reward.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/reward/types"; + +import "gogoproto/gogo.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// Params represent the genesis parameters for the module +message Params { + bytes external_chain_voting_inflation_rate = 1 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + bytes key_mgmt_relative_inflation_rate = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; +} diff --git a/ampd/proto/axelar/reward/v1beta1/query.proto b/ampd/proto/axelar/reward/v1beta1/query.proto new file mode 100644 index 000000000..f3f7225ad --- /dev/null +++ b/ampd/proto/axelar/reward/v1beta1/query.proto @@ -0,0 +1,28 @@ +syntax = "proto3"; +package axelar.reward.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/reward/types"; + +import "gogoproto/gogo.proto"; +import "axelar/reward/v1beta1/params.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// InflationRateRequest represents a message that queries the Axelar specific +// inflation RPC method. Ideally, this would use ValAddress as the validator +// field type. However, this makes it awkward for REST-based calls, because it +// would expect a byte array as part of the url. So, the bech32 encoded address +// string is used for this request instead. +message InflationRateRequest { string validator = 1; } + +message InflationRateResponse { + bytes inflation_rate = 1 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; +} + +// ParamsRequest represents a message that queries the params +message ParamsRequest {} + +message ParamsResponse { Params params = 1 [ (gogoproto.nullable) = false ]; } diff --git a/ampd/proto/axelar/reward/v1beta1/service.proto b/ampd/proto/axelar/reward/v1beta1/service.proto new file mode 100644 index 000000000..363f136e9 --- /dev/null +++ b/ampd/proto/axelar/reward/v1beta1/service.proto @@ -0,0 +1,42 @@ +syntax = "proto3"; +package axelar.reward.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/reward/types"; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "axelar/reward/v1beta1/tx.proto"; +import "axelar/reward/v1beta1/query.proto"; + +option (gogoproto.goproto_registration) = true; + +// Msg defines the axelarnet Msg service. +service MsgService { + rpc RefundMsg(RefundMsgRequest) returns (RefundMsgResponse) { + option (google.api.http) = { + post : "/axelar/reward/refund_message" + body : "*" + }; + } +} + +// QueryService defines the gRPC querier service. +service QueryService { + rpc InflationRate(InflationRateRequest) returns (InflationRateResponse) { + option (google.api.http) = { + get : "/axelar/reward/v1beta1/inflation_rate/{validator}", + additional_bindings : { + get : "/axelar/reward/v1beta1/inflation_rate" // query network inflation + // rate, without having to + // pass empty validator + // `.../inflation_rate//` + } + }; + } + + rpc Params(ParamsRequest) returns (ParamsResponse) { + option (google.api.http) = { + get : "/axelar/reward/v1beta1/params" + }; + } +} diff --git a/ampd/proto/axelar/reward/v1beta1/tx.proto b/ampd/proto/axelar/reward/v1beta1/tx.proto new file mode 100644 index 000000000..ec472dd52 --- /dev/null +++ b/ampd/proto/axelar/reward/v1beta1/tx.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; +package axelar.reward.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/reward/types"; + +import "google/protobuf/any.proto"; +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "axelar/permission/exported/v1beta1/types.proto"; + +option (gogoproto.goproto_getters_all) = false; + +message RefundMsgRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + google.protobuf.Any inner_message = 2 + [ (cosmos_proto.accepts_interface) = "Refundable" ]; +} + +message RefundMsgResponse { + bytes data = 1; + string log = 2; +} diff --git a/ampd/proto/axelar/reward/v1beta1/types.proto b/ampd/proto/axelar/reward/v1beta1/types.proto new file mode 100644 index 000000000..b025a95c0 --- /dev/null +++ b/ampd/proto/axelar/reward/v1beta1/types.proto @@ -0,0 +1,33 @@ +syntax = "proto3"; +package axelar.reward.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/reward/types"; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option (gogoproto.goproto_getters_all) = false; + +message Pool { + message Reward { + bytes validator = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.ValAddress" ]; + repeated cosmos.base.v1beta1.Coin coins = 2 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; + } + + string name = 1; + repeated Reward rewards = 2 [ (gogoproto.nullable) = false ]; +} + +message Refund { + bytes payer = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + + repeated cosmos.base.v1beta1.Coin fees = 2 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; +} diff --git a/ampd/proto/axelar/snapshot/exported/v1beta1/types.proto b/ampd/proto/axelar/snapshot/exported/v1beta1/types.proto new file mode 100644 index 000000000..80d4a4435 --- /dev/null +++ b/ampd/proto/axelar/snapshot/exported/v1beta1/types.proto @@ -0,0 +1,39 @@ +syntax = "proto3"; +package axelar.snapshot.exported.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/snapshot/exported"; + +import "gogoproto/gogo.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/any.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "axelar/tss/exported/v1beta1/types.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/staking/v1beta1/staking.proto"; + +option (gogoproto.goproto_getters_all) = false; + +message Participant { + bytes address = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.ValAddress" ]; + bytes weight = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + (gogoproto.nullable) = false + ]; +} + +message Snapshot { + reserved 1, 4, 5, 6, 7; // validators, total_share_count, counter and + // corruption_threshold were deleted in v0.26 + + option (gogoproto.stable_marshaler) = true; + + google.protobuf.Timestamp timestamp = 2 + [ (gogoproto.nullable) = false, (gogoproto.stdtime) = true ]; + int64 height = 3; + map participants = 8 [ (gogoproto.nullable) = false ]; + bytes bonded_weight = 9 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + (gogoproto.nullable) = false + ]; +} diff --git a/ampd/proto/axelar/snapshot/v1beta1/genesis.proto b/ampd/proto/axelar/snapshot/v1beta1/genesis.proto new file mode 100644 index 000000000..08d954526 --- /dev/null +++ b/ampd/proto/axelar/snapshot/v1beta1/genesis.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; +package axelar.snapshot.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/snapshot/types"; + +import "gogoproto/gogo.proto"; +import "axelar/snapshot/v1beta1/params.proto"; +import "axelar/snapshot/v1beta1/types.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// GenesisState represents the genesis state +message GenesisState { + Params params = 1 [ (gogoproto.nullable) = false ]; + + repeated ProxiedValidator proxied_validators = 2 + [ (gogoproto.nullable) = false ]; +} diff --git a/ampd/proto/axelar/snapshot/v1beta1/params.proto b/ampd/proto/axelar/snapshot/v1beta1/params.proto new file mode 100644 index 000000000..c22c60186 --- /dev/null +++ b/ampd/proto/axelar/snapshot/v1beta1/params.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; +package axelar.snapshot.v1beta1; +option go_package = "github.com/axelarnetwork/axelar-core/x/snapshot/types"; + +import "gogoproto/gogo.proto"; +import "google/protobuf/duration.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// Params represent the genesis parameters for the module +message Params { int64 min_proxy_balance = 1; } diff --git a/ampd/proto/axelar/snapshot/v1beta1/query.proto b/ampd/proto/axelar/snapshot/v1beta1/query.proto new file mode 100644 index 000000000..0e9f53def --- /dev/null +++ b/ampd/proto/axelar/snapshot/v1beta1/query.proto @@ -0,0 +1,35 @@ +syntax = "proto3"; +package axelar.snapshot.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/snapshot/types"; + +import "gogoproto/gogo.proto"; +import "axelar/snapshot/v1beta1/params.proto"; + +option (gogoproto.goproto_getters_all) = false; + +message QueryValidatorsResponse { + message TssIllegibilityInfo { + bool tombstoned = 1; + bool jailed = 2; + bool missed_too_many_blocks = 3; + bool no_proxy_registered = 4; + bool tss_suspended = 5; + bool proxy_insuficient_funds = 6; + bool stale_tss_heartbeat = 7; + } + + message Validator { + string operator_address = 1; + string moniker = 2; + TssIllegibilityInfo tss_illegibility_info = 3 + [ (gogoproto.nullable) = false ]; + } + + repeated Validator validators = 1; +} + +// ParamsRequest represents a message that queries the params +message ParamsRequest {} + +message ParamsResponse { Params params = 1 [ (gogoproto.nullable) = false ]; } diff --git a/ampd/proto/axelar/snapshot/v1beta1/service.proto b/ampd/proto/axelar/snapshot/v1beta1/service.proto new file mode 100644 index 000000000..82f430fd9 --- /dev/null +++ b/ampd/proto/axelar/snapshot/v1beta1/service.proto @@ -0,0 +1,41 @@ +syntax = "proto3"; +package axelar.snapshot.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/snapshot/types"; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "axelar/snapshot/v1beta1/tx.proto"; +import "axelar/snapshot/v1beta1/query.proto"; + +option (gogoproto.goproto_registration) = true; + +// Msg defines the snapshot Msg service. +service MsgService { + // RegisterProxy defines a method for registering a proxy account that can act + // in a validator account's stead. + rpc RegisterProxy(RegisterProxyRequest) returns (RegisterProxyResponse) { + option (google.api.http) = { + post : "/axelar/snapshot/register_proxy" + body : "*" + }; + } + + // DeactivateProxy defines a method for deregistering a proxy account. + rpc DeactivateProxy(DeactivateProxyRequest) + returns (DeactivateProxyResponse) { + option (google.api.http) = { + post : "/axelar/snapshot/deactivate_proxy" + body : "*" + }; + } +} + +// QueryService defines the gRPC querier service. +service QueryService { + rpc Params(ParamsRequest) returns (ParamsResponse) { + option (google.api.http) = { + get : "/axelar/snapshot/v1beta1/params" + }; + } +} diff --git a/ampd/proto/axelar/snapshot/v1beta1/tx.proto b/ampd/proto/axelar/snapshot/v1beta1/tx.proto new file mode 100644 index 000000000..70a1c2c0e --- /dev/null +++ b/ampd/proto/axelar/snapshot/v1beta1/tx.proto @@ -0,0 +1,28 @@ +syntax = "proto3"; +package axelar.snapshot.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/snapshot/types"; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "axelar/permission/exported/v1beta1/types.proto"; + +option (gogoproto.goproto_getters_all) = false; + +message RegisterProxyRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.ValAddress" ]; + bytes proxy_addr = 2 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; +} + +message RegisterProxyResponse {} + +message DeactivateProxyRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.ValAddress" ]; +} + +message DeactivateProxyResponse {} diff --git a/ampd/proto/axelar/snapshot/v1beta1/types.proto b/ampd/proto/axelar/snapshot/v1beta1/types.proto new file mode 100644 index 000000000..279c6e6fd --- /dev/null +++ b/ampd/proto/axelar/snapshot/v1beta1/types.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; +package axelar.snapshot.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/snapshot/types"; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; + +option (gogoproto.goproto_getters_all) = false; + +message ProxiedValidator { + bytes validator = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.ValAddress" ]; + bytes proxy = 2 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + bool active = 3; +} diff --git a/ampd/proto/axelar/tss/exported/v1beta1/types.proto b/ampd/proto/axelar/tss/exported/v1beta1/types.proto new file mode 100644 index 000000000..4b0d8bf1d --- /dev/null +++ b/ampd/proto/axelar/tss/exported/v1beta1/types.proto @@ -0,0 +1,68 @@ +syntax = "proto3"; +package axelar.tss.exported.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/tss/exported"; + +import "google/protobuf/any.proto"; +import "google/protobuf/timestamp.proto"; +import "cosmos_proto/cosmos.proto"; +import "axelar/utils/v1beta1/threshold.proto"; +import "gogoproto/gogo.proto"; + +// KeyRequirement defines requirements for keys +message KeyRequirement { + KeyRole key_role = 1; + KeyType key_type = 2; + utils.v1beta1.Threshold min_keygen_threshold = 3 + [ (gogoproto.nullable) = false ]; + utils.v1beta1.Threshold safety_threshold = 4 [ (gogoproto.nullable) = false ]; + KeyShareDistributionPolicy key_share_distribution_policy = 5; + int64 max_total_share_count = 6; + int64 min_total_share_count = 7; + utils.v1beta1.Threshold keygen_voting_threshold = 8 + [ (gogoproto.nullable) = false ]; + utils.v1beta1.Threshold sign_voting_threshold = 9 + [ (gogoproto.nullable) = false ]; + int64 keygen_timeout = 10; + int64 sign_timeout = 11; +} + +enum KeyRole { + option (gogoproto.goproto_enum_prefix) = false; + option (gogoproto.goproto_enum_stringer) = true; + + KEY_ROLE_UNSPECIFIED = 0 [ (gogoproto.enumvalue_customname) = "Unknown" ]; + KEY_ROLE_MASTER_KEY = 1 [ (gogoproto.enumvalue_customname) = "MasterKey" ]; + KEY_ROLE_SECONDARY_KEY = 2 + [ (gogoproto.enumvalue_customname) = "SecondaryKey" ]; + KEY_ROLE_EXTERNAL_KEY = 3 + [ (gogoproto.enumvalue_customname) = "ExternalKey" ]; +} + +enum KeyType { + option (gogoproto.goproto_enum_prefix) = false; + option (gogoproto.goproto_enum_stringer) = true; + + KEY_TYPE_UNSPECIFIED = 0; + KEY_TYPE_NONE = 1 [ (gogoproto.enumvalue_customname) = "None" ]; + KEY_TYPE_THRESHOLD = 2 [ (gogoproto.enumvalue_customname) = "Threshold" ]; + KEY_TYPE_MULTISIG = 3 [ (gogoproto.enumvalue_customname) = "Multisig" ]; +} + +enum KeyShareDistributionPolicy { + option (gogoproto.goproto_enum_prefix) = false; + option (gogoproto.goproto_enum_stringer) = true; + + KEY_SHARE_DISTRIBUTION_POLICY_UNSPECIFIED = 0 + [ (gogoproto.enumvalue_customname) = "Unspecified" ]; + KEY_SHARE_DISTRIBUTION_POLICY_WEIGHTED_BY_STAKE = 1 + [ (gogoproto.enumvalue_customname) = "WeightedByStake" ]; + KEY_SHARE_DISTRIBUTION_POLICY_ONE_PER_VALIDATOR = 2 + [ (gogoproto.enumvalue_customname) = "OnePerValidator" ]; +} + +// PubKeyInfo holds a pubkey and a signature +message SigKeyPair { + bytes pub_key = 1; + bytes signature = 2; +} diff --git a/ampd/proto/axelar/tss/tofnd/v1beta1/common.proto b/ampd/proto/axelar/tss/tofnd/v1beta1/common.proto new file mode 100644 index 000000000..0683c5189 --- /dev/null +++ b/ampd/proto/axelar/tss/tofnd/v1beta1/common.proto @@ -0,0 +1,28 @@ +// File copied from golang tofnd with minor tweaks +syntax = "proto3"; + +option go_package = "github.com/axelarnetwork/axelar-core/x/tss/tofnd"; + +import "gogoproto/gogo.proto"; + +package axelar.tss.tofnd.v1beta1; + +// Key presence check types +message KeyPresenceRequest { + string key_uid = 1; + bytes pub_key = 2; // SEC1-encoded compressed pub key bytes to find the right + // mnemonic. Latest is used, if empty. +} + +message KeyPresenceResponse { + enum Response { + option (gogoproto.goproto_enum_prefix) = false; + + RESPONSE_UNSPECIFIED = 0; + RESPONSE_PRESENT = 1; + RESPONSE_ABSENT = 2; + RESPONSE_FAIL = 3; + } + + Response response = 1; +} diff --git a/ampd/proto/axelar/tss/tofnd/v1beta1/multisig.proto b/ampd/proto/axelar/tss/tofnd/v1beta1/multisig.proto new file mode 100644 index 000000000..82abaaf56 --- /dev/null +++ b/ampd/proto/axelar/tss/tofnd/v1beta1/multisig.proto @@ -0,0 +1,40 @@ +// File copied from golang tofnd with minor tweaks +syntax = "proto3"; + +option go_package = "github.com/axelarnetwork/axelar-core/x/tss/tofnd"; +import "axelar/tss/tofnd/v1beta1/common.proto"; // import key presence request/response + +package axelar.tss.tofnd.v1beta1; + +// service Multisig { +// rpc KeyPresence(KeyPresenceRequest) returns (KeyPresenceResponse); +// rpc Keygen(KeygenRequest) returns (KeygenResponse); +// rpc Sign(SignRequest) returns (SignResponse); +//} + +message KeygenRequest { + string key_uid = 1; + string party_uid = 2; // used only for logging +} + +message KeygenResponse { + oneof keygen_response { + bytes pub_key = 1; // SEC1-encoded compressed curve point + string error = 2; // reply with an error message if keygen fails + } +} + +message SignRequest { + string key_uid = 1; + bytes msg_to_sign = 2; // 32-byte pre-hashed message digest + string party_uid = 3; // used only for logging + bytes pub_key = 4; // SEC1-encoded compressed pub key bytes to find the right + // mnemonic. Latest is used, if empty. +} + +message SignResponse { + oneof sign_response { + bytes signature = 1; // ASN.1 DER-encoded ECDSA signature + string error = 2; // reply with an error message if sign fails + } +} diff --git a/ampd/proto/axelar/tss/tofnd/v1beta1/tofnd.proto b/ampd/proto/axelar/tss/tofnd/v1beta1/tofnd.proto new file mode 100644 index 000000000..96140268a --- /dev/null +++ b/ampd/proto/axelar/tss/tofnd/v1beta1/tofnd.proto @@ -0,0 +1,129 @@ +// File copied from golang tofnd with minor tweaks +syntax = "proto3"; + +option go_package = "github.com/axelarnetwork/axelar-core/x/tss/tofnd"; +import "gogoproto/gogo.proto"; +import "axelar/tss/tofnd/v1beta1/common.proto"; // import key presence request/response + +package axelar.tss.tofnd.v1beta1; + +// TODO: figure out why gogoproto produces unusable services +// GG20 is the protocol https://eprint.iacr.org/2020/540 +// rpc definitions intended to wrap the API for this library: +// https://github.com/axelarnetwork/tofn +// service GG20 { +// rpc Recover(RecoverRequest) returns (RecoverResponse); +// rpc Keygen(stream MessageIn) returns (stream MessageOut); +// rpc Sign(stream MessageIn) returns (stream MessageOut); +// rpc KeyPresence(KeyPresenceRequest) returns (KeyPresenceResponse); +//} + +message RecoverRequest { + KeygenInit keygen_init = 1; + KeygenOutput keygen_output = 2; +} +message RecoverResponse { + enum Response { + RESPONSE_UNSPECIFIED = 0; + RESPONSE_SUCCESS = 1; + RESPONSE_FAIL = 2; + } + Response response = 1; +} + +// Keygen's success response +message KeygenOutput { + bytes pub_key = 1; // pub_key; common for all parties + bytes group_recover_info = + 2; // recover info of all parties' shares; common for all parties + bytes private_recover_info = + 3; // private recover info of this party's shares; unique for each party +} + +// generic message types shared by Keygen, Sign + +// TODO use nested message types +// eg. KeygenInit, SignInit should be defined inside MessageIn, etc. + +message MessageIn { + oneof data { // TODO don't reuse `data` + KeygenInit keygen_init = 1; // first message only, Keygen + SignInit sign_init = 2; // first message only, Sign + TrafficIn traffic = 3; // all subsequent messages + bool abort = 4; // abort the protocol, ignore the bool value + } +} + +message MessageOut { + oneof data { // TODO don't reuse `data` + TrafficOut traffic = 1; // all but final message + KeygenResult keygen_result = 2; // final message only, Keygen + SignResult sign_result = 3; // final message only, Sign + bool need_recover = 4; // issue recover from client + } + + // Keygen's response types + message KeygenResult { + oneof keygen_result_data { + KeygenOutput data = 1; // Success response + CriminalList criminals = 2; // Faiilure response + } + } + + // Sign's response types + message SignResult { + oneof sign_result_data { + bytes signature = 1; // Success response + CriminalList criminals = 2; // Failure response + } + } + + // Keygen/Sign failure response message + message CriminalList { + repeated Criminal criminals = 1; + + message Criminal { + string party_uid = 1; + + enum CrimeType { + option (gogoproto.goproto_enum_prefix) = false; + + CRIME_TYPE_UNSPECIFIED = 0; + CRIME_TYPE_NON_MALICIOUS = 1; + CRIME_TYPE_MALICIOUS = 2; + } + CrimeType crime_type = 2; + } + } +} + +message TrafficIn { + string from_party_uid = 1; + bytes payload = 2; + bool is_broadcast = 3; +} + +message TrafficOut { + string to_party_uid = 1; + bytes payload = 2; + bool is_broadcast = 3; +} + +// Keygen-specific message types + +message KeygenInit { + string new_key_uid = 1; + repeated string party_uids = 2; + repeated uint32 party_share_counts = 5; + uint32 my_party_index = 3; // parties[my_party_index] belongs to the server + uint32 threshold = 4; +} + +// Sign-specific message types + +message SignInit { + string new_sig_uid = 1; + string key_uid = 2; + repeated string party_uids = 3; // TODO replace this with a subset of indices? + bytes message_to_sign = 4; +} diff --git a/ampd/proto/axelar/tss/v1beta1/genesis.proto b/ampd/proto/axelar/tss/v1beta1/genesis.proto new file mode 100644 index 000000000..1e411bb56 --- /dev/null +++ b/ampd/proto/axelar/tss/v1beta1/genesis.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; +package axelar.tss.v1beta1; +option go_package = "github.com/axelarnetwork/axelar-core/x/tss/types"; + +import "gogoproto/gogo.proto"; +import "axelar/tss/v1beta1/params.proto"; +import "axelar/tss/v1beta1/types.proto"; +import "axelar/tss/exported/v1beta1/types.proto"; + +option (gogoproto.goproto_getters_all) = false; + +message GenesisState { Params params = 1 [ (gogoproto.nullable) = false ]; } diff --git a/ampd/proto/axelar/tss/v1beta1/params.proto b/ampd/proto/axelar/tss/v1beta1/params.proto new file mode 100644 index 000000000..ab44ac748 --- /dev/null +++ b/ampd/proto/axelar/tss/v1beta1/params.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; + +package axelar.tss.v1beta1; +option go_package = "github.com/axelarnetwork/axelar-core/x/tss/types"; + +import "gogoproto/gogo.proto"; +import "axelar/utils/v1beta1/threshold.proto"; +import "axelar/tss/exported/v1beta1/types.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// Params is the parameter set for this module +message Params { + // KeyRequirements defines the requirement for each key role + repeated tss.exported.v1beta1.KeyRequirement key_requirements = 1 + [ (gogoproto.nullable) = false ]; + // SuspendDurationInBlocks defines the number of blocks a + // validator is disallowed to participate in any TSS ceremony after + // committing a malicious behaviour during signing + int64 suspend_duration_in_blocks = 2; + // HeartBeatPeriodInBlocks defines the time period in blocks for tss to + // emit the event asking validators to send their heartbeats + int64 heartbeat_period_in_blocks = 3; + utils.v1beta1.Threshold max_missed_blocks_per_window = 4 + [ (gogoproto.nullable) = false ]; + int64 unbonding_locking_key_rotation_count = 5; + utils.v1beta1.Threshold external_multisig_threshold = 6 + [ (gogoproto.nullable) = false ]; + int64 max_sign_queue_size = 7; + int64 max_simultaneous_sign_shares = 8; + int64 tss_signed_blocks_window = 9; +} diff --git a/ampd/proto/axelar/tss/v1beta1/query.proto b/ampd/proto/axelar/tss/v1beta1/query.proto new file mode 100644 index 000000000..748cfefee --- /dev/null +++ b/ampd/proto/axelar/tss/v1beta1/query.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; +package axelar.tss.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/tss/types"; + +import "gogoproto/gogo.proto"; +import "axelar/tss/v1beta1/params.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// ParamsRequest represents a message that queries the params +message ParamsRequest {} + +message ParamsResponse { Params params = 1 [ (gogoproto.nullable) = false ]; } diff --git a/ampd/proto/axelar/tss/v1beta1/service.proto b/ampd/proto/axelar/tss/v1beta1/service.proto new file mode 100644 index 000000000..01dfce6ec --- /dev/null +++ b/ampd/proto/axelar/tss/v1beta1/service.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; +package axelar.tss.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/tss/types"; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "axelar/snapshot/v1beta1/tx.proto"; +import "axelar/tss/v1beta1/tx.proto"; +import "axelar/tss/v1beta1/query.proto"; + +option (gogoproto.goproto_registration) = true; + +// Msg defines the tss Msg service. +service MsgService { + rpc HeartBeat(axelar.tss.v1beta1.HeartBeatRequest) + returns (axelar.tss.v1beta1.HeartBeatResponse) { + option (google.api.http) = { + post : "/axelar/tss/heartbeat" + body : "*" + }; + } +} + +// Query defines the gRPC querier service. +service QueryService { + rpc Params(ParamsRequest) returns (ParamsResponse) { + option (google.api.http) = { + get : "/axelar/tss/v1beta1/params" + }; + } +} diff --git a/ampd/proto/axelar/tss/v1beta1/tx.proto b/ampd/proto/axelar/tss/v1beta1/tx.proto new file mode 100644 index 000000000..2cc6981d1 --- /dev/null +++ b/ampd/proto/axelar/tss/v1beta1/tx.proto @@ -0,0 +1,147 @@ +syntax = "proto3"; +package axelar.tss.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/tss/types"; + +import "gogoproto/gogo.proto"; +import "axelar/tss/exported/v1beta1/types.proto"; +import "axelar/tss/v1beta1/types.proto"; +import "axelar/tss/tofnd/v1beta1/tofnd.proto"; +import "axelar/vote/exported/v1beta1/types.proto"; +import "cosmos/crypto/multisig/keys.proto"; +import "axelar/permission/exported/v1beta1/types.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// StartKeygenRequest indicate the start of keygen +message StartKeygenRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_CHAIN_MANAGEMENT; + string sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + tss.v1beta1.KeyInfo key_info = 2 [ (gogoproto.nullable) = false ]; +} + +message StartKeygenResponse {} + +message RotateKeyRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_CHAIN_MANAGEMENT; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + tss.exported.v1beta1.KeyRole key_role = 3; + string key_id = 4 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/tss/exported.KeyID" + ]; +} + +message RotateKeyResponse {} + +// ProcessKeygenTrafficRequest protocol message +message ProcessKeygenTrafficRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string session_id = 2 [ (gogoproto.customname) = "SessionID" ]; + tss.tofnd.v1beta1.TrafficOut payload = 3 [ (gogoproto.nullable) = false ]; +} + +message ProcessKeygenTrafficResponse {} + +// ProcessSignTrafficRequest protocol message +message ProcessSignTrafficRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string session_id = 2 [ (gogoproto.customname) = "SessionID" ]; + tss.tofnd.v1beta1.TrafficOut payload = 3 [ (gogoproto.nullable) = false ]; +} + +message ProcessSignTrafficResponse {} + +// VotePubKeyRequest represents the message to vote on a public key +message VotePubKeyRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + vote.exported.v1beta1.PollKey poll_key = 2 [ (gogoproto.nullable) = false ]; + tss.tofnd.v1beta1.MessageOut.KeygenResult result = 3 + [ (gogoproto.nullable) = false ]; +} +message VotePubKeyResponse { string log = 1; } + +// VoteSigRequest represents a message to vote for a signature +message VoteSigRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + vote.exported.v1beta1.PollKey poll_key = 2 [ (gogoproto.nullable) = false ]; + tss.tofnd.v1beta1.MessageOut.SignResult result = 3 + [ (gogoproto.nullable) = false ]; +} + +message VoteSigResponse { string log = 1; } + +message HeartBeatRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + + repeated string key_ids = 2 [ + (gogoproto.customname) = "KeyIDs", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/tss/exported.KeyID" + ]; +} + +message HeartBeatResponse {} + +message RegisterExternalKeysRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_CHAIN_MANAGEMENT; + message ExternalKey { + string id = 1 [ + (gogoproto.customname) = "ID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/tss/exported.KeyID" + ]; + bytes pub_key = 2; + } + + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string chain = 2 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + repeated ExternalKey external_keys = 3 [ (gogoproto.nullable) = false ]; +} + +message RegisterExternalKeysResponse {}; + +message SubmitMultisigPubKeysRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string key_id = 2 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/tss/exported.KeyID" + ]; + repeated exported.v1beta1.SigKeyPair sig_key_pairs = 3 + [ (gogoproto.nullable) = false ]; +} + +message SubmitMultisigPubKeysResponse {} + +message SubmitMultisigSignaturesRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string sig_id = 2 [ (gogoproto.customname) = "SigID" ]; + + repeated bytes signatures = 3; +} + +message SubmitMultisigSignaturesResponse {} diff --git a/ampd/proto/axelar/tss/v1beta1/types.proto b/ampd/proto/axelar/tss/v1beta1/types.proto new file mode 100644 index 000000000..86fd8dae2 --- /dev/null +++ b/ampd/proto/axelar/tss/v1beta1/types.proto @@ -0,0 +1,63 @@ +syntax = "proto3"; +package axelar.tss.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/tss/types"; + +import "gogoproto/gogo.proto"; +import "axelar/tss/exported/v1beta1/types.proto"; + +message KeygenVoteData { + bytes pub_key = 1; + bytes group_recovery_info = 2; +} + +// KeyInfo holds information about a key +message KeyInfo { + string key_id = 1 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/tss/exported.KeyID" + ]; + tss.exported.v1beta1.KeyRole key_role = 2; + tss.exported.v1beta1.KeyType key_type = 3; +} + +message MultisigInfo { + message Info { + bytes participant = 1 + [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.ValAddress" ]; + repeated bytes data = 2; + } + string id = 1 [ (gogoproto.customname) = "ID" ]; + int64 timeout = 2; + int64 target_num = 3; + repeated Info infos = 4; +} + +message KeyRecoveryInfo { + string key_id = 1 [ + (gogoproto.customname) = "KeyID", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/tss/exported.KeyID" + ]; + bytes public = 2; + map private = 3 [ (gogoproto.nullable) = false ]; +} + +message ExternalKeys { + string chain = 1 + [ (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/nexus/exported.ChainName" ]; + repeated string key_ids = 2 [ + (gogoproto.customname) = "KeyIDs", + (gogoproto.casttype) = + "github.com/axelarnetwork/axelar-core/x/tss/exported.KeyID" + ]; +} + +message ValidatorStatus { + bytes validator = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.ValAddress" ]; + uint64 suspended_until = 2; +} diff --git a/ampd/proto/axelar/utils/v1beta1/bitmap.proto b/ampd/proto/axelar/utils/v1beta1/bitmap.proto new file mode 100644 index 000000000..4f9c714c2 --- /dev/null +++ b/ampd/proto/axelar/utils/v1beta1/bitmap.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; +package axelar.utils.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/utils"; + +import "gogoproto/gogo.proto"; + +option (gogoproto.goproto_getters_all) = false; + +message Bitmap { CircularBuffer true_count_cache = 2; } + +message CircularBuffer { + repeated uint64 cumulative_value = 1; + int32 index = 2; + int32 max_size = 3; +} diff --git a/ampd/proto/axelar/utils/v1beta1/queuer.proto b/ampd/proto/axelar/utils/v1beta1/queuer.proto new file mode 100644 index 000000000..663d9b5a5 --- /dev/null +++ b/ampd/proto/axelar/utils/v1beta1/queuer.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; +package axelar.utils.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/utils"; + +import "gogoproto/gogo.proto"; + +option (gogoproto.goproto_getters_all) = false; + +message QueueState { + option (gogoproto.stable_marshaler) = true; + + message Item { + bytes key = 1; + bytes value = 2; + } + + map items = 1 [ (gogoproto.nullable) = false ]; +} diff --git a/ampd/proto/axelar/utils/v1beta1/threshold.proto b/ampd/proto/axelar/utils/v1beta1/threshold.proto new file mode 100644 index 000000000..3d0f17a70 --- /dev/null +++ b/ampd/proto/axelar/utils/v1beta1/threshold.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; +package axelar.utils.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/utils"; + +import "gogoproto/gogo.proto"; + +option (gogoproto.goproto_getters_all) = false; + +message Threshold { + option (gogoproto.goproto_stringer) = false; + // split threshold into Numerator and denominator to avoid floating point + // errors down the line + int64 numerator = 1; + int64 denominator = 2; +} diff --git a/ampd/proto/axelar/vote/exported/v1beta1/types.proto b/ampd/proto/axelar/vote/exported/v1beta1/types.proto new file mode 100644 index 000000000..bc5e9f32e --- /dev/null +++ b/ampd/proto/axelar/vote/exported/v1beta1/types.proto @@ -0,0 +1,73 @@ +syntax = "proto3"; +package axelar.vote.exported.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/vote/exported"; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "cosmos_proto/cosmos.proto"; +import "axelar/snapshot/exported/v1beta1/types.proto"; +import "axelar/utils/v1beta1/threshold.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// PollMetadata represents a poll with write-in voting, i.e. the result of the +// vote can have any data type +message PollMetadata { + reserved 1, 8, 9, 14; // deleted poll key, total voting power, voters and + // module_metadata in 0.20.x + + int64 expires_at = 3; + google.protobuf.Any result = 4 + [ (cosmos_proto.accepts_interface) = + "github.com/cosmos/codec/ProtoMarshaler" ]; + utils.v1beta1.Threshold voting_threshold = 5 [ (gogoproto.nullable) = false ]; + PollState state = 6; + int64 min_voter_count = 7; + string reward_pool_name = 10; + int64 grace_period = 11; + int64 completed_at = 12; + uint64 id = 13 [ + (gogoproto.customname) = "ID", + (gogoproto.customtype) = "PollID", + (gogoproto.nullable) = false + ]; + snapshot.exported.v1beta1.Snapshot snapshot = 15 + [ (gogoproto.nullable) = false ]; + string module = 16; + google.protobuf.Any module_metadata = 17 + [ (cosmos_proto.accepts_interface) = + "github.com/cosmos/codec/ProtoMarshaler" ]; +} + +// PollKey represents the key data for a poll +message PollKey { + option deprecated = true; + option (gogoproto.goproto_stringer) = false; + + string module = 1; + string id = 2 [ (gogoproto.customname) = "ID" ]; +} + +enum PollState { + option (gogoproto.goproto_enum_prefix) = false; + option (gogoproto.goproto_enum_stringer) = true; + + POLL_STATE_UNSPECIFIED = 0 + [ (gogoproto.enumvalue_customname) = "NonExistent" ]; + POLL_STATE_PENDING = 1 [ (gogoproto.enumvalue_customname) = "Pending" ]; + POLL_STATE_COMPLETED = 2 [ (gogoproto.enumvalue_customname) = "Completed" ]; + POLL_STATE_FAILED = 3 [ (gogoproto.enumvalue_customname) = "Failed" ]; +} + +// PollParticipants should be embedded in poll events in other modules +message PollParticipants { + uint64 poll_id = 1 [ + (gogoproto.customname) = "PollID", + (gogoproto.customtype) = "PollID", + (gogoproto.nullable) = false + ]; + repeated bytes participants = 2 + [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.ValAddress" ]; +} diff --git a/ampd/proto/axelar/vote/v1beta1/events.proto b/ampd/proto/axelar/vote/v1beta1/events.proto new file mode 100644 index 000000000..8a81dad60 --- /dev/null +++ b/ampd/proto/axelar/vote/v1beta1/events.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package axelar.vote.v1beta1; +option go_package = "github.com/axelarnetwork/axelar-core/x/vote/types"; + +import "gogoproto/gogo.proto"; + +option (gogoproto.goproto_getters_all) = false; + +message Voted { + string module = 1; + string action = 2; + string poll = 3; + string voter = 4; + string state = 5; +} diff --git a/ampd/proto/axelar/vote/v1beta1/genesis.proto b/ampd/proto/axelar/vote/v1beta1/genesis.proto new file mode 100644 index 000000000..b26503c51 --- /dev/null +++ b/ampd/proto/axelar/vote/v1beta1/genesis.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +package axelar.vote.v1beta1; +option go_package = "github.com/axelarnetwork/axelar-core/x/vote/types"; + +import "gogoproto/gogo.proto"; +import "axelar/vote/v1beta1/params.proto"; +import "axelar/vote/exported/v1beta1/types.proto"; + +option (gogoproto.goproto_getters_all) = false; + +message GenesisState { + Params params = 1 [ (gogoproto.nullable) = false ]; + + repeated vote.exported.v1beta1.PollMetadata poll_metadatas = 2 + [ (gogoproto.nullable) = false ]; +} diff --git a/ampd/proto/axelar/vote/v1beta1/params.proto b/ampd/proto/axelar/vote/v1beta1/params.proto new file mode 100644 index 000000000..95f853532 --- /dev/null +++ b/ampd/proto/axelar/vote/v1beta1/params.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package axelar.vote.v1beta1; +option go_package = "github.com/axelarnetwork/axelar-core/x/vote/types"; + +import "gogoproto/gogo.proto"; +import "axelar/utils/v1beta1/threshold.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// Params represent the genesis parameters for the module +message Params { + utils.v1beta1.Threshold default_voting_threshold = 1 + [ (gogoproto.nullable) = false ]; + int64 end_blocker_limit = 2; +} diff --git a/ampd/proto/axelar/vote/v1beta1/query.proto b/ampd/proto/axelar/vote/v1beta1/query.proto new file mode 100644 index 000000000..d99457a28 --- /dev/null +++ b/ampd/proto/axelar/vote/v1beta1/query.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; +package axelar.vote.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/vote/types"; + +import "gogoproto/gogo.proto"; +import "axelar/vote/v1beta1/params.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// ParamsRequest represents a message that queries the params +message ParamsRequest {} + +message ParamsResponse { Params params = 1 [ (gogoproto.nullable) = false ]; } diff --git a/ampd/proto/axelar/vote/v1beta1/service.proto b/ampd/proto/axelar/vote/v1beta1/service.proto new file mode 100644 index 000000000..1dfd1574f --- /dev/null +++ b/ampd/proto/axelar/vote/v1beta1/service.proto @@ -0,0 +1,30 @@ +syntax = "proto3"; +package axelar.vote.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/vote/types"; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "axelar/vote/v1beta1/tx.proto"; +import "axelar/vote/v1beta1/query.proto"; + +option (gogoproto.goproto_registration) = true; + +// Msg defines the vote Msg service. +service MsgService { + rpc Vote(VoteRequest) returns (VoteResponse) { + option (google.api.http) = { + post : "/axelar/vote/vote" + body : "*" + }; + } +} + +// QueryService defines the gRPC querier service. +service QueryService { + rpc Params(ParamsRequest) returns (ParamsResponse) { + option (google.api.http) = { + get : "/axelar/vote/v1beta1/params" + }; + } +} diff --git a/ampd/proto/axelar/vote/v1beta1/tx.proto b/ampd/proto/axelar/vote/v1beta1/tx.proto new file mode 100644 index 000000000..771005831 --- /dev/null +++ b/ampd/proto/axelar/vote/v1beta1/tx.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; +package axelar.vote.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/vote/types"; + +import "google/protobuf/any.proto"; +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "axelar/permission/exported/v1beta1/types.proto"; +import "axelar/vote/v1beta1/types.proto"; + +option (gogoproto.goproto_getters_all) = false; + +message VoteRequest { + option (permission.exported.v1beta1.permission_role) = ROLE_UNRESTRICTED; + + reserved 2, 3; // poll_key and vote were removed in v0.20 + + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + uint64 poll_id = 4 [ + (gogoproto.customname) = "PollID", + (gogoproto.customtype) = + "github.com/axelarnetwork/axelar-core/x/vote/exported.PollID", + (gogoproto.nullable) = false + ]; + google.protobuf.Any vote = 5 [ (cosmos_proto.accepts_interface) = + "github.com/cosmos/codec/ProtoMarshaler" ]; +} + +message VoteResponse { string log = 1; } diff --git a/ampd/proto/axelar/vote/v1beta1/types.proto b/ampd/proto/axelar/vote/v1beta1/types.proto new file mode 100644 index 000000000..92b64ce57 --- /dev/null +++ b/ampd/proto/axelar/vote/v1beta1/types.proto @@ -0,0 +1,33 @@ +syntax = "proto3"; +package axelar.vote.v1beta1; + +option go_package = "github.com/axelarnetwork/axelar-core/x/vote/types"; + +import "google/protobuf/any.proto"; +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "axelar/vote/exported/v1beta1/types.proto"; + +option (gogoproto.goproto_getters_all) = false; + +// TalliedVote represents a vote for a poll with the accumulated stake of all +// validators voting for the same data +message TalliedVote { + reserved 2; // voters is deleted in version 0.20.x + + option (gogoproto.stable_marshaler) = true; + + bytes tally = 1 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + (gogoproto.nullable) = false + ]; + google.protobuf.Any data = 3 [ (cosmos_proto.accepts_interface) = + "github.com/cosmos/codec/ProtoMarshaler" ]; + uint64 poll_id = 4 [ + (gogoproto.customname) = "PollID", + (gogoproto.customtype) = + "github.com/axelarnetwork/axelar-core/x/vote/exported.PollID", + (gogoproto.nullable) = false + ]; + map is_voter_late = 5; +} diff --git a/ampd/proto/third_party/buf.yaml b/ampd/proto/third_party/buf.yaml new file mode 100644 index 000000000..aae636f0a --- /dev/null +++ b/ampd/proto/third_party/buf.yaml @@ -0,0 +1,20 @@ +# Generated by "buf config migrate-v1beta1". Edit as necessary, and +# remove this comment when you're finished. +# +# This module represents the "proto" root found in +# the previous configuration. +version: v1 +breaking: + use: + - FILE +lint: + use: + - DEFAULT + - COMMENTS + - FILE_LOWER_SNAKE_CASE + except: + - UNARY_RPC + - COMMENT_FIELD + - SERVICE_SUFFIX + - PACKAGE_VERSION_SUFFIX + - RPC_REQUEST_STANDARD_NAME diff --git a/ampd/proto/third_party/cosmos/auth/v1beta1/auth.proto b/ampd/proto/third_party/cosmos/auth/v1beta1/auth.proto new file mode 100644 index 000000000..72e1d9ec2 --- /dev/null +++ b/ampd/proto/third_party/cosmos/auth/v1beta1/auth.proto @@ -0,0 +1,50 @@ +syntax = "proto3"; +package cosmos.auth.v1beta1; + +import "cosmos_proto/cosmos.proto"; +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/auth/types"; + +// BaseAccount defines a base account type. It contains all the necessary fields +// for basic account functionality. Any custom account type should extend this +// type for additional functionality (e.g. vesting). +message BaseAccount { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + option (gogoproto.equal) = false; + + option (cosmos_proto.implements_interface) = "AccountI"; + + string address = 1; + google.protobuf.Any pub_key = 2 + [(gogoproto.jsontag) = "public_key,omitempty", (gogoproto.moretags) = "yaml:\"public_key\""]; + uint64 account_number = 3 [(gogoproto.moretags) = "yaml:\"account_number\""]; + uint64 sequence = 4; +} + +// ModuleAccount defines an account for modules that holds coins on a pool. +message ModuleAccount { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + option (cosmos_proto.implements_interface) = "ModuleAccountI"; + + BaseAccount base_account = 1 [(gogoproto.embed) = true, (gogoproto.moretags) = "yaml:\"base_account\""]; + string name = 2; + repeated string permissions = 3; +} + +// Params defines the parameters for the auth module. +message Params { + option (gogoproto.equal) = true; + option (gogoproto.goproto_stringer) = false; + + uint64 max_memo_characters = 1 [(gogoproto.moretags) = "yaml:\"max_memo_characters\""]; + uint64 tx_sig_limit = 2 [(gogoproto.moretags) = "yaml:\"tx_sig_limit\""]; + uint64 tx_size_cost_per_byte = 3 [(gogoproto.moretags) = "yaml:\"tx_size_cost_per_byte\""]; + uint64 sig_verify_cost_ed25519 = 4 + [(gogoproto.customname) = "SigVerifyCostED25519", (gogoproto.moretags) = "yaml:\"sig_verify_cost_ed25519\""]; + uint64 sig_verify_cost_secp256k1 = 5 + [(gogoproto.customname) = "SigVerifyCostSecp256k1", (gogoproto.moretags) = "yaml:\"sig_verify_cost_secp256k1\""]; +} diff --git a/ampd/proto/third_party/cosmos/auth/v1beta1/genesis.proto b/ampd/proto/third_party/cosmos/auth/v1beta1/genesis.proto new file mode 100644 index 000000000..c88b94ee4 --- /dev/null +++ b/ampd/proto/third_party/cosmos/auth/v1beta1/genesis.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; +package cosmos.auth.v1beta1; + +import "google/protobuf/any.proto"; +import "gogoproto/gogo.proto"; +import "cosmos/auth/v1beta1/auth.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/auth/types"; + +// GenesisState defines the auth module's genesis state. +message GenesisState { + // params defines all the paramaters of the module. + Params params = 1 [(gogoproto.nullable) = false]; + + // accounts are the accounts present at genesis. + repeated google.protobuf.Any accounts = 2; +} diff --git a/ampd/proto/third_party/cosmos/auth/v1beta1/query.proto b/ampd/proto/third_party/cosmos/auth/v1beta1/query.proto new file mode 100644 index 000000000..79799a4b7 --- /dev/null +++ b/ampd/proto/third_party/cosmos/auth/v1beta1/query.proto @@ -0,0 +1,89 @@ +syntax = "proto3"; +package cosmos.auth.v1beta1; + +import "cosmos/base/query/v1beta1/pagination.proto"; +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "google/api/annotations.proto"; +import "cosmos/auth/v1beta1/auth.proto"; +import "cosmos_proto/cosmos.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/auth/types"; + +// Query defines the gRPC querier service. +service Query { + // Accounts returns all the existing accounts + // + // Since: cosmos-sdk 0.43 + rpc Accounts(QueryAccountsRequest) returns (QueryAccountsResponse) { + option (google.api.http).get = "/cosmos/auth/v1beta1/accounts"; + } + + // Account returns account details based on address. + rpc Account(QueryAccountRequest) returns (QueryAccountResponse) { + option (google.api.http).get = "/cosmos/auth/v1beta1/accounts/{address}"; + } + + // Params queries all parameters. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/cosmos/auth/v1beta1/params"; + } + + // ModuleAccountByName returns the module account info by module name + rpc ModuleAccountByName(QueryModuleAccountByNameRequest) returns (QueryModuleAccountByNameResponse) { + option (google.api.http).get = "/cosmos/auth/v1beta1/module_accounts/{name}"; + } +} + +// QueryAccountsRequest is the request type for the Query/Accounts RPC method. +// +// Since: cosmos-sdk 0.43 +message QueryAccountsRequest { + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +// QueryAccountsResponse is the response type for the Query/Accounts RPC method. +// +// Since: cosmos-sdk 0.43 +message QueryAccountsResponse { + // accounts are the existing accounts + repeated google.protobuf.Any accounts = 1 [(cosmos_proto.accepts_interface) = "AccountI"]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryAccountRequest is the request type for the Query/Account RPC method. +message QueryAccountRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // address defines the address to query for. + string address = 1; +} + +// QueryAccountResponse is the response type for the Query/Account RPC method. +message QueryAccountResponse { + // account defines the account of the corresponding address. + google.protobuf.Any account = 1 [(cosmos_proto.accepts_interface) = "AccountI"]; +} + +// QueryParamsRequest is the request type for the Query/Params RPC method. +message QueryParamsRequest {} + +// QueryParamsResponse is the response type for the Query/Params RPC method. +message QueryParamsResponse { + // params defines the parameters of the module. + Params params = 1 [(gogoproto.nullable) = false]; +} + +// QueryModuleAccountByNameRequest is the request type for the Query/ModuleAccountByName RPC method. +message QueryModuleAccountByNameRequest { + string name = 1; +} + +// QueryModuleAccountByNameResponse is the response type for the Query/ModuleAccountByName RPC method. +message QueryModuleAccountByNameResponse { + google.protobuf.Any account = 1 [(cosmos_proto.accepts_interface) = "ModuleAccountI"]; +} \ No newline at end of file diff --git a/ampd/proto/third_party/cosmos/authz/v1beta1/authz.proto b/ampd/proto/third_party/cosmos/authz/v1beta1/authz.proto new file mode 100644 index 000000000..05b1feefa --- /dev/null +++ b/ampd/proto/third_party/cosmos/authz/v1beta1/authz.proto @@ -0,0 +1,39 @@ +// Since: cosmos-sdk 0.43 +syntax = "proto3"; +package cosmos.authz.v1beta1; + +import "cosmos_proto/cosmos.proto"; +import "google/protobuf/timestamp.proto"; +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/authz"; +option (gogoproto.goproto_getters_all) = false; + +// GenericAuthorization gives the grantee unrestricted permissions to execute +// the provided method on behalf of the granter's account. +message GenericAuthorization { + option (cosmos_proto.implements_interface) = "Authorization"; + + // Msg, identified by it's type URL, to grant unrestricted permissions to execute + string msg = 1; +} + +// Grant gives permissions to execute +// the provide method with expiration time. +message Grant { + google.protobuf.Any authorization = 1 [(cosmos_proto.accepts_interface) = "Authorization"]; + google.protobuf.Timestamp expiration = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; +} + +// GrantAuthorization extends a grant with both the addresses of the grantee and granter. +// It is used in genesis.proto and query.proto +// +// Since: cosmos-sdk 0.45.2 +message GrantAuthorization { + string granter = 1; + string grantee = 2; + + google.protobuf.Any authorization = 3 [(cosmos_proto.accepts_interface) = "Authorization"]; + google.protobuf.Timestamp expiration = 4 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; +} diff --git a/ampd/proto/third_party/cosmos/authz/v1beta1/event.proto b/ampd/proto/third_party/cosmos/authz/v1beta1/event.proto new file mode 100644 index 000000000..7a3cf7c8c --- /dev/null +++ b/ampd/proto/third_party/cosmos/authz/v1beta1/event.proto @@ -0,0 +1,25 @@ +// Since: cosmos-sdk 0.43 +syntax = "proto3"; +package cosmos.authz.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/authz"; + +// EventGrant is emitted on Msg/Grant +message EventGrant { + // Msg type URL for which an autorization is granted + string msg_type_url = 2; + // Granter account address + string granter = 3; + // Grantee account address + string grantee = 4; +} + +// EventRevoke is emitted on Msg/Revoke +message EventRevoke { + // Msg type URL for which an autorization is revoked + string msg_type_url = 2; + // Granter account address + string granter = 3; + // Grantee account address + string grantee = 4; +} diff --git a/ampd/proto/third_party/cosmos/authz/v1beta1/genesis.proto b/ampd/proto/third_party/cosmos/authz/v1beta1/genesis.proto new file mode 100644 index 000000000..310f62656 --- /dev/null +++ b/ampd/proto/third_party/cosmos/authz/v1beta1/genesis.proto @@ -0,0 +1,13 @@ +// Since: cosmos-sdk 0.43 +syntax = "proto3"; +package cosmos.authz.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/authz/v1beta1/authz.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/authz"; + +// GenesisState defines the authz module's genesis state. +message GenesisState { + repeated GrantAuthorization authorization = 1 [(gogoproto.nullable) = false]; +} diff --git a/ampd/proto/third_party/cosmos/authz/v1beta1/query.proto b/ampd/proto/third_party/cosmos/authz/v1beta1/query.proto new file mode 100644 index 000000000..f668309be --- /dev/null +++ b/ampd/proto/third_party/cosmos/authz/v1beta1/query.proto @@ -0,0 +1,81 @@ +// Since: cosmos-sdk 0.43 +syntax = "proto3"; +package cosmos.authz.v1beta1; + +import "google/api/annotations.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; +import "cosmos/authz/v1beta1/authz.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/authz"; + +// Query defines the gRPC querier service. +service Query { + // Returns list of `Authorization`, granted to the grantee by the granter. + rpc Grants(QueryGrantsRequest) returns (QueryGrantsResponse) { + option (google.api.http).get = "/cosmos/authz/v1beta1/grants"; + } + + // GranterGrants returns list of `GrantAuthorization`, granted by granter. + // + // Since: cosmos-sdk 0.45.2 + rpc GranterGrants(QueryGranterGrantsRequest) returns (QueryGranterGrantsResponse) { + option (google.api.http).get = "/cosmos/authz/v1beta1/grants/granter/{granter}"; + } + + // GranteeGrants returns a list of `GrantAuthorization` by grantee. + // + // Since: cosmos-sdk 0.45.2 + rpc GranteeGrants(QueryGranteeGrantsRequest) returns (QueryGranteeGrantsResponse) { + option (google.api.http).get = "/cosmos/authz/v1beta1/grants/grantee/{grantee}"; + } +} + +// QueryGrantsRequest is the request type for the Query/Grants RPC method. +message QueryGrantsRequest { + string granter = 1; + string grantee = 2; + // Optional, msg_type_url, when set, will query only grants matching given msg type. + string msg_type_url = 3; + // pagination defines an pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 4; +} + +// QueryGrantsResponse is the response type for the Query/Authorizations RPC method. +message QueryGrantsResponse { + // authorizations is a list of grants granted for grantee by granter. + repeated Grant grants = 1; + // pagination defines an pagination for the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryGranterGrantsRequest is the request type for the Query/GranterGrants RPC method. +message QueryGranterGrantsRequest { + string granter = 1; + + // pagination defines an pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryGranterGrantsResponse is the response type for the Query/GranterGrants RPC method. +message QueryGranterGrantsResponse { + // grants is a list of grants granted by the granter. + repeated GrantAuthorization grants = 1; + // pagination defines an pagination for the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryGranteeGrantsRequest is the request type for the Query/IssuedGrants RPC method. +message QueryGranteeGrantsRequest { + string grantee = 1; + + // pagination defines an pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryGranteeGrantsResponse is the response type for the Query/GranteeGrants RPC method. +message QueryGranteeGrantsResponse { + // grants is a list of grants granted to the grantee. + repeated GrantAuthorization grants = 1; + // pagination defines an pagination for the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} diff --git a/ampd/proto/third_party/cosmos/authz/v1beta1/tx.proto b/ampd/proto/third_party/cosmos/authz/v1beta1/tx.proto new file mode 100644 index 000000000..457f0d662 --- /dev/null +++ b/ampd/proto/third_party/cosmos/authz/v1beta1/tx.proto @@ -0,0 +1,70 @@ +// Since: cosmos-sdk 0.43 +syntax = "proto3"; +package cosmos.authz.v1beta1; + +import "cosmos_proto/cosmos.proto"; +import "gogoproto/gogo.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/any.proto"; +import "cosmos/base/abci/v1beta1/abci.proto"; +import "cosmos/authz/v1beta1/authz.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/authz"; +option (gogoproto.goproto_getters_all) = false; + +// Msg defines the authz Msg service. +service Msg { + // Grant grants the provided authorization to the grantee on the granter's + // account with the provided expiration time. If there is already a grant + // for the given (granter, grantee, Authorization) triple, then the grant + // will be overwritten. + rpc Grant(MsgGrant) returns (MsgGrantResponse); + + // Exec attempts to execute the provided messages using + // authorizations granted to the grantee. Each message should have only + // one signer corresponding to the granter of the authorization. + rpc Exec(MsgExec) returns (MsgExecResponse); + + // Revoke revokes any authorization corresponding to the provided method name on the + // granter's account that has been granted to the grantee. + rpc Revoke(MsgRevoke) returns (MsgRevokeResponse); +} + +// MsgGrant is a request type for Grant method. It declares authorization to the grantee +// on behalf of the granter with the provided expiration time. +message MsgGrant { + string granter = 1; + string grantee = 2; + + cosmos.authz.v1beta1.Grant grant = 3 [(gogoproto.nullable) = false]; +} + +// MsgExecResponse defines the Msg/MsgExecResponse response type. +message MsgExecResponse { + repeated bytes results = 1; +} + +// MsgExec attempts to execute the provided messages using +// authorizations granted to the grantee. Each message should have only +// one signer corresponding to the granter of the authorization. +message MsgExec { + string grantee = 1; + // Authorization Msg requests to execute. Each msg must implement Authorization interface + // The x/authz will try to find a grant matching (msg.signers[0], grantee, MsgTypeURL(msg)) + // triple and validate it. + repeated google.protobuf.Any msgs = 2 [(cosmos_proto.accepts_interface) = "sdk.Msg, authz.Authorization"]; +} + +// MsgGrantResponse defines the Msg/MsgGrant response type. +message MsgGrantResponse {} + +// MsgRevoke revokes any authorization with the provided sdk.Msg type on the +// granter's account with that has been granted to the grantee. +message MsgRevoke { + string granter = 1; + string grantee = 2; + string msg_type_url = 3; +} + +// MsgRevokeResponse defines the Msg/MsgRevokeResponse response type. +message MsgRevokeResponse {} diff --git a/ampd/proto/third_party/cosmos/bank/v1beta1/authz.proto b/ampd/proto/third_party/cosmos/bank/v1beta1/authz.proto new file mode 100644 index 000000000..4f58b15e4 --- /dev/null +++ b/ampd/proto/third_party/cosmos/bank/v1beta1/authz.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; +package cosmos.bank.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/bank/types"; + +// SendAuthorization allows the grantee to spend up to spend_limit coins from +// the granter's account. +// +// Since: cosmos-sdk 0.43 +message SendAuthorization { + option (cosmos_proto.implements_interface) = "Authorization"; + + repeated cosmos.base.v1beta1.Coin spend_limit = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; +} diff --git a/ampd/proto/third_party/cosmos/bank/v1beta1/bank.proto b/ampd/proto/third_party/cosmos/bank/v1beta1/bank.proto new file mode 100644 index 000000000..df91008df --- /dev/null +++ b/ampd/proto/third_party/cosmos/bank/v1beta1/bank.proto @@ -0,0 +1,96 @@ +syntax = "proto3"; +package cosmos.bank.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/bank/types"; + +// Params defines the parameters for the bank module. +message Params { + option (gogoproto.goproto_stringer) = false; + repeated SendEnabled send_enabled = 1 [(gogoproto.moretags) = "yaml:\"send_enabled,omitempty\""]; + bool default_send_enabled = 2 [(gogoproto.moretags) = "yaml:\"default_send_enabled,omitempty\""]; +} + +// SendEnabled maps coin denom to a send_enabled status (whether a denom is +// sendable). +message SendEnabled { + option (gogoproto.equal) = true; + option (gogoproto.goproto_stringer) = false; + string denom = 1; + bool enabled = 2; +} + +// Input models transaction input. +message Input { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string address = 1; + repeated cosmos.base.v1beta1.Coin coins = 2 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; +} + +// Output models transaction outputs. +message Output { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string address = 1; + repeated cosmos.base.v1beta1.Coin coins = 2 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; +} + +// Supply represents a struct that passively keeps track of the total supply +// amounts in the network. +// This message is deprecated now that supply is indexed by denom. +message Supply { + option deprecated = true; + + option (gogoproto.equal) = true; + option (gogoproto.goproto_getters) = false; + + option (cosmos_proto.implements_interface) = "*github.com/cosmos/cosmos-sdk/x/bank/legacy/v040.SupplyI"; + + repeated cosmos.base.v1beta1.Coin total = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; +} + +// DenomUnit represents a struct that describes a given +// denomination unit of the basic token. +message DenomUnit { + // denom represents the string name of the given denom unit (e.g uatom). + string denom = 1; + // exponent represents power of 10 exponent that one must + // raise the base_denom to in order to equal the given DenomUnit's denom + // 1 denom = 1^exponent base_denom + // (e.g. with a base_denom of uatom, one can create a DenomUnit of 'atom' with + // exponent = 6, thus: 1 atom = 10^6 uatom). + uint32 exponent = 2; + // aliases is a list of string aliases for the given denom + repeated string aliases = 3; +} + +// Metadata represents a struct that describes +// a basic token. +message Metadata { + string description = 1; + // denom_units represents the list of DenomUnit's for a given coin + repeated DenomUnit denom_units = 2; + // base represents the base denom (should be the DenomUnit with exponent = 0). + string base = 3; + // display indicates the suggested denom that should be + // displayed in clients. + string display = 4; + // name defines the name of the token (eg: Cosmos Atom) + // + // Since: cosmos-sdk 0.43 + string name = 5; + // symbol is the token symbol usually shown on exchanges (eg: ATOM). This can + // be the same as the display. + // + // Since: cosmos-sdk 0.43 + string symbol = 6; +} diff --git a/ampd/proto/third_party/cosmos/bank/v1beta1/genesis.proto b/ampd/proto/third_party/cosmos/bank/v1beta1/genesis.proto new file mode 100644 index 000000000..8fd7329a0 --- /dev/null +++ b/ampd/proto/third_party/cosmos/bank/v1beta1/genesis.proto @@ -0,0 +1,39 @@ +syntax = "proto3"; +package cosmos.bank.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/bank/v1beta1/bank.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/bank/types"; + +// GenesisState defines the bank module's genesis state. +message GenesisState { + // params defines all the paramaters of the module. + Params params = 1 [(gogoproto.nullable) = false]; + + // balances is an array containing the balances of all the accounts. + repeated Balance balances = 2 [(gogoproto.nullable) = false]; + + // supply represents the total supply. If it is left empty, then supply will be calculated based on the provided + // balances. Otherwise, it will be used to validate that the sum of the balances equals this amount. + repeated cosmos.base.v1beta1.Coin supply = 3 + [(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", (gogoproto.nullable) = false]; + + // denom_metadata defines the metadata of the differents coins. + repeated Metadata denom_metadata = 4 [(gogoproto.moretags) = "yaml:\"denom_metadata\"", (gogoproto.nullable) = false]; +} + +// Balance defines an account address and balance pair used in the bank module's +// genesis state. +message Balance { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // address is the address of the balance holder. + string address = 1; + + // coins defines the different coins this balance holds. + repeated cosmos.base.v1beta1.Coin coins = 2 + [(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", (gogoproto.nullable) = false]; +} diff --git a/ampd/proto/third_party/cosmos/bank/v1beta1/query.proto b/ampd/proto/third_party/cosmos/bank/v1beta1/query.proto new file mode 100644 index 000000000..a567e073f --- /dev/null +++ b/ampd/proto/third_party/cosmos/bank/v1beta1/query.proto @@ -0,0 +1,193 @@ +syntax = "proto3"; +package cosmos.bank.v1beta1; + +import "cosmos/base/query/v1beta1/pagination.proto"; +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/bank/v1beta1/bank.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/bank/types"; + +// Query defines the gRPC querier service. +service Query { + // Balance queries the balance of a single coin for a single account. + rpc Balance(QueryBalanceRequest) returns (QueryBalanceResponse) { + option (google.api.http).get = "/cosmos/bank/v1beta1/balances/{address}/by_denom"; + } + + // AllBalances queries the balance of all coins for a single account. + rpc AllBalances(QueryAllBalancesRequest) returns (QueryAllBalancesResponse) { + option (google.api.http).get = "/cosmos/bank/v1beta1/balances/{address}"; + } + + // SpendableBalances queries the spenable balance of all coins for a single + // account. + rpc SpendableBalances(QuerySpendableBalancesRequest) returns (QuerySpendableBalancesResponse) { + option (google.api.http).get = "/cosmos/bank/v1beta1/spendable_balances/{address}"; + } + + // TotalSupply queries the total supply of all coins. + rpc TotalSupply(QueryTotalSupplyRequest) returns (QueryTotalSupplyResponse) { + option (google.api.http).get = "/cosmos/bank/v1beta1/supply"; + } + + // SupplyOf queries the supply of a single coin. + rpc SupplyOf(QuerySupplyOfRequest) returns (QuerySupplyOfResponse) { + option (google.api.http).get = "/cosmos/bank/v1beta1/supply/{denom}"; + } + + // Params queries the parameters of x/bank module. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/cosmos/bank/v1beta1/params"; + } + + // DenomsMetadata queries the client metadata of a given coin denomination. + rpc DenomMetadata(QueryDenomMetadataRequest) returns (QueryDenomMetadataResponse) { + option (google.api.http).get = "/cosmos/bank/v1beta1/denoms_metadata/{denom}"; + } + + // DenomsMetadata queries the client metadata for all registered coin denominations. + rpc DenomsMetadata(QueryDenomsMetadataRequest) returns (QueryDenomsMetadataResponse) { + option (google.api.http).get = "/cosmos/bank/v1beta1/denoms_metadata"; + } +} + +// QueryBalanceRequest is the request type for the Query/Balance RPC method. +message QueryBalanceRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // address is the address to query balances for. + string address = 1; + + // denom is the coin denom to query balances for. + string denom = 2; +} + +// QueryBalanceResponse is the response type for the Query/Balance RPC method. +message QueryBalanceResponse { + // balance is the balance of the coin. + cosmos.base.v1beta1.Coin balance = 1; +} + +// QueryBalanceRequest is the request type for the Query/AllBalances RPC method. +message QueryAllBalancesRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // address is the address to query balances for. + string address = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryAllBalancesResponse is the response type for the Query/AllBalances RPC +// method. +message QueryAllBalancesResponse { + // balances is the balances of all the coins. + repeated cosmos.base.v1beta1.Coin balances = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QuerySpendableBalancesRequest defines the gRPC request structure for querying +// an account's spendable balances. +message QuerySpendableBalancesRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // address is the address to query spendable balances for. + string address = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QuerySpendableBalancesResponse defines the gRPC response structure for querying +// an account's spendable balances. +message QuerySpendableBalancesResponse { + // balances is the spendable balances of all the coins. + repeated cosmos.base.v1beta1.Coin balances = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryTotalSupplyRequest is the request type for the Query/TotalSupply RPC +// method. +message QueryTotalSupplyRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // pagination defines an optional pagination for the request. + // + // Since: cosmos-sdk 0.43 + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +// QueryTotalSupplyResponse is the response type for the Query/TotalSupply RPC +// method +message QueryTotalSupplyResponse { + // supply is the supply of the coins + repeated cosmos.base.v1beta1.Coin supply = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + + // pagination defines the pagination in the response. + // + // Since: cosmos-sdk 0.43 + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QuerySupplyOfRequest is the request type for the Query/SupplyOf RPC method. +message QuerySupplyOfRequest { + // denom is the coin denom to query balances for. + string denom = 1; +} + +// QuerySupplyOfResponse is the response type for the Query/SupplyOf RPC method. +message QuerySupplyOfResponse { + // amount is the supply of the coin. + cosmos.base.v1beta1.Coin amount = 1 [(gogoproto.nullable) = false]; +} + +// QueryParamsRequest defines the request type for querying x/bank parameters. +message QueryParamsRequest {} + +// QueryParamsResponse defines the response type for querying x/bank parameters. +message QueryParamsResponse { + Params params = 1 [(gogoproto.nullable) = false]; +} + +// QueryDenomsMetadataRequest is the request type for the Query/DenomsMetadata RPC method. +message QueryDenomsMetadataRequest { + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +// QueryDenomsMetadataResponse is the response type for the Query/DenomsMetadata RPC +// method. +message QueryDenomsMetadataResponse { + // metadata provides the client information for all the registered tokens. + repeated Metadata metadatas = 1 [(gogoproto.nullable) = false]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryDenomMetadataRequest is the request type for the Query/DenomMetadata RPC method. +message QueryDenomMetadataRequest { + // denom is the coin denom to query the metadata for. + string denom = 1; +} + +// QueryDenomMetadataResponse is the response type for the Query/DenomMetadata RPC +// method. +message QueryDenomMetadataResponse { + // metadata describes and provides all the client information for the requested token. + Metadata metadata = 1 [(gogoproto.nullable) = false]; +} diff --git a/ampd/proto/third_party/cosmos/bank/v1beta1/tx.proto b/ampd/proto/third_party/cosmos/bank/v1beta1/tx.proto new file mode 100644 index 000000000..26b2ab41f --- /dev/null +++ b/ampd/proto/third_party/cosmos/bank/v1beta1/tx.proto @@ -0,0 +1,42 @@ +syntax = "proto3"; +package cosmos.bank.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/bank/v1beta1/bank.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/bank/types"; + +// Msg defines the bank Msg service. +service Msg { + // Send defines a method for sending coins from one account to another account. + rpc Send(MsgSend) returns (MsgSendResponse); + + // MultiSend defines a method for sending coins from some accounts to other accounts. + rpc MultiSend(MsgMultiSend) returns (MsgMultiSendResponse); +} + +// MsgSend represents a message to send coins from one account to another. +message MsgSend { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string from_address = 1 [(gogoproto.moretags) = "yaml:\"from_address\""]; + string to_address = 2 [(gogoproto.moretags) = "yaml:\"to_address\""]; + repeated cosmos.base.v1beta1.Coin amount = 3 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; +} + +// MsgSendResponse defines the Msg/Send response type. +message MsgSendResponse {} + +// MsgMultiSend represents an arbitrary multi-in, multi-out send message. +message MsgMultiSend { + option (gogoproto.equal) = false; + + repeated Input inputs = 1 [(gogoproto.nullable) = false]; + repeated Output outputs = 2 [(gogoproto.nullable) = false]; +} + +// MsgMultiSendResponse defines the Msg/MultiSend response type. +message MsgMultiSendResponse {} diff --git a/ampd/proto/third_party/cosmos/base/abci/v1beta1/abci.proto b/ampd/proto/third_party/cosmos/base/abci/v1beta1/abci.proto new file mode 100644 index 000000000..e24ae7bd5 --- /dev/null +++ b/ampd/proto/third_party/cosmos/base/abci/v1beta1/abci.proto @@ -0,0 +1,144 @@ +syntax = "proto3"; +package cosmos.base.abci.v1beta1; + +import "gogoproto/gogo.proto"; +import "tendermint/abci/types.proto"; +import "google/protobuf/any.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/types"; +option (gogoproto.goproto_stringer_all) = false; + +// TxResponse defines a structure containing relevant tx data and metadata. The +// tags are stringified and the log is JSON decoded. +message TxResponse { + option (gogoproto.goproto_getters) = false; + // The block height + int64 height = 1; + // The transaction hash. + string txhash = 2 [(gogoproto.customname) = "TxHash"]; + // Namespace for the Code + string codespace = 3; + // Response code. + uint32 code = 4; + // Result bytes, if any. + string data = 5; + // The output of the application's logger (raw string). May be + // non-deterministic. + string raw_log = 6; + // The output of the application's logger (typed). May be non-deterministic. + repeated ABCIMessageLog logs = 7 [(gogoproto.castrepeated) = "ABCIMessageLogs", (gogoproto.nullable) = false]; + // Additional information. May be non-deterministic. + string info = 8; + // Amount of gas requested for transaction. + int64 gas_wanted = 9; + // Amount of gas consumed by transaction. + int64 gas_used = 10; + // The request transaction bytes. + google.protobuf.Any tx = 11; + // Time of the previous block. For heights > 1, it's the weighted median of + // the timestamps of the valid votes in the block.LastCommit. For height == 1, + // it's genesis time. + string timestamp = 12; + // Events defines all the events emitted by processing a transaction. Note, + // these events include those emitted by processing all the messages and those + // emitted from the ante handler. Whereas Logs contains the events, with + // additional metadata, emitted only by processing the messages. + // + // Since: cosmos-sdk 0.42.11, 0.44.5, 0.45 + repeated tendermint.abci.Event events = 13 [(gogoproto.nullable) = false]; +} + +// ABCIMessageLog defines a structure containing an indexed tx ABCI message log. +message ABCIMessageLog { + option (gogoproto.stringer) = true; + + uint32 msg_index = 1; + string log = 2; + + // Events contains a slice of Event objects that were emitted during some + // execution. + repeated StringEvent events = 3 [(gogoproto.castrepeated) = "StringEvents", (gogoproto.nullable) = false]; +} + +// StringEvent defines en Event object wrapper where all the attributes +// contain key/value pairs that are strings instead of raw bytes. +message StringEvent { + option (gogoproto.stringer) = true; + + string type = 1; + repeated Attribute attributes = 2 [(gogoproto.nullable) = false]; +} + +// Attribute defines an attribute wrapper where the key and value are +// strings instead of raw bytes. +message Attribute { + string key = 1; + string value = 2; +} + +// GasInfo defines tx execution gas context. +message GasInfo { + // GasWanted is the maximum units of work we allow this tx to perform. + uint64 gas_wanted = 1 [(gogoproto.moretags) = "yaml:\"gas_wanted\""]; + + // GasUsed is the amount of gas actually consumed. + uint64 gas_used = 2 [(gogoproto.moretags) = "yaml:\"gas_used\""]; +} + +// Result is the union of ResponseFormat and ResponseCheckTx. +message Result { + option (gogoproto.goproto_getters) = false; + + // Data is any data returned from message or handler execution. It MUST be + // length prefixed in order to separate data from multiple message executions. + bytes data = 1; + + // Log contains the log information from message or handler execution. + string log = 2; + + // Events contains a slice of Event objects that were emitted during message + // or handler execution. + repeated tendermint.abci.Event events = 3 [(gogoproto.nullable) = false]; +} + +// SimulationResponse defines the response generated when a transaction is +// successfully simulated. +message SimulationResponse { + GasInfo gas_info = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; + Result result = 2; +} + +// MsgData defines the data returned in a Result object during message +// execution. +message MsgData { + option (gogoproto.stringer) = true; + + string msg_type = 1; + bytes data = 2; +} + +// TxMsgData defines a list of MsgData. A transaction will have a MsgData object +// for each message. +message TxMsgData { + option (gogoproto.stringer) = true; + + repeated MsgData data = 1; +} + +// SearchTxsResult defines a structure for querying txs pageable +message SearchTxsResult { + option (gogoproto.stringer) = true; + + // Count of all txs + uint64 total_count = 1 [(gogoproto.moretags) = "yaml:\"total_count\"", (gogoproto.jsontag) = "total_count"]; + // Count of txs in current page + uint64 count = 2; + // Index of current page, start from 1 + uint64 page_number = 3 [(gogoproto.moretags) = "yaml:\"page_number\"", (gogoproto.jsontag) = "page_number"]; + // Count of total pages + uint64 page_total = 4 [(gogoproto.moretags) = "yaml:\"page_total\"", (gogoproto.jsontag) = "page_total"]; + // Max count txs per page + uint64 limit = 5; + // List of txs in current page + repeated TxResponse txs = 6; +} diff --git a/ampd/proto/third_party/cosmos/base/kv/v1beta1/kv.proto b/ampd/proto/third_party/cosmos/base/kv/v1beta1/kv.proto new file mode 100644 index 000000000..4e9b8d285 --- /dev/null +++ b/ampd/proto/third_party/cosmos/base/kv/v1beta1/kv.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; +package cosmos.base.kv.v1beta1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/types/kv"; + +// Pairs defines a repeated slice of Pair objects. +message Pairs { + repeated Pair pairs = 1 [(gogoproto.nullable) = false]; +} + +// Pair defines a key/value bytes tuple. +message Pair { + bytes key = 1; + bytes value = 2; +} diff --git a/ampd/proto/third_party/cosmos/base/node/v1beta1/query.proto b/ampd/proto/third_party/cosmos/base/node/v1beta1/query.proto new file mode 100644 index 000000000..8070f7b90 --- /dev/null +++ b/ampd/proto/third_party/cosmos/base/node/v1beta1/query.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; +package cosmos.base.node.v1beta1; + +import "google/api/annotations.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/client/grpc/node"; + +// Service defines the gRPC querier service for node related queries. +service Service { + // Config queries for the operator configuration. + rpc Config(ConfigRequest) returns (ConfigResponse) { + option (google.api.http).get = "/cosmos/base/node/v1beta1/config"; + } +} + +// ConfigRequest defines the request structure for the Config gRPC query. +message ConfigRequest {} + +// ConfigResponse defines the response structure for the Config gRPC query. +message ConfigResponse { + string minimum_gas_price = 1; +} diff --git a/ampd/proto/third_party/cosmos/base/query/v1beta1/pagination.proto b/ampd/proto/third_party/cosmos/base/query/v1beta1/pagination.proto new file mode 100644 index 000000000..cd5eb066d --- /dev/null +++ b/ampd/proto/third_party/cosmos/base/query/v1beta1/pagination.proto @@ -0,0 +1,55 @@ +syntax = "proto3"; +package cosmos.base.query.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/types/query"; + +// PageRequest is to be embedded in gRPC request messages for efficient +// pagination. Ex: +// +// message SomeRequest { +// Foo some_parameter = 1; +// PageRequest pagination = 2; +// } +message PageRequest { + // key is a value returned in PageResponse.next_key to begin + // querying the next page most efficiently. Only one of offset or key + // should be set. + bytes key = 1; + + // offset is a numeric offset that can be used when key is unavailable. + // It is less efficient than using key. Only one of offset or key should + // be set. + uint64 offset = 2; + + // limit is the total number of results to be returned in the result page. + // If left empty it will default to a value to be set by each app. + uint64 limit = 3; + + // count_total is set to true to indicate that the result set should include + // a count of the total number of items available for pagination in UIs. + // count_total is only respected when offset is used. It is ignored when key + // is set. + bool count_total = 4; + + // reverse is set to true if results are to be returned in the descending order. + // + // Since: cosmos-sdk 0.43 + bool reverse = 5; +} + +// PageResponse is to be embedded in gRPC response messages where the +// corresponding request message has used PageRequest. +// +// message SomeResponse { +// repeated Bar results = 1; +// PageResponse page = 2; +// } +message PageResponse { + // next_key is the key to be passed to PageRequest.key to + // query the next page most efficiently + bytes next_key = 1; + + // total is total number of results available if PageRequest.count_total + // was set, its value is undefined otherwise + uint64 total = 2; +} diff --git a/ampd/proto/third_party/cosmos/base/reflection/v1beta1/reflection.proto b/ampd/proto/third_party/cosmos/base/reflection/v1beta1/reflection.proto new file mode 100644 index 000000000..22670e72b --- /dev/null +++ b/ampd/proto/third_party/cosmos/base/reflection/v1beta1/reflection.proto @@ -0,0 +1,44 @@ +syntax = "proto3"; +package cosmos.base.reflection.v1beta1; + +import "google/api/annotations.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/client/grpc/reflection"; + +// ReflectionService defines a service for interface reflection. +service ReflectionService { + // ListAllInterfaces lists all the interfaces registered in the interface + // registry. + rpc ListAllInterfaces(ListAllInterfacesRequest) returns (ListAllInterfacesResponse) { + option (google.api.http).get = "/cosmos/base/reflection/v1beta1/interfaces"; + }; + + // ListImplementations list all the concrete types that implement a given + // interface. + rpc ListImplementations(ListImplementationsRequest) returns (ListImplementationsResponse) { + option (google.api.http).get = "/cosmos/base/reflection/v1beta1/interfaces/" + "{interface_name}/implementations"; + }; +} + +// ListAllInterfacesRequest is the request type of the ListAllInterfaces RPC. +message ListAllInterfacesRequest {} + +// ListAllInterfacesResponse is the response type of the ListAllInterfaces RPC. +message ListAllInterfacesResponse { + // interface_names is an array of all the registered interfaces. + repeated string interface_names = 1; +} + +// ListImplementationsRequest is the request type of the ListImplementations +// RPC. +message ListImplementationsRequest { + // interface_name defines the interface to query the implementations for. + string interface_name = 1; +} + +// ListImplementationsResponse is the response type of the ListImplementations +// RPC. +message ListImplementationsResponse { + repeated string implementation_message_names = 1; +} diff --git a/ampd/proto/third_party/cosmos/base/reflection/v2alpha1/reflection.proto b/ampd/proto/third_party/cosmos/base/reflection/v2alpha1/reflection.proto new file mode 100644 index 000000000..d5b048558 --- /dev/null +++ b/ampd/proto/third_party/cosmos/base/reflection/v2alpha1/reflection.proto @@ -0,0 +1,218 @@ +// Since: cosmos-sdk 0.43 +syntax = "proto3"; +package cosmos.base.reflection.v2alpha1; + +import "google/api/annotations.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/server/grpc/reflection/v2alpha1"; + +// AppDescriptor describes a cosmos-sdk based application +message AppDescriptor { + // AuthnDescriptor provides information on how to authenticate transactions on the application + // NOTE: experimental and subject to change in future releases. + AuthnDescriptor authn = 1; + // chain provides the chain descriptor + ChainDescriptor chain = 2; + // codec provides metadata information regarding codec related types + CodecDescriptor codec = 3; + // configuration provides metadata information regarding the sdk.Config type + ConfigurationDescriptor configuration = 4; + // query_services provides metadata information regarding the available queriable endpoints + QueryServicesDescriptor query_services = 5; + // tx provides metadata information regarding how to send transactions to the given application + TxDescriptor tx = 6; +} + +// TxDescriptor describes the accepted transaction type +message TxDescriptor { + // fullname is the protobuf fullname of the raw transaction type (for instance the tx.Tx type) + // it is not meant to support polymorphism of transaction types, it is supposed to be used by + // reflection clients to understand if they can handle a specific transaction type in an application. + string fullname = 1; + // msgs lists the accepted application messages (sdk.Msg) + repeated MsgDescriptor msgs = 2; +} + +// AuthnDescriptor provides information on how to sign transactions without relying +// on the online RPCs GetTxMetadata and CombineUnsignedTxAndSignatures +message AuthnDescriptor { + // sign_modes defines the supported signature algorithm + repeated SigningModeDescriptor sign_modes = 1; +} + +// SigningModeDescriptor provides information on a signing flow of the application +// NOTE(fdymylja): here we could go as far as providing an entire flow on how +// to sign a message given a SigningModeDescriptor, but it's better to think about +// this another time +message SigningModeDescriptor { + // name defines the unique name of the signing mode + string name = 1; + // number is the unique int32 identifier for the sign_mode enum + int32 number = 2; + // authn_info_provider_method_fullname defines the fullname of the method to call to get + // the metadata required to authenticate using the provided sign_modes + string authn_info_provider_method_fullname = 3; +} + +// ChainDescriptor describes chain information of the application +message ChainDescriptor { + // id is the chain id + string id = 1; +} + +// CodecDescriptor describes the registered interfaces and provides metadata information on the types +message CodecDescriptor { + // interfaces is a list of the registerted interfaces descriptors + repeated InterfaceDescriptor interfaces = 1; +} + +// InterfaceDescriptor describes the implementation of an interface +message InterfaceDescriptor { + // fullname is the name of the interface + string fullname = 1; + // interface_accepting_messages contains information regarding the proto messages which contain the interface as + // google.protobuf.Any field + repeated InterfaceAcceptingMessageDescriptor interface_accepting_messages = 2; + // interface_implementers is a list of the descriptors of the interface implementers + repeated InterfaceImplementerDescriptor interface_implementers = 3; +} + +// InterfaceImplementerDescriptor describes an interface implementer +message InterfaceImplementerDescriptor { + // fullname is the protobuf queryable name of the interface implementer + string fullname = 1; + // type_url defines the type URL used when marshalling the type as any + // this is required so we can provide type safe google.protobuf.Any marshalling and + // unmarshalling, making sure that we don't accept just 'any' type + // in our interface fields + string type_url = 2; +} + +// InterfaceAcceptingMessageDescriptor describes a protobuf message which contains +// an interface represented as a google.protobuf.Any +message InterfaceAcceptingMessageDescriptor { + // fullname is the protobuf fullname of the type containing the interface + string fullname = 1; + // field_descriptor_names is a list of the protobuf name (not fullname) of the field + // which contains the interface as google.protobuf.Any (the interface is the same, but + // it can be in multiple fields of the same proto message) + repeated string field_descriptor_names = 2; +} + +// ConfigurationDescriptor contains metadata information on the sdk.Config +message ConfigurationDescriptor { + // bech32_account_address_prefix is the account address prefix + string bech32_account_address_prefix = 1; +} + +// MsgDescriptor describes a cosmos-sdk message that can be delivered with a transaction +message MsgDescriptor { + // msg_type_url contains the TypeURL of a sdk.Msg. + string msg_type_url = 1; +} + +// ReflectionService defines a service for application reflection. +service ReflectionService { + // GetAuthnDescriptor returns information on how to authenticate transactions in the application + // NOTE: this RPC is still experimental and might be subject to breaking changes or removal in + // future releases of the cosmos-sdk. + rpc GetAuthnDescriptor(GetAuthnDescriptorRequest) returns (GetAuthnDescriptorResponse) { + option (google.api.http).get = "/cosmos/base/reflection/v1beta1/app_descriptor/authn"; + } + // GetChainDescriptor returns the description of the chain + rpc GetChainDescriptor(GetChainDescriptorRequest) returns (GetChainDescriptorResponse) { + option (google.api.http).get = "/cosmos/base/reflection/v1beta1/app_descriptor/chain"; + }; + // GetCodecDescriptor returns the descriptor of the codec of the application + rpc GetCodecDescriptor(GetCodecDescriptorRequest) returns (GetCodecDescriptorResponse) { + option (google.api.http).get = "/cosmos/base/reflection/v1beta1/app_descriptor/codec"; + } + // GetConfigurationDescriptor returns the descriptor for the sdk.Config of the application + rpc GetConfigurationDescriptor(GetConfigurationDescriptorRequest) returns (GetConfigurationDescriptorResponse) { + option (google.api.http).get = "/cosmos/base/reflection/v1beta1/app_descriptor/configuration"; + } + // GetQueryServicesDescriptor returns the available gRPC queryable services of the application + rpc GetQueryServicesDescriptor(GetQueryServicesDescriptorRequest) returns (GetQueryServicesDescriptorResponse) { + option (google.api.http).get = "/cosmos/base/reflection/v1beta1/app_descriptor/query_services"; + } + // GetTxDescriptor returns information on the used transaction object and available msgs that can be used + rpc GetTxDescriptor(GetTxDescriptorRequest) returns (GetTxDescriptorResponse) { + option (google.api.http).get = "/cosmos/base/reflection/v1beta1/app_descriptor/tx_descriptor"; + } +} + +// GetAuthnDescriptorRequest is the request used for the GetAuthnDescriptor RPC +message GetAuthnDescriptorRequest {} +// GetAuthnDescriptorResponse is the response returned by the GetAuthnDescriptor RPC +message GetAuthnDescriptorResponse { + // authn describes how to authenticate to the application when sending transactions + AuthnDescriptor authn = 1; +} + +// GetChainDescriptorRequest is the request used for the GetChainDescriptor RPC +message GetChainDescriptorRequest {} +// GetChainDescriptorResponse is the response returned by the GetChainDescriptor RPC +message GetChainDescriptorResponse { + // chain describes application chain information + ChainDescriptor chain = 1; +} + +// GetCodecDescriptorRequest is the request used for the GetCodecDescriptor RPC +message GetCodecDescriptorRequest {} +// GetCodecDescriptorResponse is the response returned by the GetCodecDescriptor RPC +message GetCodecDescriptorResponse { + // codec describes the application codec such as registered interfaces and implementations + CodecDescriptor codec = 1; +} + +// GetConfigurationDescriptorRequest is the request used for the GetConfigurationDescriptor RPC +message GetConfigurationDescriptorRequest {} +// GetConfigurationDescriptorResponse is the response returned by the GetConfigurationDescriptor RPC +message GetConfigurationDescriptorResponse { + // config describes the application's sdk.Config + ConfigurationDescriptor config = 1; +} + +// GetQueryServicesDescriptorRequest is the request used for the GetQueryServicesDescriptor RPC +message GetQueryServicesDescriptorRequest {} +// GetQueryServicesDescriptorResponse is the response returned by the GetQueryServicesDescriptor RPC +message GetQueryServicesDescriptorResponse { + // queries provides information on the available queryable services + QueryServicesDescriptor queries = 1; +} + +// GetTxDescriptorRequest is the request used for the GetTxDescriptor RPC +message GetTxDescriptorRequest {} +// GetTxDescriptorResponse is the response returned by the GetTxDescriptor RPC +message GetTxDescriptorResponse { + // tx provides information on msgs that can be forwarded to the application + // alongside the accepted transaction protobuf type + TxDescriptor tx = 1; +} + +// QueryServicesDescriptor contains the list of cosmos-sdk queriable services +message QueryServicesDescriptor { + // query_services is a list of cosmos-sdk QueryServiceDescriptor + repeated QueryServiceDescriptor query_services = 1; +} + +// QueryServiceDescriptor describes a cosmos-sdk queryable service +message QueryServiceDescriptor { + // fullname is the protobuf fullname of the service descriptor + string fullname = 1; + // is_module describes if this service is actually exposed by an application's module + bool is_module = 2; + // methods provides a list of query service methods + repeated QueryMethodDescriptor methods = 3; +} + +// QueryMethodDescriptor describes a queryable method of a query service +// no other info is provided beside method name and tendermint queryable path +// because it would be redundant with the grpc reflection service +message QueryMethodDescriptor { + // name is the protobuf name (not fullname) of the method + string name = 1; + // full_query_path is the path that can be used to query + // this method via tendermint abci.Query + string full_query_path = 2; +} diff --git a/ampd/proto/third_party/cosmos/base/snapshots/v1beta1/snapshot.proto b/ampd/proto/third_party/cosmos/base/snapshots/v1beta1/snapshot.proto new file mode 100644 index 000000000..6dcc4a933 --- /dev/null +++ b/ampd/proto/third_party/cosmos/base/snapshots/v1beta1/snapshot.proto @@ -0,0 +1,57 @@ +syntax = "proto3"; +package cosmos.base.snapshots.v1beta1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/snapshots/types"; + +// Snapshot contains Tendermint state sync snapshot info. +message Snapshot { + uint64 height = 1; + uint32 format = 2; + uint32 chunks = 3; + bytes hash = 4; + Metadata metadata = 5 [(gogoproto.nullable) = false]; +} + +// Metadata contains SDK-specific snapshot metadata. +message Metadata { + repeated bytes chunk_hashes = 1; // SHA-256 chunk hashes +} + +// SnapshotItem is an item contained in a rootmulti.Store snapshot. +message SnapshotItem { + // item is the specific type of snapshot item. + oneof item { + SnapshotStoreItem store = 1; + SnapshotIAVLItem iavl = 2 [(gogoproto.customname) = "IAVL"]; + SnapshotExtensionMeta extension = 3; + SnapshotExtensionPayload extension_payload = 4; + } +} + +// SnapshotStoreItem contains metadata about a snapshotted store. +message SnapshotStoreItem { + string name = 1; +} + +// SnapshotIAVLItem is an exported IAVL node. +message SnapshotIAVLItem { + bytes key = 1; + bytes value = 2; + // version is block height + int64 version = 3; + // height is depth of the tree. + int32 height = 4; +} + +// SnapshotExtensionMeta contains metadata about an external snapshotter. +message SnapshotExtensionMeta { + string name = 1; + uint32 format = 2; +} + +// SnapshotExtensionPayload contains payloads of an external snapshotter. +message SnapshotExtensionPayload { + bytes payload = 1; +} diff --git a/ampd/proto/third_party/cosmos/base/store/v1beta1/commit_info.proto b/ampd/proto/third_party/cosmos/base/store/v1beta1/commit_info.proto new file mode 100644 index 000000000..98a33d30e --- /dev/null +++ b/ampd/proto/third_party/cosmos/base/store/v1beta1/commit_info.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; +package cosmos.base.store.v1beta1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/store/types"; + +// CommitInfo defines commit information used by the multi-store when committing +// a version/height. +message CommitInfo { + int64 version = 1; + repeated StoreInfo store_infos = 2 [(gogoproto.nullable) = false]; +} + +// StoreInfo defines store-specific commit information. It contains a reference +// between a store name and the commit ID. +message StoreInfo { + string name = 1; + CommitID commit_id = 2 [(gogoproto.nullable) = false]; +} + +// CommitID defines the committment information when a specific store is +// committed. +message CommitID { + option (gogoproto.goproto_stringer) = false; + + int64 version = 1; + bytes hash = 2; +} diff --git a/ampd/proto/third_party/cosmos/base/store/v1beta1/listening.proto b/ampd/proto/third_party/cosmos/base/store/v1beta1/listening.proto new file mode 100644 index 000000000..753f7c165 --- /dev/null +++ b/ampd/proto/third_party/cosmos/base/store/v1beta1/listening.proto @@ -0,0 +1,34 @@ +syntax = "proto3"; +package cosmos.base.store.v1beta1; + +import "tendermint/abci/types.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/store/types"; + +// StoreKVPair is a KVStore KVPair used for listening to state changes (Sets and Deletes) +// It optionally includes the StoreKey for the originating KVStore and a Boolean flag to distinguish between Sets and +// Deletes +// +// Since: cosmos-sdk 0.43 +message StoreKVPair { + string store_key = 1; // the store key for the KVStore this pair originates from + bool delete = 2; // true indicates a delete operation, false indicates a set operation + bytes key = 3; + bytes value = 4; +} + +// BlockMetadata contains all the abci event data of a block +// the file streamer dump them into files together with the state changes. +message BlockMetadata { + // DeliverTx encapulate deliver tx request and response. + message DeliverTx { + tendermint.abci.RequestDeliverTx request = 1; + tendermint.abci.ResponseDeliverTx response = 2; + } + tendermint.abci.RequestBeginBlock request_begin_block = 1; + tendermint.abci.ResponseBeginBlock response_begin_block = 2; + repeated DeliverTx deliver_txs = 3; + tendermint.abci.RequestEndBlock request_end_block = 4; + tendermint.abci.ResponseEndBlock response_end_block = 5; + tendermint.abci.ResponseCommit response_commit = 6; +} diff --git a/ampd/proto/third_party/cosmos/base/tendermint/v1beta1/query.proto b/ampd/proto/third_party/cosmos/base/tendermint/v1beta1/query.proto new file mode 100644 index 000000000..98542d23d --- /dev/null +++ b/ampd/proto/third_party/cosmos/base/tendermint/v1beta1/query.proto @@ -0,0 +1,138 @@ +syntax = "proto3"; +package cosmos.base.tendermint.v1beta1; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "google/api/annotations.proto"; +import "tendermint/p2p/types.proto"; +import "tendermint/types/block.proto"; +import "tendermint/types/types.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/client/grpc/tmservice"; + +// Service defines the gRPC querier service for tendermint queries. +service Service { + // GetNodeInfo queries the current node info. + rpc GetNodeInfo(GetNodeInfoRequest) returns (GetNodeInfoResponse) { + option (google.api.http).get = "/cosmos/base/tendermint/v1beta1/node_info"; + } + // GetSyncing queries node syncing. + rpc GetSyncing(GetSyncingRequest) returns (GetSyncingResponse) { + option (google.api.http).get = "/cosmos/base/tendermint/v1beta1/syncing"; + } + // GetLatestBlock returns the latest block. + rpc GetLatestBlock(GetLatestBlockRequest) returns (GetLatestBlockResponse) { + option (google.api.http).get = "/cosmos/base/tendermint/v1beta1/blocks/latest"; + } + // GetBlockByHeight queries block for given height. + rpc GetBlockByHeight(GetBlockByHeightRequest) returns (GetBlockByHeightResponse) { + option (google.api.http).get = "/cosmos/base/tendermint/v1beta1/blocks/{height}"; + } + + // GetLatestValidatorSet queries latest validator-set. + rpc GetLatestValidatorSet(GetLatestValidatorSetRequest) returns (GetLatestValidatorSetResponse) { + option (google.api.http).get = "/cosmos/base/tendermint/v1beta1/validatorsets/latest"; + } + // GetValidatorSetByHeight queries validator-set at a given height. + rpc GetValidatorSetByHeight(GetValidatorSetByHeightRequest) returns (GetValidatorSetByHeightResponse) { + option (google.api.http).get = "/cosmos/base/tendermint/v1beta1/validatorsets/{height}"; + } +} + +// GetValidatorSetByHeightRequest is the request type for the Query/GetValidatorSetByHeight RPC method. +message GetValidatorSetByHeightRequest { + int64 height = 1; + // pagination defines an pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// GetValidatorSetByHeightResponse is the response type for the Query/GetValidatorSetByHeight RPC method. +message GetValidatorSetByHeightResponse { + int64 block_height = 1; + repeated Validator validators = 2; + // pagination defines an pagination for the response. + cosmos.base.query.v1beta1.PageResponse pagination = 3; +} + +// GetLatestValidatorSetRequest is the request type for the Query/GetValidatorSetByHeight RPC method. +message GetLatestValidatorSetRequest { + // pagination defines an pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +// GetLatestValidatorSetResponse is the response type for the Query/GetValidatorSetByHeight RPC method. +message GetLatestValidatorSetResponse { + int64 block_height = 1; + repeated Validator validators = 2; + // pagination defines an pagination for the response. + cosmos.base.query.v1beta1.PageResponse pagination = 3; +} + +// Validator is the type for the validator-set. +message Validator { + string address = 1; + google.protobuf.Any pub_key = 2; + int64 voting_power = 3; + int64 proposer_priority = 4; +} + +// GetBlockByHeightRequest is the request type for the Query/GetBlockByHeight RPC method. +message GetBlockByHeightRequest { + int64 height = 1; +} + +// GetBlockByHeightResponse is the response type for the Query/GetBlockByHeight RPC method. +message GetBlockByHeightResponse { + .tendermint.types.BlockID block_id = 1; + .tendermint.types.Block block = 2; +} + +// GetLatestBlockRequest is the request type for the Query/GetLatestBlock RPC method. +message GetLatestBlockRequest {} + +// GetLatestBlockResponse is the response type for the Query/GetLatestBlock RPC method. +message GetLatestBlockResponse { + .tendermint.types.BlockID block_id = 1; + .tendermint.types.Block block = 2; +} + +// GetSyncingRequest is the request type for the Query/GetSyncing RPC method. +message GetSyncingRequest {} + +// GetSyncingResponse is the response type for the Query/GetSyncing RPC method. +message GetSyncingResponse { + bool syncing = 1; +} + +// GetNodeInfoRequest is the request type for the Query/GetNodeInfo RPC method. +message GetNodeInfoRequest {} + +// GetNodeInfoResponse is the request type for the Query/GetNodeInfo RPC method. +message GetNodeInfoResponse { + .tendermint.p2p.DefaultNodeInfo default_node_info = 1; + VersionInfo application_version = 2; +} + +// VersionInfo is the type for the GetNodeInfoResponse message. +message VersionInfo { + string name = 1; + string app_name = 2; + string version = 3; + string git_commit = 4; + string build_tags = 5; + string go_version = 6; + repeated Module build_deps = 7; + // Since: cosmos-sdk 0.43 + string cosmos_sdk_version = 8; +} + +// Module is the type for VersionInfo +message Module { + // module path + string path = 1; + // module version + string version = 2; + // checksum + string sum = 3; +} diff --git a/ampd/proto/third_party/cosmos/base/v1beta1/coin.proto b/ampd/proto/third_party/cosmos/base/v1beta1/coin.proto new file mode 100644 index 000000000..fab75284b --- /dev/null +++ b/ampd/proto/third_party/cosmos/base/v1beta1/coin.proto @@ -0,0 +1,40 @@ +syntax = "proto3"; +package cosmos.base.v1beta1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/types"; +option (gogoproto.goproto_stringer_all) = false; +option (gogoproto.stringer_all) = false; + +// Coin defines a token with a denomination and an amount. +// +// NOTE: The amount field is an Int which implements the custom method +// signatures required by gogoproto. +message Coin { + option (gogoproto.equal) = true; + + string denom = 1; + string amount = 2 [(gogoproto.customtype) = "Int", (gogoproto.nullable) = false]; +} + +// DecCoin defines a token with a denomination and a decimal amount. +// +// NOTE: The amount field is an Dec which implements the custom method +// signatures required by gogoproto. +message DecCoin { + option (gogoproto.equal) = true; + + string denom = 1; + string amount = 2 [(gogoproto.customtype) = "Dec", (gogoproto.nullable) = false]; +} + +// IntProto defines a Protobuf wrapper around an Int object. +message IntProto { + string int = 1 [(gogoproto.customtype) = "Int", (gogoproto.nullable) = false]; +} + +// DecProto defines a Protobuf wrapper around a Dec object. +message DecProto { + string dec = 1 [(gogoproto.customtype) = "Dec", (gogoproto.nullable) = false]; +} diff --git a/ampd/proto/third_party/cosmos/capability/v1beta1/capability.proto b/ampd/proto/third_party/cosmos/capability/v1beta1/capability.proto new file mode 100644 index 000000000..1c8332f34 --- /dev/null +++ b/ampd/proto/third_party/cosmos/capability/v1beta1/capability.proto @@ -0,0 +1,30 @@ +syntax = "proto3"; +package cosmos.capability.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/capability/types"; + +import "gogoproto/gogo.proto"; + +// Capability defines an implementation of an object capability. The index +// provided to a Capability must be globally unique. +message Capability { + option (gogoproto.goproto_stringer) = false; + + uint64 index = 1 [(gogoproto.moretags) = "yaml:\"index\""]; +} + +// Owner defines a single capability owner. An owner is defined by the name of +// capability and the module name. +message Owner { + option (gogoproto.goproto_stringer) = false; + option (gogoproto.goproto_getters) = false; + + string module = 1 [(gogoproto.moretags) = "yaml:\"module\""]; + string name = 2 [(gogoproto.moretags) = "yaml:\"name\""]; +} + +// CapabilityOwners defines a set of owners of a single Capability. The set of +// owners must be unique. +message CapabilityOwners { + repeated Owner owners = 1 [(gogoproto.nullable) = false]; +} diff --git a/ampd/proto/third_party/cosmos/capability/v1beta1/genesis.proto b/ampd/proto/third_party/cosmos/capability/v1beta1/genesis.proto new file mode 100644 index 000000000..05bb0afc4 --- /dev/null +++ b/ampd/proto/third_party/cosmos/capability/v1beta1/genesis.proto @@ -0,0 +1,26 @@ +syntax = "proto3"; +package cosmos.capability.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/capability/v1beta1/capability.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/capability/types"; + +// GenesisOwners defines the capability owners with their corresponding index. +message GenesisOwners { + // index is the index of the capability owner. + uint64 index = 1; + + // index_owners are the owners at the given index. + CapabilityOwners index_owners = 2 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"index_owners\""]; +} + +// GenesisState defines the capability module's genesis state. +message GenesisState { + // index is the capability global index. + uint64 index = 1; + + // owners represents a map from index to owners of the capability index + // index key is string to allow amino marshalling. + repeated GenesisOwners owners = 2 [(gogoproto.nullable) = false]; +} diff --git a/ampd/proto/third_party/cosmos/crisis/v1beta1/genesis.proto b/ampd/proto/third_party/cosmos/crisis/v1beta1/genesis.proto new file mode 100644 index 000000000..5b0ff7ec7 --- /dev/null +++ b/ampd/proto/third_party/cosmos/crisis/v1beta1/genesis.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; +package cosmos.crisis.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/crisis/types"; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +// GenesisState defines the crisis module's genesis state. +message GenesisState { + // constant_fee is the fee used to verify the invariant in the crisis + // module. + cosmos.base.v1beta1.Coin constant_fee = 3 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"constant_fee\""]; +} diff --git a/ampd/proto/third_party/cosmos/crisis/v1beta1/tx.proto b/ampd/proto/third_party/cosmos/crisis/v1beta1/tx.proto new file mode 100644 index 000000000..26457ad6d --- /dev/null +++ b/ampd/proto/third_party/cosmos/crisis/v1beta1/tx.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; +package cosmos.crisis.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/crisis/types"; + +import "gogoproto/gogo.proto"; + +// Msg defines the bank Msg service. +service Msg { + // VerifyInvariant defines a method to verify a particular invariance. + rpc VerifyInvariant(MsgVerifyInvariant) returns (MsgVerifyInvariantResponse); +} + +// MsgVerifyInvariant represents a message to verify a particular invariance. +message MsgVerifyInvariant { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string sender = 1; + string invariant_module_name = 2 [(gogoproto.moretags) = "yaml:\"invariant_module_name\""]; + string invariant_route = 3 [(gogoproto.moretags) = "yaml:\"invariant_route\""]; +} + +// MsgVerifyInvariantResponse defines the Msg/VerifyInvariant response type. +message MsgVerifyInvariantResponse {} diff --git a/ampd/proto/third_party/cosmos/crypto/ed25519/keys.proto b/ampd/proto/third_party/cosmos/crypto/ed25519/keys.proto new file mode 100644 index 000000000..6ffec3448 --- /dev/null +++ b/ampd/proto/third_party/cosmos/crypto/ed25519/keys.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; +package cosmos.crypto.ed25519; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"; + +// PubKey is an ed25519 public key for handling Tendermint keys in SDK. +// It's needed for Any serialization and SDK compatibility. +// It must not be used in a non Tendermint key context because it doesn't implement +// ADR-28. Nevertheless, you will like to use ed25519 in app user level +// then you must create a new proto message and follow ADR-28 for Address construction. +message PubKey { + option (gogoproto.goproto_stringer) = false; + + bytes key = 1 [(gogoproto.casttype) = "crypto/ed25519.PublicKey"]; +} + +// Deprecated: PrivKey defines a ed25519 private key. +// NOTE: ed25519 keys must not be used in SDK apps except in a tendermint validator context. +message PrivKey { + bytes key = 1 [(gogoproto.casttype) = "crypto/ed25519.PrivateKey"]; +} diff --git a/ampd/proto/third_party/cosmos/crypto/multisig/keys.proto b/ampd/proto/third_party/cosmos/crypto/multisig/keys.proto new file mode 100644 index 000000000..f8398e805 --- /dev/null +++ b/ampd/proto/third_party/cosmos/crypto/multisig/keys.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; +package cosmos.crypto.multisig; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/crypto/keys/multisig"; + +// LegacyAminoPubKey specifies a public key type +// which nests multiple public keys and a threshold, +// it uses legacy amino address rules. +message LegacyAminoPubKey { + option (gogoproto.goproto_getters) = false; + + uint32 threshold = 1 [(gogoproto.moretags) = "yaml:\"threshold\""]; + repeated google.protobuf.Any public_keys = 2 + [(gogoproto.customname) = "PubKeys", (gogoproto.moretags) = "yaml:\"pubkeys\""]; +} diff --git a/ampd/proto/third_party/cosmos/crypto/multisig/v1beta1/multisig.proto b/ampd/proto/third_party/cosmos/crypto/multisig/v1beta1/multisig.proto new file mode 100644 index 000000000..bf671f171 --- /dev/null +++ b/ampd/proto/third_party/cosmos/crypto/multisig/v1beta1/multisig.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; +package cosmos.crypto.multisig.v1beta1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/crypto/types"; + +// MultiSignature wraps the signatures from a multisig.LegacyAminoPubKey. +// See cosmos.tx.v1betata1.ModeInfo.Multi for how to specify which signers +// signed and with which modes. +message MultiSignature { + option (gogoproto.goproto_unrecognized) = true; + repeated bytes signatures = 1; +} + +// CompactBitArray is an implementation of a space efficient bit array. +// This is used to ensure that the encoded data takes up a minimal amount of +// space after proto encoding. +// This is not thread safe, and is not intended for concurrent usage. +message CompactBitArray { + option (gogoproto.goproto_stringer) = false; + + uint32 extra_bits_stored = 1; + bytes elems = 2; +} diff --git a/ampd/proto/third_party/cosmos/crypto/secp256k1/keys.proto b/ampd/proto/third_party/cosmos/crypto/secp256k1/keys.proto new file mode 100644 index 000000000..a22725713 --- /dev/null +++ b/ampd/proto/third_party/cosmos/crypto/secp256k1/keys.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; +package cosmos.crypto.secp256k1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"; + +// PubKey defines a secp256k1 public key +// Key is the compressed form of the pubkey. The first byte depends is a 0x02 byte +// if the y-coordinate is the lexicographically largest of the two associated with +// the x-coordinate. Otherwise the first byte is a 0x03. +// This prefix is followed with the x-coordinate. +message PubKey { + option (gogoproto.goproto_stringer) = false; + + bytes key = 1; +} + +// PrivKey defines a secp256k1 private key. +message PrivKey { + bytes key = 1; +} diff --git a/ampd/proto/third_party/cosmos/crypto/secp256r1/keys.proto b/ampd/proto/third_party/cosmos/crypto/secp256r1/keys.proto new file mode 100644 index 000000000..2e96c6e3c --- /dev/null +++ b/ampd/proto/third_party/cosmos/crypto/secp256r1/keys.proto @@ -0,0 +1,23 @@ +// Since: cosmos-sdk 0.43 +syntax = "proto3"; +package cosmos.crypto.secp256r1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/crypto/keys/secp256r1"; +option (gogoproto.messagename_all) = true; +option (gogoproto.goproto_stringer_all) = false; +option (gogoproto.goproto_getters_all) = false; + +// PubKey defines a secp256r1 ECDSA public key. +message PubKey { + // Point on secp256r1 curve in a compressed representation as specified in section + // 4.3.6 of ANSI X9.62: https://webstore.ansi.org/standards/ascx9/ansix9621998 + bytes key = 1 [(gogoproto.customtype) = "ecdsaPK"]; +} + +// PrivKey defines a secp256r1 ECDSA private key. +message PrivKey { + // secret number serialized using big-endian encoding + bytes secret = 1 [(gogoproto.customtype) = "ecdsaSK"]; +} diff --git a/ampd/proto/third_party/cosmos/distribution/v1beta1/distribution.proto b/ampd/proto/third_party/cosmos/distribution/v1beta1/distribution.proto new file mode 100644 index 000000000..ae98ec0b9 --- /dev/null +++ b/ampd/proto/third_party/cosmos/distribution/v1beta1/distribution.proto @@ -0,0 +1,157 @@ +syntax = "proto3"; +package cosmos.distribution.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/distribution/types"; +option (gogoproto.equal_all) = true; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +// Params defines the set of params for the distribution module. +message Params { + option (gogoproto.goproto_stringer) = false; + string community_tax = 1 [ + (gogoproto.moretags) = "yaml:\"community_tax\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string base_proposer_reward = 2 [ + (gogoproto.moretags) = "yaml:\"base_proposer_reward\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string bonus_proposer_reward = 3 [ + (gogoproto.moretags) = "yaml:\"bonus_proposer_reward\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + bool withdraw_addr_enabled = 4 [(gogoproto.moretags) = "yaml:\"withdraw_addr_enabled\""]; +} + +// ValidatorHistoricalRewards represents historical rewards for a validator. +// Height is implicit within the store key. +// Cumulative reward ratio is the sum from the zeroeth period +// until this period of rewards / tokens, per the spec. +// The reference count indicates the number of objects +// which might need to reference this historical entry at any point. +// ReferenceCount = +// number of outstanding delegations which ended the associated period (and +// might need to read that record) +// + number of slashes which ended the associated period (and might need to +// read that record) +// + one per validator for the zeroeth period, set on initialization +message ValidatorHistoricalRewards { + repeated cosmos.base.v1beta1.DecCoin cumulative_reward_ratio = 1 [ + (gogoproto.moretags) = "yaml:\"cumulative_reward_ratio\"", + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins", + (gogoproto.nullable) = false + ]; + uint32 reference_count = 2 [(gogoproto.moretags) = "yaml:\"reference_count\""]; +} + +// ValidatorCurrentRewards represents current rewards and current +// period for a validator kept as a running counter and incremented +// each block as long as the validator's tokens remain constant. +message ValidatorCurrentRewards { + repeated cosmos.base.v1beta1.DecCoin rewards = 1 + [(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins", (gogoproto.nullable) = false]; + uint64 period = 2; +} + +// ValidatorAccumulatedCommission represents accumulated commission +// for a validator kept as a running counter, can be withdrawn at any time. +message ValidatorAccumulatedCommission { + repeated cosmos.base.v1beta1.DecCoin commission = 1 + [(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins", (gogoproto.nullable) = false]; +} + +// ValidatorOutstandingRewards represents outstanding (un-withdrawn) rewards +// for a validator inexpensive to track, allows simple sanity checks. +message ValidatorOutstandingRewards { + repeated cosmos.base.v1beta1.DecCoin rewards = 1 [ + (gogoproto.moretags) = "yaml:\"rewards\"", + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins", + (gogoproto.nullable) = false + ]; +} + +// ValidatorSlashEvent represents a validator slash event. +// Height is implicit within the store key. +// This is needed to calculate appropriate amount of staking tokens +// for delegations which are withdrawn after a slash has occurred. +message ValidatorSlashEvent { + uint64 validator_period = 1 [(gogoproto.moretags) = "yaml:\"validator_period\""]; + string fraction = 2 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false]; +} + +// ValidatorSlashEvents is a collection of ValidatorSlashEvent messages. +message ValidatorSlashEvents { + option (gogoproto.goproto_stringer) = false; + repeated ValidatorSlashEvent validator_slash_events = 1 + [(gogoproto.moretags) = "yaml:\"validator_slash_events\"", (gogoproto.nullable) = false]; +} + +// FeePool is the global fee pool for distribution. +message FeePool { + repeated cosmos.base.v1beta1.DecCoin community_pool = 1 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins", + (gogoproto.moretags) = "yaml:\"community_pool\"" + ]; +} + +// CommunityPoolSpendProposal details a proposal for use of community funds, +// together with how many coins are proposed to be spent, and to which +// recipient account. +message CommunityPoolSpendProposal { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + string title = 1; + string description = 2; + string recipient = 3; + repeated cosmos.base.v1beta1.Coin amount = 4 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; +} + +// DelegatorStartingInfo represents the starting info for a delegator reward +// period. It tracks the previous validator period, the delegation's amount of +// staking token, and the creation height (to check later on if any slashes have +// occurred). NOTE: Even though validators are slashed to whole staking tokens, +// the delegators within the validator may be left with less than a full token, +// thus sdk.Dec is used. +message DelegatorStartingInfo { + uint64 previous_period = 1 [(gogoproto.moretags) = "yaml:\"previous_period\""]; + string stake = 2 [ + (gogoproto.moretags) = "yaml:\"stake\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + uint64 height = 3 [(gogoproto.moretags) = "yaml:\"creation_height\"", (gogoproto.jsontag) = "creation_height"]; +} + +// DelegationDelegatorReward represents the properties +// of a delegator's delegation reward. +message DelegationDelegatorReward { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = true; + + string validator_address = 1 [(gogoproto.moretags) = "yaml:\"validator_address\""]; + + repeated cosmos.base.v1beta1.DecCoin reward = 2 + [(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins", (gogoproto.nullable) = false]; +} + +// CommunityPoolSpendProposalWithDeposit defines a CommunityPoolSpendProposal +// with a deposit +message CommunityPoolSpendProposalWithDeposit { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = true; + + string title = 1 [(gogoproto.moretags) = "yaml:\"title\""]; + string description = 2 [(gogoproto.moretags) = "yaml:\"description\""]; + string recipient = 3 [(gogoproto.moretags) = "yaml:\"recipient\""]; + string amount = 4 [(gogoproto.moretags) = "yaml:\"amount\""]; + string deposit = 5 [(gogoproto.moretags) = "yaml:\"deposit\""]; +} diff --git a/ampd/proto/third_party/cosmos/distribution/v1beta1/genesis.proto b/ampd/proto/third_party/cosmos/distribution/v1beta1/genesis.proto new file mode 100644 index 000000000..c0b17cdf1 --- /dev/null +++ b/ampd/proto/third_party/cosmos/distribution/v1beta1/genesis.proto @@ -0,0 +1,155 @@ +syntax = "proto3"; +package cosmos.distribution.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/distribution/types"; +option (gogoproto.equal_all) = true; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/distribution/v1beta1/distribution.proto"; + +// DelegatorWithdrawInfo is the address for where distributions rewards are +// withdrawn to by default this struct is only used at genesis to feed in +// default withdraw addresses. +message DelegatorWithdrawInfo { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // delegator_address is the address of the delegator. + string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + + // withdraw_address is the address to withdraw the delegation rewards to. + string withdraw_address = 2 [(gogoproto.moretags) = "yaml:\"withdraw_address\""]; +} + +// ValidatorOutstandingRewardsRecord is used for import/export via genesis json. +message ValidatorOutstandingRewardsRecord { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // validator_address is the address of the validator. + string validator_address = 1 [(gogoproto.moretags) = "yaml:\"validator_address\""]; + + // outstanding_rewards represents the oustanding rewards of a validator. + repeated cosmos.base.v1beta1.DecCoin outstanding_rewards = 2 [ + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins", + (gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"outstanding_rewards\"" + ]; +} + +// ValidatorAccumulatedCommissionRecord is used for import / export via genesis +// json. +message ValidatorAccumulatedCommissionRecord { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // validator_address is the address of the validator. + string validator_address = 1 [(gogoproto.moretags) = "yaml:\"validator_address\""]; + + // accumulated is the accumulated commission of a validator. + ValidatorAccumulatedCommission accumulated = 2 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"accumulated\""]; +} + +// ValidatorHistoricalRewardsRecord is used for import / export via genesis +// json. +message ValidatorHistoricalRewardsRecord { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // validator_address is the address of the validator. + string validator_address = 1 [(gogoproto.moretags) = "yaml:\"validator_address\""]; + + // period defines the period the historical rewards apply to. + uint64 period = 2; + + // rewards defines the historical rewards of a validator. + ValidatorHistoricalRewards rewards = 3 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"rewards\""]; +} + +// ValidatorCurrentRewardsRecord is used for import / export via genesis json. +message ValidatorCurrentRewardsRecord { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // validator_address is the address of the validator. + string validator_address = 1 [(gogoproto.moretags) = "yaml:\"validator_address\""]; + + // rewards defines the current rewards of a validator. + ValidatorCurrentRewards rewards = 2 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"rewards\""]; +} + +// DelegatorStartingInfoRecord used for import / export via genesis json. +message DelegatorStartingInfoRecord { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // delegator_address is the address of the delegator. + string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + + // validator_address is the address of the validator. + string validator_address = 2 [(gogoproto.moretags) = "yaml:\"validator_address\""]; + + // starting_info defines the starting info of a delegator. + DelegatorStartingInfo starting_info = 3 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"starting_info\""]; +} + +// ValidatorSlashEventRecord is used for import / export via genesis json. +message ValidatorSlashEventRecord { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // validator_address is the address of the validator. + string validator_address = 1 [(gogoproto.moretags) = "yaml:\"validator_address\""]; + // height defines the block height at which the slash event occured. + uint64 height = 2; + // period is the period of the slash event. + uint64 period = 3; + // validator_slash_event describes the slash event. + ValidatorSlashEvent validator_slash_event = 4 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"event\""]; +} + +// GenesisState defines the distribution module's genesis state. +message GenesisState { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // params defines all the paramaters of the module. + Params params = 1 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"params\""]; + + // fee_pool defines the fee pool at genesis. + FeePool fee_pool = 2 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"fee_pool\""]; + + // fee_pool defines the delegator withdraw infos at genesis. + repeated DelegatorWithdrawInfo delegator_withdraw_infos = 3 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"delegator_withdraw_infos\""]; + + // fee_pool defines the previous proposer at genesis. + string previous_proposer = 4 [(gogoproto.moretags) = "yaml:\"previous_proposer\""]; + + // fee_pool defines the outstanding rewards of all validators at genesis. + repeated ValidatorOutstandingRewardsRecord outstanding_rewards = 5 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"outstanding_rewards\""]; + + // fee_pool defines the accumulated commisions of all validators at genesis. + repeated ValidatorAccumulatedCommissionRecord validator_accumulated_commissions = 6 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"validator_accumulated_commissions\""]; + + // fee_pool defines the historical rewards of all validators at genesis. + repeated ValidatorHistoricalRewardsRecord validator_historical_rewards = 7 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"validator_historical_rewards\""]; + + // fee_pool defines the current rewards of all validators at genesis. + repeated ValidatorCurrentRewardsRecord validator_current_rewards = 8 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"validator_current_rewards\""]; + + // fee_pool defines the delegator starting infos at genesis. + repeated DelegatorStartingInfoRecord delegator_starting_infos = 9 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"delegator_starting_infos\""]; + + // fee_pool defines the validator slash events at genesis. + repeated ValidatorSlashEventRecord validator_slash_events = 10 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"validator_slash_events\""]; +} diff --git a/ampd/proto/third_party/cosmos/distribution/v1beta1/query.proto b/ampd/proto/third_party/cosmos/distribution/v1beta1/query.proto new file mode 100644 index 000000000..2991218d8 --- /dev/null +++ b/ampd/proto/third_party/cosmos/distribution/v1beta1/query.proto @@ -0,0 +1,218 @@ +syntax = "proto3"; +package cosmos.distribution.v1beta1; + +import "cosmos/base/query/v1beta1/pagination.proto"; +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/distribution/v1beta1/distribution.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/distribution/types"; + +// Query defines the gRPC querier service for distribution module. +service Query { + // Params queries params of the distribution module. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/cosmos/distribution/v1beta1/params"; + } + + // ValidatorOutstandingRewards queries rewards of a validator address. + rpc ValidatorOutstandingRewards(QueryValidatorOutstandingRewardsRequest) + returns (QueryValidatorOutstandingRewardsResponse) { + option (google.api.http).get = "/cosmos/distribution/v1beta1/validators/" + "{validator_address}/outstanding_rewards"; + } + + // ValidatorCommission queries accumulated commission for a validator. + rpc ValidatorCommission(QueryValidatorCommissionRequest) returns (QueryValidatorCommissionResponse) { + option (google.api.http).get = "/cosmos/distribution/v1beta1/validators/" + "{validator_address}/commission"; + } + + // ValidatorSlashes queries slash events of a validator. + rpc ValidatorSlashes(QueryValidatorSlashesRequest) returns (QueryValidatorSlashesResponse) { + option (google.api.http).get = "/cosmos/distribution/v1beta1/validators/{validator_address}/slashes"; + } + + // DelegationRewards queries the total rewards accrued by a delegation. + rpc DelegationRewards(QueryDelegationRewardsRequest) returns (QueryDelegationRewardsResponse) { + option (google.api.http).get = "/cosmos/distribution/v1beta1/delegators/{delegator_address}/rewards/" + "{validator_address}"; + } + + // DelegationTotalRewards queries the total rewards accrued by a each + // validator. + rpc DelegationTotalRewards(QueryDelegationTotalRewardsRequest) returns (QueryDelegationTotalRewardsResponse) { + option (google.api.http).get = "/cosmos/distribution/v1beta1/delegators/{delegator_address}/rewards"; + } + + // DelegatorValidators queries the validators of a delegator. + rpc DelegatorValidators(QueryDelegatorValidatorsRequest) returns (QueryDelegatorValidatorsResponse) { + option (google.api.http).get = "/cosmos/distribution/v1beta1/delegators/" + "{delegator_address}/validators"; + } + + // DelegatorWithdrawAddress queries withdraw address of a delegator. + rpc DelegatorWithdrawAddress(QueryDelegatorWithdrawAddressRequest) returns (QueryDelegatorWithdrawAddressResponse) { + option (google.api.http).get = "/cosmos/distribution/v1beta1/delegators/" + "{delegator_address}/withdraw_address"; + } + + // CommunityPool queries the community pool coins. + rpc CommunityPool(QueryCommunityPoolRequest) returns (QueryCommunityPoolResponse) { + option (google.api.http).get = "/cosmos/distribution/v1beta1/community_pool"; + } +} + +// QueryParamsRequest is the request type for the Query/Params RPC method. +message QueryParamsRequest {} + +// QueryParamsResponse is the response type for the Query/Params RPC method. +message QueryParamsResponse { + // params defines the parameters of the module. + Params params = 1 [(gogoproto.nullable) = false]; +} + +// QueryValidatorOutstandingRewardsRequest is the request type for the +// Query/ValidatorOutstandingRewards RPC method. +message QueryValidatorOutstandingRewardsRequest { + // validator_address defines the validator address to query for. + string validator_address = 1; +} + +// QueryValidatorOutstandingRewardsResponse is the response type for the +// Query/ValidatorOutstandingRewards RPC method. +message QueryValidatorOutstandingRewardsResponse { + ValidatorOutstandingRewards rewards = 1 [(gogoproto.nullable) = false]; +} + +// QueryValidatorCommissionRequest is the request type for the +// Query/ValidatorCommission RPC method +message QueryValidatorCommissionRequest { + // validator_address defines the validator address to query for. + string validator_address = 1; +} + +// QueryValidatorCommissionResponse is the response type for the +// Query/ValidatorCommission RPC method +message QueryValidatorCommissionResponse { + // commission defines the commision the validator received. + ValidatorAccumulatedCommission commission = 1 [(gogoproto.nullable) = false]; +} + +// QueryValidatorSlashesRequest is the request type for the +// Query/ValidatorSlashes RPC method +message QueryValidatorSlashesRequest { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = true; + + // validator_address defines the validator address to query for. + string validator_address = 1; + // starting_height defines the optional starting height to query the slashes. + uint64 starting_height = 2; + // starting_height defines the optional ending height to query the slashes. + uint64 ending_height = 3; + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 4; +} + +// QueryValidatorSlashesResponse is the response type for the +// Query/ValidatorSlashes RPC method. +message QueryValidatorSlashesResponse { + // slashes defines the slashes the validator received. + repeated ValidatorSlashEvent slashes = 1 [(gogoproto.nullable) = false]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryDelegationRewardsRequest is the request type for the +// Query/DelegationRewards RPC method. +message QueryDelegationRewardsRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // delegator_address defines the delegator address to query for. + string delegator_address = 1; + // validator_address defines the validator address to query for. + string validator_address = 2; +} + +// QueryDelegationRewardsResponse is the response type for the +// Query/DelegationRewards RPC method. +message QueryDelegationRewardsResponse { + // rewards defines the rewards accrued by a delegation. + repeated cosmos.base.v1beta1.DecCoin rewards = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins"]; +} + +// QueryDelegationTotalRewardsRequest is the request type for the +// Query/DelegationTotalRewards RPC method. +message QueryDelegationTotalRewardsRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + // delegator_address defines the delegator address to query for. + string delegator_address = 1; +} + +// QueryDelegationTotalRewardsResponse is the response type for the +// Query/DelegationTotalRewards RPC method. +message QueryDelegationTotalRewardsResponse { + // rewards defines all the rewards accrued by a delegator. + repeated DelegationDelegatorReward rewards = 1 [(gogoproto.nullable) = false]; + // total defines the sum of all the rewards. + repeated cosmos.base.v1beta1.DecCoin total = 2 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins"]; +} + +// QueryDelegatorValidatorsRequest is the request type for the +// Query/DelegatorValidators RPC method. +message QueryDelegatorValidatorsRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // delegator_address defines the delegator address to query for. + string delegator_address = 1; +} + +// QueryDelegatorValidatorsResponse is the response type for the +// Query/DelegatorValidators RPC method. +message QueryDelegatorValidatorsResponse { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // validators defines the validators a delegator is delegating for. + repeated string validators = 1; +} + +// QueryDelegatorWithdrawAddressRequest is the request type for the +// Query/DelegatorWithdrawAddress RPC method. +message QueryDelegatorWithdrawAddressRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // delegator_address defines the delegator address to query for. + string delegator_address = 1; +} + +// QueryDelegatorWithdrawAddressResponse is the response type for the +// Query/DelegatorWithdrawAddress RPC method. +message QueryDelegatorWithdrawAddressResponse { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // withdraw_address defines the delegator address to query for. + string withdraw_address = 1; +} + +// QueryCommunityPoolRequest is the request type for the Query/CommunityPool RPC +// method. +message QueryCommunityPoolRequest {} + +// QueryCommunityPoolResponse is the response type for the Query/CommunityPool +// RPC method. +message QueryCommunityPoolResponse { + // pool defines community pool's coins. + repeated cosmos.base.v1beta1.DecCoin pool = 1 + [(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins", (gogoproto.nullable) = false]; +} diff --git a/ampd/proto/third_party/cosmos/distribution/v1beta1/tx.proto b/ampd/proto/third_party/cosmos/distribution/v1beta1/tx.proto new file mode 100644 index 000000000..e6ce478bc --- /dev/null +++ b/ampd/proto/third_party/cosmos/distribution/v1beta1/tx.proto @@ -0,0 +1,79 @@ +syntax = "proto3"; +package cosmos.distribution.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/distribution/types"; +option (gogoproto.equal_all) = true; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +// Msg defines the distribution Msg service. +service Msg { + // SetWithdrawAddress defines a method to change the withdraw address + // for a delegator (or validator self-delegation). + rpc SetWithdrawAddress(MsgSetWithdrawAddress) returns (MsgSetWithdrawAddressResponse); + + // WithdrawDelegatorReward defines a method to withdraw rewards of delegator + // from a single validator. + rpc WithdrawDelegatorReward(MsgWithdrawDelegatorReward) returns (MsgWithdrawDelegatorRewardResponse); + + // WithdrawValidatorCommission defines a method to withdraw the + // full commission to the validator address. + rpc WithdrawValidatorCommission(MsgWithdrawValidatorCommission) returns (MsgWithdrawValidatorCommissionResponse); + + // FundCommunityPool defines a method to allow an account to directly + // fund the community pool. + rpc FundCommunityPool(MsgFundCommunityPool) returns (MsgFundCommunityPoolResponse); +} + +// MsgSetWithdrawAddress sets the withdraw address for +// a delegator (or validator self-delegation). +message MsgSetWithdrawAddress { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + string withdraw_address = 2 [(gogoproto.moretags) = "yaml:\"withdraw_address\""]; +} + +// MsgSetWithdrawAddressResponse defines the Msg/SetWithdrawAddress response type. +message MsgSetWithdrawAddressResponse {} + +// MsgWithdrawDelegatorReward represents delegation withdrawal to a delegator +// from a single validator. +message MsgWithdrawDelegatorReward { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + string validator_address = 2 [(gogoproto.moretags) = "yaml:\"validator_address\""]; +} + +// MsgWithdrawDelegatorRewardResponse defines the Msg/WithdrawDelegatorReward response type. +message MsgWithdrawDelegatorRewardResponse {} + +// MsgWithdrawValidatorCommission withdraws the full commission to the validator +// address. +message MsgWithdrawValidatorCommission { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string validator_address = 1 [(gogoproto.moretags) = "yaml:\"validator_address\""]; +} + +// MsgWithdrawValidatorCommissionResponse defines the Msg/WithdrawValidatorCommission response type. +message MsgWithdrawValidatorCommissionResponse {} + +// MsgFundCommunityPool allows an account to directly +// fund the community pool. +message MsgFundCommunityPool { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + repeated cosmos.base.v1beta1.Coin amount = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + string depositor = 2; +} + +// MsgFundCommunityPoolResponse defines the Msg/FundCommunityPool response type. +message MsgFundCommunityPoolResponse {} diff --git a/ampd/proto/third_party/cosmos/evidence/v1beta1/evidence.proto b/ampd/proto/third_party/cosmos/evidence/v1beta1/evidence.proto new file mode 100644 index 000000000..14612c314 --- /dev/null +++ b/ampd/proto/third_party/cosmos/evidence/v1beta1/evidence.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; +package cosmos.evidence.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/evidence/types"; +option (gogoproto.equal_all) = true; + +import "gogoproto/gogo.proto"; +import "google/protobuf/timestamp.proto"; + +// Equivocation implements the Evidence interface and defines evidence of double +// signing misbehavior. +message Equivocation { + option (gogoproto.goproto_stringer) = false; + option (gogoproto.goproto_getters) = false; + option (gogoproto.equal) = false; + + int64 height = 1; + google.protobuf.Timestamp time = 2 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + int64 power = 3; + string consensus_address = 4 [(gogoproto.moretags) = "yaml:\"consensus_address\""]; +} \ No newline at end of file diff --git a/ampd/proto/third_party/cosmos/evidence/v1beta1/genesis.proto b/ampd/proto/third_party/cosmos/evidence/v1beta1/genesis.proto new file mode 100644 index 000000000..199f446f7 --- /dev/null +++ b/ampd/proto/third_party/cosmos/evidence/v1beta1/genesis.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; +package cosmos.evidence.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/evidence/types"; + +import "google/protobuf/any.proto"; + +// GenesisState defines the evidence module's genesis state. +message GenesisState { + // evidence defines all the evidence at genesis. + repeated google.protobuf.Any evidence = 1; +} diff --git a/ampd/proto/third_party/cosmos/evidence/v1beta1/query.proto b/ampd/proto/third_party/cosmos/evidence/v1beta1/query.proto new file mode 100644 index 000000000..eda00544c --- /dev/null +++ b/ampd/proto/third_party/cosmos/evidence/v1beta1/query.proto @@ -0,0 +1,51 @@ +syntax = "proto3"; +package cosmos.evidence.v1beta1; + +import "cosmos/base/query/v1beta1/pagination.proto"; +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "google/api/annotations.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/evidence/types"; + +// Query defines the gRPC querier service. +service Query { + // Evidence queries evidence based on evidence hash. + rpc Evidence(QueryEvidenceRequest) returns (QueryEvidenceResponse) { + option (google.api.http).get = "/cosmos/evidence/v1beta1/evidence/{evidence_hash}"; + } + + // AllEvidence queries all evidence. + rpc AllEvidence(QueryAllEvidenceRequest) returns (QueryAllEvidenceResponse) { + option (google.api.http).get = "/cosmos/evidence/v1beta1/evidence"; + } +} + +// QueryEvidenceRequest is the request type for the Query/Evidence RPC method. +message QueryEvidenceRequest { + // evidence_hash defines the hash of the requested evidence. + bytes evidence_hash = 1 [(gogoproto.casttype) = "github.com/tendermint/tendermint/libs/bytes.HexBytes"]; +} + +// QueryEvidenceResponse is the response type for the Query/Evidence RPC method. +message QueryEvidenceResponse { + // evidence returns the requested evidence. + google.protobuf.Any evidence = 1; +} + +// QueryEvidenceRequest is the request type for the Query/AllEvidence RPC +// method. +message QueryAllEvidenceRequest { + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +// QueryAllEvidenceResponse is the response type for the Query/AllEvidence RPC +// method. +message QueryAllEvidenceResponse { + // evidence returns all evidences. + repeated google.protobuf.Any evidence = 1; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} diff --git a/ampd/proto/third_party/cosmos/evidence/v1beta1/tx.proto b/ampd/proto/third_party/cosmos/evidence/v1beta1/tx.proto new file mode 100644 index 000000000..38795f25d --- /dev/null +++ b/ampd/proto/third_party/cosmos/evidence/v1beta1/tx.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; +package cosmos.evidence.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/evidence/types"; +option (gogoproto.equal_all) = true; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "cosmos_proto/cosmos.proto"; + +// Msg defines the evidence Msg service. +service Msg { + // SubmitEvidence submits an arbitrary Evidence of misbehavior such as equivocation or + // counterfactual signing. + rpc SubmitEvidence(MsgSubmitEvidence) returns (MsgSubmitEvidenceResponse); +} + +// MsgSubmitEvidence represents a message that supports submitting arbitrary +// Evidence of misbehavior such as equivocation or counterfactual signing. +message MsgSubmitEvidence { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string submitter = 1; + google.protobuf.Any evidence = 2 [(cosmos_proto.accepts_interface) = "Evidence"]; +} + +// MsgSubmitEvidenceResponse defines the Msg/SubmitEvidence response type. +message MsgSubmitEvidenceResponse { + // hash defines the hash of the evidence. + bytes hash = 4; +} diff --git a/ampd/proto/third_party/cosmos/feegrant/v1beta1/feegrant.proto b/ampd/proto/third_party/cosmos/feegrant/v1beta1/feegrant.proto new file mode 100644 index 000000000..a86691f91 --- /dev/null +++ b/ampd/proto/third_party/cosmos/feegrant/v1beta1/feegrant.proto @@ -0,0 +1,78 @@ +// Since: cosmos-sdk 0.43 +syntax = "proto3"; +package cosmos.feegrant.v1beta1; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/duration.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/feegrant"; + +// BasicAllowance implements Allowance with a one-time grant of tokens +// that optionally expires. The grantee can use up to SpendLimit to cover fees. +message BasicAllowance { + option (cosmos_proto.implements_interface) = "FeeAllowanceI"; + + // spend_limit specifies the maximum amount of tokens that can be spent + // by this allowance and will be updated as tokens are spent. If it is + // empty, there is no spend limit and any amount of coins can be spent. + repeated cosmos.base.v1beta1.Coin spend_limit = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + + // expiration specifies an optional time when this allowance expires + google.protobuf.Timestamp expiration = 2 [(gogoproto.stdtime) = true]; +} + +// PeriodicAllowance extends Allowance to allow for both a maximum cap, +// as well as a limit per time period. +message PeriodicAllowance { + option (cosmos_proto.implements_interface) = "FeeAllowanceI"; + + // basic specifies a struct of `BasicAllowance` + BasicAllowance basic = 1 [(gogoproto.nullable) = false]; + + // period specifies the time duration in which period_spend_limit coins can + // be spent before that allowance is reset + google.protobuf.Duration period = 2 [(gogoproto.stdduration) = true, (gogoproto.nullable) = false]; + + // period_spend_limit specifies the maximum number of coins that can be spent + // in the period + repeated cosmos.base.v1beta1.Coin period_spend_limit = 3 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + + // period_can_spend is the number of coins left to be spent before the period_reset time + repeated cosmos.base.v1beta1.Coin period_can_spend = 4 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + + // period_reset is the time at which this period resets and a new one begins, + // it is calculated from the start time of the first transaction after the + // last period ended + google.protobuf.Timestamp period_reset = 5 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; +} + +// AllowedMsgAllowance creates allowance only for specified message types. +message AllowedMsgAllowance { + option (gogoproto.goproto_getters) = false; + option (cosmos_proto.implements_interface) = "FeeAllowanceI"; + + // allowance can be any of basic and filtered fee allowance. + google.protobuf.Any allowance = 1 [(cosmos_proto.accepts_interface) = "FeeAllowanceI"]; + + // allowed_messages are the messages for which the grantee has the access. + repeated string allowed_messages = 2; +} + +// Grant is stored in the KVStore to record a grant with full context +message Grant { + // granter is the address of the user granting an allowance of their funds. + string granter = 1; + + // grantee is the address of the user being granted an allowance of another user's funds. + string grantee = 2; + + // allowance can be any of basic and filtered fee allowance. + google.protobuf.Any allowance = 3 [(cosmos_proto.accepts_interface) = "FeeAllowanceI"]; +} diff --git a/ampd/proto/third_party/cosmos/feegrant/v1beta1/genesis.proto b/ampd/proto/third_party/cosmos/feegrant/v1beta1/genesis.proto new file mode 100644 index 000000000..5b1ac4ca5 --- /dev/null +++ b/ampd/proto/third_party/cosmos/feegrant/v1beta1/genesis.proto @@ -0,0 +1,13 @@ +// Since: cosmos-sdk 0.43 +syntax = "proto3"; +package cosmos.feegrant.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/feegrant/v1beta1/feegrant.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/feegrant"; + +// GenesisState contains a set of fee allowances, persisted from the store +message GenesisState { + repeated Grant allowances = 1 [(gogoproto.nullable) = false]; +} diff --git a/ampd/proto/third_party/cosmos/feegrant/v1beta1/query.proto b/ampd/proto/third_party/cosmos/feegrant/v1beta1/query.proto new file mode 100644 index 000000000..42d7a842d --- /dev/null +++ b/ampd/proto/third_party/cosmos/feegrant/v1beta1/query.proto @@ -0,0 +1,78 @@ +// Since: cosmos-sdk 0.43 +syntax = "proto3"; +package cosmos.feegrant.v1beta1; + +import "cosmos/feegrant/v1beta1/feegrant.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; +import "google/api/annotations.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/feegrant"; + +// Query defines the gRPC querier service. +service Query { + + // Allowance returns fee granted to the grantee by the granter. + rpc Allowance(QueryAllowanceRequest) returns (QueryAllowanceResponse) { + option (google.api.http).get = "/cosmos/feegrant/v1beta1/allowance/{granter}/{grantee}"; + } + + // Allowances returns all the grants for address. + rpc Allowances(QueryAllowancesRequest) returns (QueryAllowancesResponse) { + option (google.api.http).get = "/cosmos/feegrant/v1beta1/allowances/{grantee}"; + } + + // AllowancesByGranter returns all the grants given by an address + // Since v0.46 + rpc AllowancesByGranter(QueryAllowancesByGranterRequest) returns (QueryAllowancesByGranterResponse) { + option (google.api.http).get = "/cosmos/feegrant/v1beta1/issued/{granter}"; + } +} + +// QueryAllowanceRequest is the request type for the Query/Allowance RPC method. +message QueryAllowanceRequest { + // granter is the address of the user granting an allowance of their funds. + string granter = 1; + + // grantee is the address of the user being granted an allowance of another user's funds. + string grantee = 2; +} + +// QueryAllowanceResponse is the response type for the Query/Allowance RPC method. +message QueryAllowanceResponse { + // allowance is a allowance granted for grantee by granter. + cosmos.feegrant.v1beta1.Grant allowance = 1; +} + +// QueryAllowancesRequest is the request type for the Query/Allowances RPC method. +message QueryAllowancesRequest { + string grantee = 1; + + // pagination defines an pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryAllowancesResponse is the response type for the Query/Allowances RPC method. +message QueryAllowancesResponse { + // allowances are allowance's granted for grantee by granter. + repeated cosmos.feegrant.v1beta1.Grant allowances = 1; + + // pagination defines an pagination for the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryAllowancesByGranterRequest is the request type for the Query/AllowancesByGranter RPC method. +message QueryAllowancesByGranterRequest { + string granter = 1; + + // pagination defines an pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryAllowancesByGranterResponse is the response type for the Query/AllowancesByGranter RPC method. +message QueryAllowancesByGranterResponse { + // allowances that have been issued by the granter. + repeated cosmos.feegrant.v1beta1.Grant allowances = 1; + + // pagination defines an pagination for the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} diff --git a/ampd/proto/third_party/cosmos/feegrant/v1beta1/tx.proto b/ampd/proto/third_party/cosmos/feegrant/v1beta1/tx.proto new file mode 100644 index 000000000..2d875e922 --- /dev/null +++ b/ampd/proto/third_party/cosmos/feegrant/v1beta1/tx.proto @@ -0,0 +1,49 @@ +// Since: cosmos-sdk 0.43 +syntax = "proto3"; +package cosmos.feegrant.v1beta1; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "cosmos_proto/cosmos.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/feegrant"; + +// Msg defines the feegrant msg service. +service Msg { + + // GrantAllowance grants fee allowance to the grantee on the granter's + // account with the provided expiration time. + rpc GrantAllowance(MsgGrantAllowance) returns (MsgGrantAllowanceResponse); + + // RevokeAllowance revokes any fee allowance of granter's account that + // has been granted to the grantee. + rpc RevokeAllowance(MsgRevokeAllowance) returns (MsgRevokeAllowanceResponse); +} + +// MsgGrantAllowance adds permission for Grantee to spend up to Allowance +// of fees from the account of Granter. +message MsgGrantAllowance { + // granter is the address of the user granting an allowance of their funds. + string granter = 1; + + // grantee is the address of the user being granted an allowance of another user's funds. + string grantee = 2; + + // allowance can be any of basic and filtered fee allowance. + google.protobuf.Any allowance = 3 [(cosmos_proto.accepts_interface) = "FeeAllowanceI"]; +} + +// MsgGrantAllowanceResponse defines the Msg/GrantAllowanceResponse response type. +message MsgGrantAllowanceResponse {} + +// MsgRevokeAllowance removes any existing Allowance from Granter to Grantee. +message MsgRevokeAllowance { + // granter is the address of the user granting an allowance of their funds. + string granter = 1; + + // grantee is the address of the user being granted an allowance of another user's funds. + string grantee = 2; +} + +// MsgRevokeAllowanceResponse defines the Msg/RevokeAllowanceResponse response type. +message MsgRevokeAllowanceResponse {} diff --git a/ampd/proto/third_party/cosmos/genutil/v1beta1/genesis.proto b/ampd/proto/third_party/cosmos/genutil/v1beta1/genesis.proto new file mode 100644 index 000000000..a0207793d --- /dev/null +++ b/ampd/proto/third_party/cosmos/genutil/v1beta1/genesis.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; +package cosmos.genutil.v1beta1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/genutil/types"; + +// GenesisState defines the raw genesis transaction in JSON. +message GenesisState { + // gen_txs defines the genesis transactions. + repeated bytes gen_txs = 1 [ + (gogoproto.casttype) = "encoding/json.RawMessage", + (gogoproto.jsontag) = "gentxs", + (gogoproto.moretags) = "yaml:\"gentxs\"" + ]; +} diff --git a/ampd/proto/third_party/cosmos/gov/v1beta1/genesis.proto b/ampd/proto/third_party/cosmos/gov/v1beta1/genesis.proto new file mode 100644 index 000000000..a99950044 --- /dev/null +++ b/ampd/proto/third_party/cosmos/gov/v1beta1/genesis.proto @@ -0,0 +1,26 @@ +syntax = "proto3"; + +package cosmos.gov.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/gov/v1beta1/gov.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/gov/types"; + +// GenesisState defines the gov module's genesis state. +message GenesisState { + // starting_proposal_id is the ID of the starting proposal. + uint64 starting_proposal_id = 1 [(gogoproto.moretags) = "yaml:\"starting_proposal_id\""]; + // deposits defines all the deposits present at genesis. + repeated Deposit deposits = 2 [(gogoproto.castrepeated) = "Deposits", (gogoproto.nullable) = false]; + // votes defines all the votes present at genesis. + repeated Vote votes = 3 [(gogoproto.castrepeated) = "Votes", (gogoproto.nullable) = false]; + // proposals defines all the proposals present at genesis. + repeated Proposal proposals = 4 [(gogoproto.castrepeated) = "Proposals", (gogoproto.nullable) = false]; + // params defines all the paramaters of related to deposit. + DepositParams deposit_params = 5 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"deposit_params\""]; + // params defines all the paramaters of related to voting. + VotingParams voting_params = 6 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"voting_params\""]; + // params defines all the paramaters of related to tally. + TallyParams tally_params = 7 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"tally_params\""]; +} diff --git a/ampd/proto/third_party/cosmos/gov/v1beta1/gov.proto b/ampd/proto/third_party/cosmos/gov/v1beta1/gov.proto new file mode 100644 index 000000000..01aebf950 --- /dev/null +++ b/ampd/proto/third_party/cosmos/gov/v1beta1/gov.proto @@ -0,0 +1,200 @@ +syntax = "proto3"; +package cosmos.gov.v1beta1; + +import "cosmos/base/v1beta1/coin.proto"; +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/any.proto"; +import "google/protobuf/duration.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/gov/types"; +option (gogoproto.goproto_stringer_all) = false; +option (gogoproto.stringer_all) = false; +option (gogoproto.goproto_getters_all) = false; + +// VoteOption enumerates the valid vote options for a given governance proposal. +enum VoteOption { + option (gogoproto.goproto_enum_prefix) = false; + + // VOTE_OPTION_UNSPECIFIED defines a no-op vote option. + VOTE_OPTION_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "OptionEmpty"]; + // VOTE_OPTION_YES defines a yes vote option. + VOTE_OPTION_YES = 1 [(gogoproto.enumvalue_customname) = "OptionYes"]; + // VOTE_OPTION_ABSTAIN defines an abstain vote option. + VOTE_OPTION_ABSTAIN = 2 [(gogoproto.enumvalue_customname) = "OptionAbstain"]; + // VOTE_OPTION_NO defines a no vote option. + VOTE_OPTION_NO = 3 [(gogoproto.enumvalue_customname) = "OptionNo"]; + // VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. + VOTE_OPTION_NO_WITH_VETO = 4 [(gogoproto.enumvalue_customname) = "OptionNoWithVeto"]; +} + +// WeightedVoteOption defines a unit of vote for vote split. +// +// Since: cosmos-sdk 0.43 +message WeightedVoteOption { + VoteOption option = 1; + string weight = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"weight\"" + ]; +} + +// TextProposal defines a standard text proposal whose changes need to be +// manually updated in case of approval. +message TextProposal { + option (cosmos_proto.implements_interface) = "Content"; + + option (gogoproto.equal) = true; + + string title = 1; + string description = 2; +} + +// Deposit defines an amount deposited by an account address to an active +// proposal. +message Deposit { + option (gogoproto.goproto_getters) = false; + option (gogoproto.equal) = false; + + uint64 proposal_id = 1 [(gogoproto.moretags) = "yaml:\"proposal_id\""]; + string depositor = 2; + repeated cosmos.base.v1beta1.Coin amount = 3 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; +} + +// Proposal defines the core field members of a governance proposal. +message Proposal { + option (gogoproto.equal) = true; + + uint64 proposal_id = 1 [(gogoproto.jsontag) = "id", (gogoproto.moretags) = "yaml:\"id\""]; + google.protobuf.Any content = 2 [(cosmos_proto.accepts_interface) = "Content"]; + ProposalStatus status = 3 [(gogoproto.moretags) = "yaml:\"proposal_status\""]; + TallyResult final_tally_result = 4 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"final_tally_result\""]; + google.protobuf.Timestamp submit_time = 5 + [(gogoproto.stdtime) = true, (gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"submit_time\""]; + google.protobuf.Timestamp deposit_end_time = 6 + [(gogoproto.stdtime) = true, (gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"deposit_end_time\""]; + repeated cosmos.base.v1beta1.Coin total_deposit = 7 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", + (gogoproto.moretags) = "yaml:\"total_deposit\"" + ]; + google.protobuf.Timestamp voting_start_time = 8 + [(gogoproto.stdtime) = true, (gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"voting_start_time\""]; + google.protobuf.Timestamp voting_end_time = 9 + [(gogoproto.stdtime) = true, (gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"voting_end_time\""]; +} + +// ProposalStatus enumerates the valid statuses of a proposal. +enum ProposalStatus { + option (gogoproto.goproto_enum_prefix) = false; + + // PROPOSAL_STATUS_UNSPECIFIED defines the default propopsal status. + PROPOSAL_STATUS_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "StatusNil"]; + // PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit + // period. + PROPOSAL_STATUS_DEPOSIT_PERIOD = 1 [(gogoproto.enumvalue_customname) = "StatusDepositPeriod"]; + // PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting + // period. + PROPOSAL_STATUS_VOTING_PERIOD = 2 [(gogoproto.enumvalue_customname) = "StatusVotingPeriod"]; + // PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has + // passed. + PROPOSAL_STATUS_PASSED = 3 [(gogoproto.enumvalue_customname) = "StatusPassed"]; + // PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has + // been rejected. + PROPOSAL_STATUS_REJECTED = 4 [(gogoproto.enumvalue_customname) = "StatusRejected"]; + // PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has + // failed. + PROPOSAL_STATUS_FAILED = 5 [(gogoproto.enumvalue_customname) = "StatusFailed"]; +} + +// TallyResult defines a standard tally for a governance proposal. +message TallyResult { + option (gogoproto.equal) = true; + + string yes = 1 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", (gogoproto.nullable) = false]; + string abstain = 2 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", (gogoproto.nullable) = false]; + string no = 3 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", (gogoproto.nullable) = false]; + string no_with_veto = 4 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"no_with_veto\"" + ]; +} + +// Vote defines a vote on a governance proposal. +// A Vote consists of a proposal ID, the voter, and the vote option. +message Vote { + option (gogoproto.goproto_stringer) = false; + option (gogoproto.equal) = false; + + uint64 proposal_id = 1 [(gogoproto.moretags) = "yaml:\"proposal_id\""]; + string voter = 2; + // Deprecated: Prefer to use `options` instead. This field is set in queries + // if and only if `len(options) == 1` and that option has weight 1. In all + // other cases, this field will default to VOTE_OPTION_UNSPECIFIED. + VoteOption option = 3 [deprecated = true]; + // Since: cosmos-sdk 0.43 + repeated WeightedVoteOption options = 4 [(gogoproto.nullable) = false]; +} + +// DepositParams defines the params for deposits on governance proposals. +message DepositParams { + // Minimum deposit for a proposal to enter voting period. + repeated cosmos.base.v1beta1.Coin min_deposit = 1 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", + (gogoproto.moretags) = "yaml:\"min_deposit\"", + (gogoproto.jsontag) = "min_deposit,omitempty" + ]; + + // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 + // months. + google.protobuf.Duration max_deposit_period = 2 [ + (gogoproto.nullable) = false, + (gogoproto.stdduration) = true, + (gogoproto.jsontag) = "max_deposit_period,omitempty", + (gogoproto.moretags) = "yaml:\"max_deposit_period\"" + ]; +} + +// VotingParams defines the params for voting on governance proposals. +message VotingParams { + // Length of the voting period. + google.protobuf.Duration voting_period = 1 [ + (gogoproto.nullable) = false, + (gogoproto.stdduration) = true, + (gogoproto.jsontag) = "voting_period,omitempty", + (gogoproto.moretags) = "yaml:\"voting_period\"" + ]; +} + +// TallyParams defines the params for tallying votes on governance proposals. +message TallyParams { + // Minimum percentage of total stake needed to vote for a result to be + // considered valid. + bytes quorum = 1 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "quorum,omitempty" + ]; + + // Minimum proportion of Yes votes for proposal to pass. Default value: 0.5. + bytes threshold = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "threshold,omitempty" + ]; + + // Minimum value of Veto votes to Total votes ratio for proposal to be + // vetoed. Default value: 1/3. + bytes veto_threshold = 3 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "veto_threshold,omitempty", + (gogoproto.moretags) = "yaml:\"veto_threshold\"" + ]; +} diff --git a/ampd/proto/third_party/cosmos/gov/v1beta1/query.proto b/ampd/proto/third_party/cosmos/gov/v1beta1/query.proto new file mode 100644 index 000000000..da62bdbad --- /dev/null +++ b/ampd/proto/third_party/cosmos/gov/v1beta1/query.proto @@ -0,0 +1,190 @@ +syntax = "proto3"; +package cosmos.gov.v1beta1; + +import "cosmos/base/query/v1beta1/pagination.proto"; +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cosmos/gov/v1beta1/gov.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/gov/types"; + +// Query defines the gRPC querier service for gov module +service Query { + // Proposal queries proposal details based on ProposalID. + rpc Proposal(QueryProposalRequest) returns (QueryProposalResponse) { + option (google.api.http).get = "/cosmos/gov/v1beta1/proposals/{proposal_id}"; + } + + // Proposals queries all proposals based on given status. + rpc Proposals(QueryProposalsRequest) returns (QueryProposalsResponse) { + option (google.api.http).get = "/cosmos/gov/v1beta1/proposals"; + } + + // Vote queries voted information based on proposalID, voterAddr. + rpc Vote(QueryVoteRequest) returns (QueryVoteResponse) { + option (google.api.http).get = "/cosmos/gov/v1beta1/proposals/{proposal_id}/votes/{voter}"; + } + + // Votes queries votes of a given proposal. + rpc Votes(QueryVotesRequest) returns (QueryVotesResponse) { + option (google.api.http).get = "/cosmos/gov/v1beta1/proposals/{proposal_id}/votes"; + } + + // Params queries all parameters of the gov module. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/cosmos/gov/v1beta1/params/{params_type}"; + } + + // Deposit queries single deposit information based proposalID, depositAddr. + rpc Deposit(QueryDepositRequest) returns (QueryDepositResponse) { + option (google.api.http).get = "/cosmos/gov/v1beta1/proposals/{proposal_id}/deposits/{depositor}"; + } + + // Deposits queries all deposits of a single proposal. + rpc Deposits(QueryDepositsRequest) returns (QueryDepositsResponse) { + option (google.api.http).get = "/cosmos/gov/v1beta1/proposals/{proposal_id}/deposits"; + } + + // TallyResult queries the tally of a proposal vote. + rpc TallyResult(QueryTallyResultRequest) returns (QueryTallyResultResponse) { + option (google.api.http).get = "/cosmos/gov/v1beta1/proposals/{proposal_id}/tally"; + } +} + +// QueryProposalRequest is the request type for the Query/Proposal RPC method. +message QueryProposalRequest { + // proposal_id defines the unique id of the proposal. + uint64 proposal_id = 1; +} + +// QueryProposalResponse is the response type for the Query/Proposal RPC method. +message QueryProposalResponse { + Proposal proposal = 1 [(gogoproto.nullable) = false]; +} + +// QueryProposalsRequest is the request type for the Query/Proposals RPC method. +message QueryProposalsRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // proposal_status defines the status of the proposals. + ProposalStatus proposal_status = 1; + + // voter defines the voter address for the proposals. + string voter = 2; + + // depositor defines the deposit addresses from the proposals. + string depositor = 3; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 4; +} + +// QueryProposalsResponse is the response type for the Query/Proposals RPC +// method. +message QueryProposalsResponse { + repeated Proposal proposals = 1 [(gogoproto.nullable) = false]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryVoteRequest is the request type for the Query/Vote RPC method. +message QueryVoteRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // proposal_id defines the unique id of the proposal. + uint64 proposal_id = 1; + + // voter defines the oter address for the proposals. + string voter = 2; +} + +// QueryVoteResponse is the response type for the Query/Vote RPC method. +message QueryVoteResponse { + // vote defined the queried vote. + Vote vote = 1 [(gogoproto.nullable) = false]; +} + +// QueryVotesRequest is the request type for the Query/Votes RPC method. +message QueryVotesRequest { + // proposal_id defines the unique id of the proposal. + uint64 proposal_id = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryVotesResponse is the response type for the Query/Votes RPC method. +message QueryVotesResponse { + // votes defined the queried votes. + repeated Vote votes = 1 [(gogoproto.nullable) = false]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryParamsRequest is the request type for the Query/Params RPC method. +message QueryParamsRequest { + // params_type defines which parameters to query for, can be one of "voting", + // "tallying" or "deposit". + string params_type = 1; +} + +// QueryParamsResponse is the response type for the Query/Params RPC method. +message QueryParamsResponse { + // voting_params defines the parameters related to voting. + VotingParams voting_params = 1 [(gogoproto.nullable) = false]; + // deposit_params defines the parameters related to deposit. + DepositParams deposit_params = 2 [(gogoproto.nullable) = false]; + // tally_params defines the parameters related to tally. + TallyParams tally_params = 3 [(gogoproto.nullable) = false]; +} + +// QueryDepositRequest is the request type for the Query/Deposit RPC method. +message QueryDepositRequest { + option (gogoproto.goproto_getters) = false; + option (gogoproto.equal) = false; + + // proposal_id defines the unique id of the proposal. + uint64 proposal_id = 1; + + // depositor defines the deposit addresses from the proposals. + string depositor = 2; +} + +// QueryDepositResponse is the response type for the Query/Deposit RPC method. +message QueryDepositResponse { + // deposit defines the requested deposit. + Deposit deposit = 1 [(gogoproto.nullable) = false]; +} + +// QueryDepositsRequest is the request type for the Query/Deposits RPC method. +message QueryDepositsRequest { + // proposal_id defines the unique id of the proposal. + uint64 proposal_id = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryDepositsResponse is the response type for the Query/Deposits RPC method. +message QueryDepositsResponse { + repeated Deposit deposits = 1 [(gogoproto.nullable) = false]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryTallyResultRequest is the request type for the Query/Tally RPC method. +message QueryTallyResultRequest { + // proposal_id defines the unique id of the proposal. + uint64 proposal_id = 1; +} + +// QueryTallyResultResponse is the response type for the Query/Tally RPC method. +message QueryTallyResultResponse { + // tally defines the requested tally. + TallyResult tally = 1 [(gogoproto.nullable) = false]; +} diff --git a/ampd/proto/third_party/cosmos/gov/v1beta1/tx.proto b/ampd/proto/third_party/cosmos/gov/v1beta1/tx.proto new file mode 100644 index 000000000..36c0a95d2 --- /dev/null +++ b/ampd/proto/third_party/cosmos/gov/v1beta1/tx.proto @@ -0,0 +1,99 @@ +syntax = "proto3"; +package cosmos.gov.v1beta1; + +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/gov/v1beta1/gov.proto"; +import "cosmos_proto/cosmos.proto"; +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/gov/types"; + +// Msg defines the bank Msg service. +service Msg { + // SubmitProposal defines a method to create new proposal given a content. + rpc SubmitProposal(MsgSubmitProposal) returns (MsgSubmitProposalResponse); + + // Vote defines a method to add a vote on a specific proposal. + rpc Vote(MsgVote) returns (MsgVoteResponse); + + // VoteWeighted defines a method to add a weighted vote on a specific proposal. + // + // Since: cosmos-sdk 0.43 + rpc VoteWeighted(MsgVoteWeighted) returns (MsgVoteWeightedResponse); + + // Deposit defines a method to add deposit on a specific proposal. + rpc Deposit(MsgDeposit) returns (MsgDepositResponse); +} + +// MsgSubmitProposal defines an sdk.Msg type that supports submitting arbitrary +// proposal Content. +message MsgSubmitProposal { + option (gogoproto.equal) = false; + option (gogoproto.goproto_stringer) = false; + option (gogoproto.stringer) = false; + option (gogoproto.goproto_getters) = false; + + google.protobuf.Any content = 1 [(cosmos_proto.accepts_interface) = "Content"]; + repeated cosmos.base.v1beta1.Coin initial_deposit = 2 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", + (gogoproto.moretags) = "yaml:\"initial_deposit\"" + ]; + string proposer = 3; +} + +// MsgSubmitProposalResponse defines the Msg/SubmitProposal response type. +message MsgSubmitProposalResponse { + uint64 proposal_id = 1 [(gogoproto.jsontag) = "proposal_id", (gogoproto.moretags) = "yaml:\"proposal_id\""]; +} + +// MsgVote defines a message to cast a vote. +message MsgVote { + option (gogoproto.equal) = false; + option (gogoproto.goproto_stringer) = false; + option (gogoproto.stringer) = false; + option (gogoproto.goproto_getters) = false; + + uint64 proposal_id = 1 [(gogoproto.jsontag) = "proposal_id", (gogoproto.moretags) = "yaml:\"proposal_id\""]; + string voter = 2; + VoteOption option = 3; +} + +// MsgVoteResponse defines the Msg/Vote response type. +message MsgVoteResponse {} + +// MsgVoteWeighted defines a message to cast a vote. +// +// Since: cosmos-sdk 0.43 +message MsgVoteWeighted { + option (gogoproto.equal) = false; + option (gogoproto.goproto_stringer) = false; + option (gogoproto.stringer) = false; + option (gogoproto.goproto_getters) = false; + + uint64 proposal_id = 1 [(gogoproto.moretags) = "yaml:\"proposal_id\""]; + string voter = 2; + repeated WeightedVoteOption options = 3 [(gogoproto.nullable) = false]; +} + +// MsgVoteWeightedResponse defines the Msg/VoteWeighted response type. +// +// Since: cosmos-sdk 0.43 +message MsgVoteWeightedResponse {} + +// MsgDeposit defines a message to submit a deposit to an existing proposal. +message MsgDeposit { + option (gogoproto.equal) = false; + option (gogoproto.goproto_stringer) = false; + option (gogoproto.stringer) = false; + option (gogoproto.goproto_getters) = false; + + uint64 proposal_id = 1 [(gogoproto.jsontag) = "proposal_id", (gogoproto.moretags) = "yaml:\"proposal_id\""]; + string depositor = 2; + repeated cosmos.base.v1beta1.Coin amount = 3 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; +} + +// MsgDepositResponse defines the Msg/Deposit response type. +message MsgDepositResponse {} diff --git a/ampd/proto/third_party/cosmos/mint/v1beta1/genesis.proto b/ampd/proto/third_party/cosmos/mint/v1beta1/genesis.proto new file mode 100644 index 000000000..4e783fb54 --- /dev/null +++ b/ampd/proto/third_party/cosmos/mint/v1beta1/genesis.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; +package cosmos.mint.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/mint/v1beta1/mint.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/mint/types"; + +// GenesisState defines the mint module's genesis state. +message GenesisState { + // minter is a space for holding current inflation information. + Minter minter = 1 [(gogoproto.nullable) = false]; + + // params defines all the paramaters of the module. + Params params = 2 [(gogoproto.nullable) = false]; +} diff --git a/ampd/proto/third_party/cosmos/mint/v1beta1/mint.proto b/ampd/proto/third_party/cosmos/mint/v1beta1/mint.proto new file mode 100644 index 000000000..f94d4ae2e --- /dev/null +++ b/ampd/proto/third_party/cosmos/mint/v1beta1/mint.proto @@ -0,0 +1,53 @@ +syntax = "proto3"; +package cosmos.mint.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/mint/types"; + +import "gogoproto/gogo.proto"; + +// Minter represents the minting state. +message Minter { + // current annual inflation rate + string inflation = 1 + [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false]; + // current annual expected provisions + string annual_provisions = 2 [ + (gogoproto.moretags) = "yaml:\"annual_provisions\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; +} + +// Params holds parameters for the mint module. +message Params { + option (gogoproto.goproto_stringer) = false; + + // type of coin to mint + string mint_denom = 1; + // maximum annual change in inflation rate + string inflation_rate_change = 2 [ + (gogoproto.moretags) = "yaml:\"inflation_rate_change\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + // maximum inflation rate + string inflation_max = 3 [ + (gogoproto.moretags) = "yaml:\"inflation_max\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + // minimum inflation rate + string inflation_min = 4 [ + (gogoproto.moretags) = "yaml:\"inflation_min\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + // goal of percent bonded atoms + string goal_bonded = 5 [ + (gogoproto.moretags) = "yaml:\"goal_bonded\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + // expected blocks per year + uint64 blocks_per_year = 6 [(gogoproto.moretags) = "yaml:\"blocks_per_year\""]; +} diff --git a/ampd/proto/third_party/cosmos/mint/v1beta1/query.proto b/ampd/proto/third_party/cosmos/mint/v1beta1/query.proto new file mode 100644 index 000000000..acd341d77 --- /dev/null +++ b/ampd/proto/third_party/cosmos/mint/v1beta1/query.proto @@ -0,0 +1,57 @@ +syntax = "proto3"; +package cosmos.mint.v1beta1; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cosmos/mint/v1beta1/mint.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/mint/types"; + +// Query provides defines the gRPC querier service. +service Query { + // Params returns the total set of minting parameters. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/cosmos/mint/v1beta1/params"; + } + + // Inflation returns the current minting inflation value. + rpc Inflation(QueryInflationRequest) returns (QueryInflationResponse) { + option (google.api.http).get = "/cosmos/mint/v1beta1/inflation"; + } + + // AnnualProvisions current minting annual provisions value. + rpc AnnualProvisions(QueryAnnualProvisionsRequest) returns (QueryAnnualProvisionsResponse) { + option (google.api.http).get = "/cosmos/mint/v1beta1/annual_provisions"; + } +} + +// QueryParamsRequest is the request type for the Query/Params RPC method. +message QueryParamsRequest {} + +// QueryParamsResponse is the response type for the Query/Params RPC method. +message QueryParamsResponse { + // params defines the parameters of the module. + Params params = 1 [(gogoproto.nullable) = false]; +} + +// QueryInflationRequest is the request type for the Query/Inflation RPC method. +message QueryInflationRequest {} + +// QueryInflationResponse is the response type for the Query/Inflation RPC +// method. +message QueryInflationResponse { + // inflation is the current minting inflation value. + bytes inflation = 1 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false]; +} + +// QueryAnnualProvisionsRequest is the request type for the +// Query/AnnualProvisions RPC method. +message QueryAnnualProvisionsRequest {} + +// QueryAnnualProvisionsResponse is the response type for the +// Query/AnnualProvisions RPC method. +message QueryAnnualProvisionsResponse { + // annual_provisions is the current minting annual provisions value. + bytes annual_provisions = 1 + [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false]; +} diff --git a/ampd/proto/third_party/cosmos/params/v1beta1/params.proto b/ampd/proto/third_party/cosmos/params/v1beta1/params.proto new file mode 100644 index 000000000..5382fd799 --- /dev/null +++ b/ampd/proto/third_party/cosmos/params/v1beta1/params.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; +package cosmos.params.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/params/types/proposal"; +option (gogoproto.equal_all) = true; + +import "gogoproto/gogo.proto"; + +// ParameterChangeProposal defines a proposal to change one or more parameters. +message ParameterChangeProposal { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + string title = 1; + string description = 2; + repeated ParamChange changes = 3 [(gogoproto.nullable) = false]; +} + +// ParamChange defines an individual parameter change, for use in +// ParameterChangeProposal. +message ParamChange { + option (gogoproto.goproto_stringer) = false; + + string subspace = 1; + string key = 2; + string value = 3; +} diff --git a/ampd/proto/third_party/cosmos/params/v1beta1/query.proto b/ampd/proto/third_party/cosmos/params/v1beta1/query.proto new file mode 100644 index 000000000..1078e02ae --- /dev/null +++ b/ampd/proto/third_party/cosmos/params/v1beta1/query.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; +package cosmos.params.v1beta1; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cosmos/params/v1beta1/params.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/params/types/proposal"; + +// Query defines the gRPC querier service. +service Query { + // Params queries a specific parameter of a module, given its subspace and + // key. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/cosmos/params/v1beta1/params"; + } +} + +// QueryParamsRequest is request type for the Query/Params RPC method. +message QueryParamsRequest { + // subspace defines the module to query the parameter for. + string subspace = 1; + + // key defines the key of the parameter in the subspace. + string key = 2; +} + +// QueryParamsResponse is response type for the Query/Params RPC method. +message QueryParamsResponse { + // param defines the queried parameter. + ParamChange param = 1 [(gogoproto.nullable) = false]; +} diff --git a/ampd/proto/third_party/cosmos/slashing/v1beta1/genesis.proto b/ampd/proto/third_party/cosmos/slashing/v1beta1/genesis.proto new file mode 100644 index 000000000..a7aebcfba --- /dev/null +++ b/ampd/proto/third_party/cosmos/slashing/v1beta1/genesis.proto @@ -0,0 +1,50 @@ +syntax = "proto3"; +package cosmos.slashing.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/slashing/types"; + +import "gogoproto/gogo.proto"; +import "cosmos/slashing/v1beta1/slashing.proto"; + +// GenesisState defines the slashing module's genesis state. +message GenesisState { + // params defines all the paramaters of related to deposit. + Params params = 1 [(gogoproto.nullable) = false]; + + // signing_infos represents a map between validator addresses and their + // signing infos. + repeated SigningInfo signing_infos = 2 + [(gogoproto.moretags) = "yaml:\"signing_infos\"", (gogoproto.nullable) = false]; + + // missed_blocks represents a map between validator addresses and their + // missed blocks. + repeated ValidatorMissedBlocks missed_blocks = 3 + [(gogoproto.moretags) = "yaml:\"missed_blocks\"", (gogoproto.nullable) = false]; +} + +// SigningInfo stores validator signing info of corresponding address. +message SigningInfo { + // address is the validator address. + string address = 1; + // validator_signing_info represents the signing info of this validator. + ValidatorSigningInfo validator_signing_info = 2 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"validator_signing_info\""]; +} + +// ValidatorMissedBlocks contains array of missed blocks of corresponding +// address. +message ValidatorMissedBlocks { + // address is the validator address. + string address = 1; + // missed_blocks is an array of missed blocks by the validator. + repeated MissedBlock missed_blocks = 2 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"missed_blocks\""]; +} + +// MissedBlock contains height and missed status as boolean. +message MissedBlock { + // index is the height at which the block was missed. + int64 index = 1; + // missed is the missed status. + bool missed = 2; +} diff --git a/ampd/proto/third_party/cosmos/slashing/v1beta1/query.proto b/ampd/proto/third_party/cosmos/slashing/v1beta1/query.proto new file mode 100644 index 000000000..869049a0e --- /dev/null +++ b/ampd/proto/third_party/cosmos/slashing/v1beta1/query.proto @@ -0,0 +1,63 @@ +syntax = "proto3"; +package cosmos.slashing.v1beta1; + +import "cosmos/base/query/v1beta1/pagination.proto"; +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cosmos/slashing/v1beta1/slashing.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/slashing/types"; + +// Query provides defines the gRPC querier service +service Query { + // Params queries the parameters of slashing module + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/cosmos/slashing/v1beta1/params"; + } + + // SigningInfo queries the signing info of given cons address + rpc SigningInfo(QuerySigningInfoRequest) returns (QuerySigningInfoResponse) { + option (google.api.http).get = "/cosmos/slashing/v1beta1/signing_infos/{cons_address}"; + } + + // SigningInfos queries signing info of all validators + rpc SigningInfos(QuerySigningInfosRequest) returns (QuerySigningInfosResponse) { + option (google.api.http).get = "/cosmos/slashing/v1beta1/signing_infos"; + } +} + +// QueryParamsRequest is the request type for the Query/Params RPC method +message QueryParamsRequest {} + +// QueryParamsResponse is the response type for the Query/Params RPC method +message QueryParamsResponse { + Params params = 1 [(gogoproto.nullable) = false]; +} + +// QuerySigningInfoRequest is the request type for the Query/SigningInfo RPC +// method +message QuerySigningInfoRequest { + // cons_address is the address to query signing info of + string cons_address = 1; +} + +// QuerySigningInfoResponse is the response type for the Query/SigningInfo RPC +// method +message QuerySigningInfoResponse { + // val_signing_info is the signing info of requested val cons address + ValidatorSigningInfo val_signing_info = 1 [(gogoproto.nullable) = false]; +} + +// QuerySigningInfosRequest is the request type for the Query/SigningInfos RPC +// method +message QuerySigningInfosRequest { + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +// QuerySigningInfosResponse is the response type for the Query/SigningInfos RPC +// method +message QuerySigningInfosResponse { + // info is the signing info of all validators + repeated cosmos.slashing.v1beta1.ValidatorSigningInfo info = 1 [(gogoproto.nullable) = false]; + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} diff --git a/ampd/proto/third_party/cosmos/slashing/v1beta1/slashing.proto b/ampd/proto/third_party/cosmos/slashing/v1beta1/slashing.proto new file mode 100644 index 000000000..882a0fb60 --- /dev/null +++ b/ampd/proto/third_party/cosmos/slashing/v1beta1/slashing.proto @@ -0,0 +1,58 @@ +syntax = "proto3"; +package cosmos.slashing.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/slashing/types"; +option (gogoproto.equal_all) = true; + +import "gogoproto/gogo.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/timestamp.proto"; + +// ValidatorSigningInfo defines a validator's signing info for monitoring their +// liveness activity. +message ValidatorSigningInfo { + option (gogoproto.equal) = true; + option (gogoproto.goproto_stringer) = false; + + string address = 1; + // Height at which validator was first a candidate OR was unjailed + int64 start_height = 2 [(gogoproto.moretags) = "yaml:\"start_height\""]; + // Index which is incremented each time the validator was a bonded + // in a block and may have signed a precommit or not. This in conjunction with the + // `SignedBlocksWindow` param determines the index in the `MissedBlocksBitArray`. + int64 index_offset = 3 [(gogoproto.moretags) = "yaml:\"index_offset\""]; + // Timestamp until which the validator is jailed due to liveness downtime. + google.protobuf.Timestamp jailed_until = 4 + [(gogoproto.moretags) = "yaml:\"jailed_until\"", (gogoproto.stdtime) = true, (gogoproto.nullable) = false]; + // Whether or not a validator has been tombstoned (killed out of validator set). It is set + // once the validator commits an equivocation or for any other configured misbehiavor. + bool tombstoned = 5; + // A counter kept to avoid unnecessary array reads. + // Note that `Sum(MissedBlocksBitArray)` always equals `MissedBlocksCounter`. + int64 missed_blocks_counter = 6 [(gogoproto.moretags) = "yaml:\"missed_blocks_counter\""]; +} + +// Params represents the parameters used for by the slashing module. +message Params { + int64 signed_blocks_window = 1 [(gogoproto.moretags) = "yaml:\"signed_blocks_window\""]; + bytes min_signed_per_window = 2 [ + (gogoproto.moretags) = "yaml:\"min_signed_per_window\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + google.protobuf.Duration downtime_jail_duration = 3 [ + (gogoproto.nullable) = false, + (gogoproto.stdduration) = true, + (gogoproto.moretags) = "yaml:\"downtime_jail_duration\"" + ]; + bytes slash_fraction_double_sign = 4 [ + (gogoproto.moretags) = "yaml:\"slash_fraction_double_sign\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + bytes slash_fraction_downtime = 5 [ + (gogoproto.moretags) = "yaml:\"slash_fraction_downtime\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; +} diff --git a/ampd/proto/third_party/cosmos/slashing/v1beta1/tx.proto b/ampd/proto/third_party/cosmos/slashing/v1beta1/tx.proto new file mode 100644 index 000000000..4d63370ec --- /dev/null +++ b/ampd/proto/third_party/cosmos/slashing/v1beta1/tx.proto @@ -0,0 +1,26 @@ +syntax = "proto3"; +package cosmos.slashing.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/slashing/types"; +option (gogoproto.equal_all) = true; + +import "gogoproto/gogo.proto"; + +// Msg defines the slashing Msg service. +service Msg { + // Unjail defines a method for unjailing a jailed validator, thus returning + // them into the bonded validator set, so they can begin receiving provisions + // and rewards again. + rpc Unjail(MsgUnjail) returns (MsgUnjailResponse); +} + +// MsgUnjail defines the Msg/Unjail request type +message MsgUnjail { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = true; + + string validator_addr = 1 [(gogoproto.moretags) = "yaml:\"address\"", (gogoproto.jsontag) = "address"]; +} + +// MsgUnjailResponse defines the Msg/Unjail response type +message MsgUnjailResponse {} \ No newline at end of file diff --git a/ampd/proto/third_party/cosmos/staking/v1beta1/authz.proto b/ampd/proto/third_party/cosmos/staking/v1beta1/authz.proto new file mode 100644 index 000000000..d50c329c9 --- /dev/null +++ b/ampd/proto/third_party/cosmos/staking/v1beta1/authz.proto @@ -0,0 +1,47 @@ +syntax = "proto3"; +package cosmos.staking.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/staking/types"; + +// StakeAuthorization defines authorization for delegate/undelegate/redelegate. +// +// Since: cosmos-sdk 0.43 +message StakeAuthorization { + option (cosmos_proto.implements_interface) = "Authorization"; + + // max_tokens specifies the maximum amount of tokens can be delegate to a validator. If it is + // empty, there is no spend limit and any amount of coins can be delegated. + cosmos.base.v1beta1.Coin max_tokens = 1 [(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coin"]; + // validators is the oneof that represents either allow_list or deny_list + oneof validators { + // allow_list specifies list of validator addresses to whom grantee can delegate tokens on behalf of granter's + // account. + Validators allow_list = 2; + // deny_list specifies list of validator addresses to whom grantee can not delegate tokens. + Validators deny_list = 3; + } + // Validators defines list of validator addresses. + message Validators { + repeated string address = 1; + } + // authorization_type defines one of AuthorizationType. + AuthorizationType authorization_type = 4; +} + +// AuthorizationType defines the type of staking module authorization type +// +// Since: cosmos-sdk 0.43 +enum AuthorizationType { + // AUTHORIZATION_TYPE_UNSPECIFIED specifies an unknown authorization type + AUTHORIZATION_TYPE_UNSPECIFIED = 0; + // AUTHORIZATION_TYPE_DELEGATE defines an authorization type for Msg/Delegate + AUTHORIZATION_TYPE_DELEGATE = 1; + // AUTHORIZATION_TYPE_UNDELEGATE defines an authorization type for Msg/Undelegate + AUTHORIZATION_TYPE_UNDELEGATE = 2; + // AUTHORIZATION_TYPE_REDELEGATE defines an authorization type for Msg/BeginRedelegate + AUTHORIZATION_TYPE_REDELEGATE = 3; +} diff --git a/ampd/proto/third_party/cosmos/staking/v1beta1/genesis.proto b/ampd/proto/third_party/cosmos/staking/v1beta1/genesis.proto new file mode 100644 index 000000000..d1563dbc5 --- /dev/null +++ b/ampd/proto/third_party/cosmos/staking/v1beta1/genesis.proto @@ -0,0 +1,53 @@ +syntax = "proto3"; +package cosmos.staking.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/staking/types"; + +import "gogoproto/gogo.proto"; +import "cosmos/staking/v1beta1/staking.proto"; + +// GenesisState defines the staking module's genesis state. +message GenesisState { + // params defines all the paramaters of related to deposit. + Params params = 1 [(gogoproto.nullable) = false]; + + // last_total_power tracks the total amounts of bonded tokens recorded during + // the previous end block. + bytes last_total_power = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.moretags) = "yaml:\"last_total_power\"", + (gogoproto.nullable) = false + ]; + + // last_validator_powers is a special index that provides a historical list + // of the last-block's bonded validators. + repeated LastValidatorPower last_validator_powers = 3 + [(gogoproto.moretags) = "yaml:\"last_validator_powers\"", (gogoproto.nullable) = false]; + + // delegations defines the validator set at genesis. + repeated Validator validators = 4 [(gogoproto.nullable) = false]; + + // delegations defines the delegations active at genesis. + repeated Delegation delegations = 5 [(gogoproto.nullable) = false]; + + // unbonding_delegations defines the unbonding delegations active at genesis. + repeated UnbondingDelegation unbonding_delegations = 6 + [(gogoproto.moretags) = "yaml:\"unbonding_delegations\"", (gogoproto.nullable) = false]; + + // redelegations defines the redelegations active at genesis. + repeated Redelegation redelegations = 7 [(gogoproto.nullable) = false]; + + bool exported = 8; +} + +// LastValidatorPower required for validator set update logic. +message LastValidatorPower { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // address is the address of the validator. + string address = 1; + + // power defines the power of the validator. + int64 power = 2; +} diff --git a/ampd/proto/third_party/cosmos/staking/v1beta1/query.proto b/ampd/proto/third_party/cosmos/staking/v1beta1/query.proto new file mode 100644 index 000000000..4852c5353 --- /dev/null +++ b/ampd/proto/third_party/cosmos/staking/v1beta1/query.proto @@ -0,0 +1,348 @@ +syntax = "proto3"; +package cosmos.staking.v1beta1; + +import "cosmos/base/query/v1beta1/pagination.proto"; +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cosmos/staking/v1beta1/staking.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/staking/types"; + +// Query defines the gRPC querier service. +service Query { + // Validators queries all validators that match the given status. + rpc Validators(QueryValidatorsRequest) returns (QueryValidatorsResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/validators"; + } + + // Validator queries validator info for given validator address. + rpc Validator(QueryValidatorRequest) returns (QueryValidatorResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/validators/{validator_addr}"; + } + + // ValidatorDelegations queries delegate info for given validator. + rpc ValidatorDelegations(QueryValidatorDelegationsRequest) returns (QueryValidatorDelegationsResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/validators/{validator_addr}/delegations"; + } + + // ValidatorUnbondingDelegations queries unbonding delegations of a validator. + rpc ValidatorUnbondingDelegations(QueryValidatorUnbondingDelegationsRequest) + returns (QueryValidatorUnbondingDelegationsResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/validators/" + "{validator_addr}/unbonding_delegations"; + } + + // Delegation queries delegate info for given validator delegator pair. + rpc Delegation(QueryDelegationRequest) returns (QueryDelegationResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/validators/{validator_addr}/delegations/" + "{delegator_addr}"; + } + + // UnbondingDelegation queries unbonding info for given validator delegator + // pair. + rpc UnbondingDelegation(QueryUnbondingDelegationRequest) returns (QueryUnbondingDelegationResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/validators/{validator_addr}/delegations/" + "{delegator_addr}/unbonding_delegation"; + } + + // DelegatorDelegations queries all delegations of a given delegator address. + rpc DelegatorDelegations(QueryDelegatorDelegationsRequest) returns (QueryDelegatorDelegationsResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/delegations/{delegator_addr}"; + } + + // DelegatorUnbondingDelegations queries all unbonding delegations of a given + // delegator address. + rpc DelegatorUnbondingDelegations(QueryDelegatorUnbondingDelegationsRequest) + returns (QueryDelegatorUnbondingDelegationsResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/delegators/" + "{delegator_addr}/unbonding_delegations"; + } + + // Redelegations queries redelegations of given address. + rpc Redelegations(QueryRedelegationsRequest) returns (QueryRedelegationsResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/delegators/{delegator_addr}/redelegations"; + } + + // DelegatorValidators queries all validators info for given delegator + // address. + rpc DelegatorValidators(QueryDelegatorValidatorsRequest) returns (QueryDelegatorValidatorsResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/delegators/{delegator_addr}/validators"; + } + + // DelegatorValidator queries validator info for given delegator validator + // pair. + rpc DelegatorValidator(QueryDelegatorValidatorRequest) returns (QueryDelegatorValidatorResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/delegators/{delegator_addr}/validators/" + "{validator_addr}"; + } + + // HistoricalInfo queries the historical info for given height. + rpc HistoricalInfo(QueryHistoricalInfoRequest) returns (QueryHistoricalInfoResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/historical_info/{height}"; + } + + // Pool queries the pool info. + rpc Pool(QueryPoolRequest) returns (QueryPoolResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/pool"; + } + + // Parameters queries the staking parameters. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/params"; + } +} + +// QueryValidatorsRequest is request type for Query/Validators RPC method. +message QueryValidatorsRequest { + // status enables to query for validators matching a given status. + string status = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryValidatorsResponse is response type for the Query/Validators RPC method +message QueryValidatorsResponse { + // validators contains all the queried validators. + repeated Validator validators = 1 [(gogoproto.nullable) = false]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryValidatorRequest is response type for the Query/Validator RPC method +message QueryValidatorRequest { + // validator_addr defines the validator address to query for. + string validator_addr = 1; +} + +// QueryValidatorResponse is response type for the Query/Validator RPC method +message QueryValidatorResponse { + // validator defines the the validator info. + Validator validator = 1 [(gogoproto.nullable) = false]; +} + +// QueryValidatorDelegationsRequest is request type for the +// Query/ValidatorDelegations RPC method +message QueryValidatorDelegationsRequest { + // validator_addr defines the validator address to query for. + string validator_addr = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryValidatorDelegationsResponse is response type for the +// Query/ValidatorDelegations RPC method +message QueryValidatorDelegationsResponse { + repeated DelegationResponse delegation_responses = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "DelegationResponses"]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryValidatorUnbondingDelegationsRequest is required type for the +// Query/ValidatorUnbondingDelegations RPC method +message QueryValidatorUnbondingDelegationsRequest { + // validator_addr defines the validator address to query for. + string validator_addr = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryValidatorUnbondingDelegationsResponse is response type for the +// Query/ValidatorUnbondingDelegations RPC method. +message QueryValidatorUnbondingDelegationsResponse { + repeated UnbondingDelegation unbonding_responses = 1 [(gogoproto.nullable) = false]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryDelegationRequest is request type for the Query/Delegation RPC method. +message QueryDelegationRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // delegator_addr defines the delegator address to query for. + string delegator_addr = 1; + + // validator_addr defines the validator address to query for. + string validator_addr = 2; +} + +// QueryDelegationResponse is response type for the Query/Delegation RPC method. +message QueryDelegationResponse { + // delegation_responses defines the delegation info of a delegation. + DelegationResponse delegation_response = 1; +} + +// QueryUnbondingDelegationRequest is request type for the +// Query/UnbondingDelegation RPC method. +message QueryUnbondingDelegationRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // delegator_addr defines the delegator address to query for. + string delegator_addr = 1; + + // validator_addr defines the validator address to query for. + string validator_addr = 2; +} + +// QueryDelegationResponse is response type for the Query/UnbondingDelegation +// RPC method. +message QueryUnbondingDelegationResponse { + // unbond defines the unbonding information of a delegation. + UnbondingDelegation unbond = 1 [(gogoproto.nullable) = false]; +} + +// QueryDelegatorDelegationsRequest is request type for the +// Query/DelegatorDelegations RPC method. +message QueryDelegatorDelegationsRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // delegator_addr defines the delegator address to query for. + string delegator_addr = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryDelegatorDelegationsResponse is response type for the +// Query/DelegatorDelegations RPC method. +message QueryDelegatorDelegationsResponse { + // delegation_responses defines all the delegations' info of a delegator. + repeated DelegationResponse delegation_responses = 1 [(gogoproto.nullable) = false]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryDelegatorUnbondingDelegationsRequest is request type for the +// Query/DelegatorUnbondingDelegations RPC method. +message QueryDelegatorUnbondingDelegationsRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // delegator_addr defines the delegator address to query for. + string delegator_addr = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryUnbondingDelegatorDelegationsResponse is response type for the +// Query/UnbondingDelegatorDelegations RPC method. +message QueryDelegatorUnbondingDelegationsResponse { + repeated UnbondingDelegation unbonding_responses = 1 [(gogoproto.nullable) = false]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryRedelegationsRequest is request type for the Query/Redelegations RPC +// method. +message QueryRedelegationsRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // delegator_addr defines the delegator address to query for. + string delegator_addr = 1; + + // src_validator_addr defines the validator address to redelegate from. + string src_validator_addr = 2; + + // dst_validator_addr defines the validator address to redelegate to. + string dst_validator_addr = 3; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 4; +} + +// QueryRedelegationsResponse is response type for the Query/Redelegations RPC +// method. +message QueryRedelegationsResponse { + repeated RedelegationResponse redelegation_responses = 1 [(gogoproto.nullable) = false]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryDelegatorValidatorsRequest is request type for the +// Query/DelegatorValidators RPC method. +message QueryDelegatorValidatorsRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // delegator_addr defines the delegator address to query for. + string delegator_addr = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryDelegatorValidatorsResponse is response type for the +// Query/DelegatorValidators RPC method. +message QueryDelegatorValidatorsResponse { + // validators defines the the validators' info of a delegator. + repeated Validator validators = 1 [(gogoproto.nullable) = false]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryDelegatorValidatorRequest is request type for the +// Query/DelegatorValidator RPC method. +message QueryDelegatorValidatorRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // delegator_addr defines the delegator address to query for. + string delegator_addr = 1; + + // validator_addr defines the validator address to query for. + string validator_addr = 2; +} + +// QueryDelegatorValidatorResponse response type for the +// Query/DelegatorValidator RPC method. +message QueryDelegatorValidatorResponse { + // validator defines the the validator info. + Validator validator = 1 [(gogoproto.nullable) = false]; +} + +// QueryHistoricalInfoRequest is request type for the Query/HistoricalInfo RPC +// method. +message QueryHistoricalInfoRequest { + // height defines at which height to query the historical info. + int64 height = 1; +} + +// QueryHistoricalInfoResponse is response type for the Query/HistoricalInfo RPC +// method. +message QueryHistoricalInfoResponse { + // hist defines the historical info at the given height. + HistoricalInfo hist = 1; +} + +// QueryPoolRequest is request type for the Query/Pool RPC method. +message QueryPoolRequest {} + +// QueryPoolResponse is response type for the Query/Pool RPC method. +message QueryPoolResponse { + // pool defines the pool info. + Pool pool = 1 [(gogoproto.nullable) = false]; +} + +// QueryParamsRequest is request type for the Query/Params RPC method. +message QueryParamsRequest {} + +// QueryParamsResponse is response type for the Query/Params RPC method. +message QueryParamsResponse { + // params holds all the parameters of this module. + Params params = 1 [(gogoproto.nullable) = false]; +} diff --git a/ampd/proto/third_party/cosmos/staking/v1beta1/staking.proto b/ampd/proto/third_party/cosmos/staking/v1beta1/staking.proto new file mode 100644 index 000000000..76e9599e2 --- /dev/null +++ b/ampd/proto/third_party/cosmos/staking/v1beta1/staking.proto @@ -0,0 +1,334 @@ +syntax = "proto3"; +package cosmos.staking.v1beta1; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/timestamp.proto"; + +import "cosmos_proto/cosmos.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "tendermint/types/types.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/staking/types"; + +// HistoricalInfo contains header and validator information for a given block. +// It is stored as part of staking module's state, which persists the `n` most +// recent HistoricalInfo +// (`n` is set by the staking module's `historical_entries` parameter). +message HistoricalInfo { + tendermint.types.Header header = 1 [(gogoproto.nullable) = false]; + repeated Validator valset = 2 [(gogoproto.nullable) = false]; +} + +// CommissionRates defines the initial commission rates to be used for creating +// a validator. +message CommissionRates { + option (gogoproto.equal) = true; + option (gogoproto.goproto_stringer) = false; + + // rate is the commission rate charged to delegators, as a fraction. + string rate = 1 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false]; + // max_rate defines the maximum commission rate which validator can ever charge, as a fraction. + string max_rate = 2 [ + (gogoproto.moretags) = "yaml:\"max_rate\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + // max_change_rate defines the maximum daily increase of the validator commission, as a fraction. + string max_change_rate = 3 [ + (gogoproto.moretags) = "yaml:\"max_change_rate\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; +} + +// Commission defines commission parameters for a given validator. +message Commission { + option (gogoproto.equal) = true; + option (gogoproto.goproto_stringer) = false; + + // commission_rates defines the initial commission rates to be used for creating a validator. + CommissionRates commission_rates = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; + // update_time is the last time the commission rate was changed. + google.protobuf.Timestamp update_time = 2 + [(gogoproto.nullable) = false, (gogoproto.stdtime) = true, (gogoproto.moretags) = "yaml:\"update_time\""]; +} + +// Description defines a validator description. +message Description { + option (gogoproto.equal) = true; + option (gogoproto.goproto_stringer) = false; + + // moniker defines a human-readable name for the validator. + string moniker = 1; + // identity defines an optional identity signature (ex. UPort or Keybase). + string identity = 2; + // website defines an optional website link. + string website = 3; + // security_contact defines an optional email for security contact. + string security_contact = 4 [(gogoproto.moretags) = "yaml:\"security_contact\""]; + // details define other optional details. + string details = 5; +} + +// Validator defines a validator, together with the total amount of the +// Validator's bond shares and their exchange rate to coins. Slashing results in +// a decrease in the exchange rate, allowing correct calculation of future +// undelegations without iterating over delegators. When coins are delegated to +// this validator, the validator is credited with a delegation whose number of +// bond shares is based on the amount of coins delegated divided by the current +// exchange rate. Voting power can be calculated as total bonded shares +// multiplied by exchange rate. +message Validator { + option (gogoproto.equal) = false; + option (gogoproto.goproto_stringer) = false; + option (gogoproto.goproto_getters) = false; + + // operator_address defines the address of the validator's operator; bech encoded in JSON. + string operator_address = 1 [(gogoproto.moretags) = "yaml:\"operator_address\""]; + // consensus_pubkey is the consensus public key of the validator, as a Protobuf Any. + google.protobuf.Any consensus_pubkey = 2 + [(cosmos_proto.accepts_interface) = "cosmos.crypto.PubKey", (gogoproto.moretags) = "yaml:\"consensus_pubkey\""]; + // jailed defined whether the validator has been jailed from bonded status or not. + bool jailed = 3; + // status is the validator status (bonded/unbonding/unbonded). + BondStatus status = 4; + // tokens define the delegated tokens (incl. self-delegation). + string tokens = 5 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", (gogoproto.nullable) = false]; + // delegator_shares defines total shares issued to a validator's delegators. + string delegator_shares = 6 [ + (gogoproto.moretags) = "yaml:\"delegator_shares\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + // description defines the description terms for the validator. + Description description = 7 [(gogoproto.nullable) = false]; + // unbonding_height defines, if unbonding, the height at which this validator has begun unbonding. + int64 unbonding_height = 8 [(gogoproto.moretags) = "yaml:\"unbonding_height\""]; + // unbonding_time defines, if unbonding, the min time for the validator to complete unbonding. + google.protobuf.Timestamp unbonding_time = 9 + [(gogoproto.nullable) = false, (gogoproto.stdtime) = true, (gogoproto.moretags) = "yaml:\"unbonding_time\""]; + // commission defines the commission parameters. + Commission commission = 10 [(gogoproto.nullable) = false]; + // min_self_delegation is the validator's self declared minimum self delegation. + string min_self_delegation = 11 [ + (gogoproto.moretags) = "yaml:\"min_self_delegation\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; +} + +// BondStatus is the status of a validator. +enum BondStatus { + option (gogoproto.goproto_enum_prefix) = false; + + // UNSPECIFIED defines an invalid validator status. + BOND_STATUS_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "Unspecified"]; + // UNBONDED defines a validator that is not bonded. + BOND_STATUS_UNBONDED = 1 [(gogoproto.enumvalue_customname) = "Unbonded"]; + // UNBONDING defines a validator that is unbonding. + BOND_STATUS_UNBONDING = 2 [(gogoproto.enumvalue_customname) = "Unbonding"]; + // BONDED defines a validator that is bonded. + BOND_STATUS_BONDED = 3 [(gogoproto.enumvalue_customname) = "Bonded"]; +} + +// ValAddresses defines a repeated set of validator addresses. +message ValAddresses { + option (gogoproto.goproto_stringer) = false; + option (gogoproto.stringer) = true; + + repeated string addresses = 1; +} + +// DVPair is struct that just has a delegator-validator pair with no other data. +// It is intended to be used as a marshalable pointer. For example, a DVPair can +// be used to construct the key to getting an UnbondingDelegation from state. +message DVPair { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + string validator_address = 2 [(gogoproto.moretags) = "yaml:\"validator_address\""]; +} + +// DVPairs defines an array of DVPair objects. +message DVPairs { + repeated DVPair pairs = 1 [(gogoproto.nullable) = false]; +} + +// DVVTriplet is struct that just has a delegator-validator-validator triplet +// with no other data. It is intended to be used as a marshalable pointer. For +// example, a DVVTriplet can be used to construct the key to getting a +// Redelegation from state. +message DVVTriplet { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + string validator_src_address = 2 [(gogoproto.moretags) = "yaml:\"validator_src_address\""]; + string validator_dst_address = 3 [(gogoproto.moretags) = "yaml:\"validator_dst_address\""]; +} + +// DVVTriplets defines an array of DVVTriplet objects. +message DVVTriplets { + repeated DVVTriplet triplets = 1 [(gogoproto.nullable) = false]; +} + +// Delegation represents the bond with tokens held by an account. It is +// owned by one delegator, and is associated with the voting power of one +// validator. +message Delegation { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + // delegator_address is the bech32-encoded address of the delegator. + string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + // validator_address is the bech32-encoded address of the validator. + string validator_address = 2 [(gogoproto.moretags) = "yaml:\"validator_address\""]; + // shares define the delegation shares received. + string shares = 3 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false]; +} + +// UnbondingDelegation stores all of a single delegator's unbonding bonds +// for a single validator in an time-ordered list. +message UnbondingDelegation { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + // delegator_address is the bech32-encoded address of the delegator. + string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + // validator_address is the bech32-encoded address of the validator. + string validator_address = 2 [(gogoproto.moretags) = "yaml:\"validator_address\""]; + // entries are the unbonding delegation entries. + repeated UnbondingDelegationEntry entries = 3 [(gogoproto.nullable) = false]; // unbonding delegation entries +} + +// UnbondingDelegationEntry defines an unbonding object with relevant metadata. +message UnbondingDelegationEntry { + option (gogoproto.equal) = true; + option (gogoproto.goproto_stringer) = false; + + // creation_height is the height which the unbonding took place. + int64 creation_height = 1 [(gogoproto.moretags) = "yaml:\"creation_height\""]; + // completion_time is the unix time for unbonding completion. + google.protobuf.Timestamp completion_time = 2 + [(gogoproto.nullable) = false, (gogoproto.stdtime) = true, (gogoproto.moretags) = "yaml:\"completion_time\""]; + // initial_balance defines the tokens initially scheduled to receive at completion. + string initial_balance = 3 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"initial_balance\"" + ]; + // balance defines the tokens to receive at completion. + string balance = 4 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", (gogoproto.nullable) = false]; +} + +// RedelegationEntry defines a redelegation object with relevant metadata. +message RedelegationEntry { + option (gogoproto.equal) = true; + option (gogoproto.goproto_stringer) = false; + + // creation_height defines the height which the redelegation took place. + int64 creation_height = 1 [(gogoproto.moretags) = "yaml:\"creation_height\""]; + // completion_time defines the unix time for redelegation completion. + google.protobuf.Timestamp completion_time = 2 + [(gogoproto.nullable) = false, (gogoproto.stdtime) = true, (gogoproto.moretags) = "yaml:\"completion_time\""]; + // initial_balance defines the initial balance when redelegation started. + string initial_balance = 3 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"initial_balance\"" + ]; + // shares_dst is the amount of destination-validator shares created by redelegation. + string shares_dst = 4 + [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false]; +} + +// Redelegation contains the list of a particular delegator's redelegating bonds +// from a particular source validator to a particular destination validator. +message Redelegation { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + // delegator_address is the bech32-encoded address of the delegator. + string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + // validator_src_address is the validator redelegation source operator address. + string validator_src_address = 2 [(gogoproto.moretags) = "yaml:\"validator_src_address\""]; + // validator_dst_address is the validator redelegation destination operator address. + string validator_dst_address = 3 [(gogoproto.moretags) = "yaml:\"validator_dst_address\""]; + // entries are the redelegation entries. + repeated RedelegationEntry entries = 4 [(gogoproto.nullable) = false]; // redelegation entries +} + +// Params defines the parameters for the staking module. +message Params { + option (gogoproto.equal) = true; + option (gogoproto.goproto_stringer) = false; + + // unbonding_time is the time duration of unbonding. + google.protobuf.Duration unbonding_time = 1 + [(gogoproto.nullable) = false, (gogoproto.stdduration) = true, (gogoproto.moretags) = "yaml:\"unbonding_time\""]; + // max_validators is the maximum number of validators. + uint32 max_validators = 2 [(gogoproto.moretags) = "yaml:\"max_validators\""]; + // max_entries is the max entries for either unbonding delegation or redelegation (per pair/trio). + uint32 max_entries = 3 [(gogoproto.moretags) = "yaml:\"max_entries\""]; + // historical_entries is the number of historical entries to persist. + uint32 historical_entries = 4 [(gogoproto.moretags) = "yaml:\"historical_entries\""]; + // bond_denom defines the bondable coin denomination. + string bond_denom = 5 [(gogoproto.moretags) = "yaml:\"bond_denom\""]; +} + +// DelegationResponse is equivalent to Delegation except that it contains a +// balance in addition to shares which is more suitable for client responses. +message DelegationResponse { + option (gogoproto.equal) = false; + option (gogoproto.goproto_stringer) = false; + + Delegation delegation = 1 [(gogoproto.nullable) = false]; + + cosmos.base.v1beta1.Coin balance = 2 [(gogoproto.nullable) = false]; +} + +// RedelegationEntryResponse is equivalent to a RedelegationEntry except that it +// contains a balance in addition to shares which is more suitable for client +// responses. +message RedelegationEntryResponse { + option (gogoproto.equal) = true; + + RedelegationEntry redelegation_entry = 1 [(gogoproto.nullable) = false]; + string balance = 4 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", (gogoproto.nullable) = false]; +} + +// RedelegationResponse is equivalent to a Redelegation except that its entries +// contain a balance in addition to shares which is more suitable for client +// responses. +message RedelegationResponse { + option (gogoproto.equal) = false; + + Redelegation redelegation = 1 [(gogoproto.nullable) = false]; + repeated RedelegationEntryResponse entries = 2 [(gogoproto.nullable) = false]; +} + +// Pool is used for tracking bonded and not-bonded token supply of the bond +// denomination. +message Pool { + option (gogoproto.description) = true; + option (gogoproto.equal) = true; + string not_bonded_tokens = 1 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.jsontag) = "not_bonded_tokens", + (gogoproto.nullable) = false + ]; + string bonded_tokens = 2 [ + (gogoproto.jsontag) = "bonded_tokens", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"bonded_tokens\"" + ]; +} diff --git a/ampd/proto/third_party/cosmos/staking/v1beta1/tx.proto b/ampd/proto/third_party/cosmos/staking/v1beta1/tx.proto new file mode 100644 index 000000000..d074fe010 --- /dev/null +++ b/ampd/proto/third_party/cosmos/staking/v1beta1/tx.proto @@ -0,0 +1,123 @@ +syntax = "proto3"; +package cosmos.staking.v1beta1; + +import "google/protobuf/any.proto"; +import "google/protobuf/timestamp.proto"; +import "gogoproto/gogo.proto"; + +import "cosmos_proto/cosmos.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/staking/v1beta1/staking.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/staking/types"; + +// Msg defines the staking Msg service. +service Msg { + // CreateValidator defines a method for creating a new validator. + rpc CreateValidator(MsgCreateValidator) returns (MsgCreateValidatorResponse); + + // EditValidator defines a method for editing an existing validator. + rpc EditValidator(MsgEditValidator) returns (MsgEditValidatorResponse); + + // Delegate defines a method for performing a delegation of coins + // from a delegator to a validator. + rpc Delegate(MsgDelegate) returns (MsgDelegateResponse); + + // BeginRedelegate defines a method for performing a redelegation + // of coins from a delegator and source validator to a destination validator. + rpc BeginRedelegate(MsgBeginRedelegate) returns (MsgBeginRedelegateResponse); + + // Undelegate defines a method for performing an undelegation from a + // delegate and a validator. + rpc Undelegate(MsgUndelegate) returns (MsgUndelegateResponse); +} + +// MsgCreateValidator defines a SDK message for creating a new validator. +message MsgCreateValidator { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + Description description = 1 [(gogoproto.nullable) = false]; + CommissionRates commission = 2 [(gogoproto.nullable) = false]; + string min_self_delegation = 3 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.moretags) = "yaml:\"min_self_delegation\"", + (gogoproto.nullable) = false + ]; + string delegator_address = 4 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + string validator_address = 5 [(gogoproto.moretags) = "yaml:\"validator_address\""]; + google.protobuf.Any pubkey = 6 [(cosmos_proto.accepts_interface) = "cosmos.crypto.PubKey"]; + cosmos.base.v1beta1.Coin value = 7 [(gogoproto.nullable) = false]; +} + +// MsgCreateValidatorResponse defines the Msg/CreateValidator response type. +message MsgCreateValidatorResponse {} + +// MsgEditValidator defines a SDK message for editing an existing validator. +message MsgEditValidator { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + Description description = 1 [(gogoproto.nullable) = false]; + string validator_address = 2 [(gogoproto.moretags) = "yaml:\"address\""]; + + // We pass a reference to the new commission rate and min self delegation as + // it's not mandatory to update. If not updated, the deserialized rate will be + // zero with no way to distinguish if an update was intended. + // REF: #2373 + string commission_rate = 3 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.moretags) = "yaml:\"commission_rate\"" + ]; + string min_self_delegation = 4 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.moretags) = "yaml:\"min_self_delegation\"" + ]; +} + +// MsgEditValidatorResponse defines the Msg/EditValidator response type. +message MsgEditValidatorResponse {} + +// MsgDelegate defines a SDK message for performing a delegation of coins +// from a delegator to a validator. +message MsgDelegate { + option (gogoproto.equal) = false; + + string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + string validator_address = 2 [(gogoproto.moretags) = "yaml:\"validator_address\""]; + cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false]; +} + +// MsgDelegateResponse defines the Msg/Delegate response type. +message MsgDelegateResponse {} + +// MsgBeginRedelegate defines a SDK message for performing a redelegation +// of coins from a delegator and source validator to a destination validator. +message MsgBeginRedelegate { + option (gogoproto.equal) = false; + + string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + string validator_src_address = 2 [(gogoproto.moretags) = "yaml:\"validator_src_address\""]; + string validator_dst_address = 3 [(gogoproto.moretags) = "yaml:\"validator_dst_address\""]; + cosmos.base.v1beta1.Coin amount = 4 [(gogoproto.nullable) = false]; +} + +// MsgBeginRedelegateResponse defines the Msg/BeginRedelegate response type. +message MsgBeginRedelegateResponse { + google.protobuf.Timestamp completion_time = 1 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; +} + +// MsgUndelegate defines a SDK message for performing an undelegation from a +// delegate and a validator. +message MsgUndelegate { + option (gogoproto.equal) = false; + + string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + string validator_address = 2 [(gogoproto.moretags) = "yaml:\"validator_address\""]; + cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false]; +} + +// MsgUndelegateResponse defines the Msg/Undelegate response type. +message MsgUndelegateResponse { + google.protobuf.Timestamp completion_time = 1 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; +} diff --git a/ampd/proto/third_party/cosmos/tx/signing/v1beta1/signing.proto b/ampd/proto/third_party/cosmos/tx/signing/v1beta1/signing.proto new file mode 100644 index 000000000..50de89c8f --- /dev/null +++ b/ampd/proto/third_party/cosmos/tx/signing/v1beta1/signing.proto @@ -0,0 +1,91 @@ +syntax = "proto3"; +package cosmos.tx.signing.v1beta1; + +import "cosmos/crypto/multisig/v1beta1/multisig.proto"; +import "google/protobuf/any.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/types/tx/signing"; + +// SignMode represents a signing mode with its own security guarantees. +enum SignMode { + // SIGN_MODE_UNSPECIFIED specifies an unknown signing mode and will be + // rejected + SIGN_MODE_UNSPECIFIED = 0; + + // SIGN_MODE_DIRECT specifies a signing mode which uses SignDoc and is + // verified with raw bytes from Tx + SIGN_MODE_DIRECT = 1; + + // SIGN_MODE_TEXTUAL is a future signing mode that will verify some + // human-readable textual representation on top of the binary representation + // from SIGN_MODE_DIRECT + SIGN_MODE_TEXTUAL = 2; + + // SIGN_MODE_LEGACY_AMINO_JSON is a backwards compatibility mode which uses + // Amino JSON and will be removed in the future + SIGN_MODE_LEGACY_AMINO_JSON = 127; + + // SIGN_MODE_EIP_191 specifies the sign mode for EIP 191 signing on the Cosmos + // SDK. Ref: https://eips.ethereum.org/EIPS/eip-191 + // + // Currently, SIGN_MODE_EIP_191 is registered as a SignMode enum variant, + // but is not implemented on the SDK by default. To enable EIP-191, you need + // to pass a custom `TxConfig` that has an implementation of + // `SignModeHandler` for EIP-191. The SDK may decide to fully support + // EIP-191 in the future. + // + // Since: cosmos-sdk 0.45.2 + SIGN_MODE_EIP_191 = 191; +} + +// SignatureDescriptors wraps multiple SignatureDescriptor's. +message SignatureDescriptors { + // signatures are the signature descriptors + repeated SignatureDescriptor signatures = 1; +} + +// SignatureDescriptor is a convenience type which represents the full data for +// a signature including the public key of the signer, signing modes and the +// signature itself. It is primarily used for coordinating signatures between +// clients. +message SignatureDescriptor { + // public_key is the public key of the signer + google.protobuf.Any public_key = 1; + + Data data = 2; + + // sequence is the sequence of the account, which describes the + // number of committed transactions signed by a given address. It is used to prevent + // replay attacks. + uint64 sequence = 3; + + // Data represents signature data + message Data { + // sum is the oneof that specifies whether this represents single or multi-signature data + oneof sum { + // single represents a single signer + Single single = 1; + + // multi represents a multisig signer + Multi multi = 2; + } + + // Single is the signature data for a single signer + message Single { + // mode is the signing mode of the single signer + SignMode mode = 1; + + // signature is the raw signature bytes + bytes signature = 2; + } + + // Multi is the signature data for a multisig public key + message Multi { + // bitarray specifies which keys within the multisig are signing + cosmos.crypto.multisig.v1beta1.CompactBitArray bitarray = 1; + + // signatures is the signatures of the multi-signature + repeated Data signatures = 2; + } + } +} diff --git a/ampd/proto/third_party/cosmos/tx/v1beta1/service.proto b/ampd/proto/third_party/cosmos/tx/v1beta1/service.proto new file mode 100644 index 000000000..d9f828f76 --- /dev/null +++ b/ampd/proto/third_party/cosmos/tx/v1beta1/service.proto @@ -0,0 +1,165 @@ +syntax = "proto3"; +package cosmos.tx.v1beta1; + +import "google/api/annotations.proto"; +import "cosmos/base/abci/v1beta1/abci.proto"; +import "cosmos/tx/v1beta1/tx.proto"; +import "gogoproto/gogo.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; +import "tendermint/types/block.proto"; +import "tendermint/types/types.proto"; + +option (gogoproto.goproto_registration) = true; +option go_package = "github.com/cosmos/cosmos-sdk/types/tx"; + +// Service defines a gRPC service for interacting with transactions. +service Service { + // Simulate simulates executing a transaction for estimating gas usage. + rpc Simulate(SimulateRequest) returns (SimulateResponse) { + option (google.api.http) = { + post: "/cosmos/tx/v1beta1/simulate" + body: "*" + }; + } + // GetTx fetches a tx by hash. + rpc GetTx(GetTxRequest) returns (GetTxResponse) { + option (google.api.http).get = "/cosmos/tx/v1beta1/txs/{hash}"; + } + // BroadcastTx broadcast transaction. + rpc BroadcastTx(BroadcastTxRequest) returns (BroadcastTxResponse) { + option (google.api.http) = { + post: "/cosmos/tx/v1beta1/txs" + body: "*" + }; + } + // GetTxsEvent fetches txs by event. + rpc GetTxsEvent(GetTxsEventRequest) returns (GetTxsEventResponse) { + option (google.api.http).get = "/cosmos/tx/v1beta1/txs"; + } + // GetBlockWithTxs fetches a block with decoded txs. + // + // Since: cosmos-sdk 0.45.2 + rpc GetBlockWithTxs(GetBlockWithTxsRequest) returns (GetBlockWithTxsResponse) { + option (google.api.http).get = "/cosmos/tx/v1beta1/txs/block/{height}"; + } +} + +// GetTxsEventRequest is the request type for the Service.TxsByEvents +// RPC method. +message GetTxsEventRequest { + // events is the list of transaction event type. + repeated string events = 1; + // pagination defines a pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; + OrderBy order_by = 3; +} + +// OrderBy defines the sorting order +enum OrderBy { + // ORDER_BY_UNSPECIFIED specifies an unknown sorting order. OrderBy defaults to ASC in this case. + ORDER_BY_UNSPECIFIED = 0; + // ORDER_BY_ASC defines ascending order + ORDER_BY_ASC = 1; + // ORDER_BY_DESC defines descending order + ORDER_BY_DESC = 2; +} + +// GetTxsEventResponse is the response type for the Service.TxsByEvents +// RPC method. +message GetTxsEventResponse { + // txs is the list of queried transactions. + repeated cosmos.tx.v1beta1.Tx txs = 1; + // tx_responses is the list of queried TxResponses. + repeated cosmos.base.abci.v1beta1.TxResponse tx_responses = 2; + // pagination defines a pagination for the response. + cosmos.base.query.v1beta1.PageResponse pagination = 3; +} + +// BroadcastTxRequest is the request type for the Service.BroadcastTxRequest +// RPC method. +message BroadcastTxRequest { + // tx_bytes is the raw transaction. + bytes tx_bytes = 1; + BroadcastMode mode = 2; +} + +// BroadcastMode specifies the broadcast mode for the TxService.Broadcast RPC method. +enum BroadcastMode { + // zero-value for mode ordering + BROADCAST_MODE_UNSPECIFIED = 0; + // BROADCAST_MODE_BLOCK defines a tx broadcasting mode where the client waits for + // the tx to be committed in a block. + BROADCAST_MODE_BLOCK = 1; + // BROADCAST_MODE_SYNC defines a tx broadcasting mode where the client waits for + // a CheckTx execution response only. + BROADCAST_MODE_SYNC = 2; + // BROADCAST_MODE_ASYNC defines a tx broadcasting mode where the client returns + // immediately. + BROADCAST_MODE_ASYNC = 3; +} + +// BroadcastTxResponse is the response type for the +// Service.BroadcastTx method. +message BroadcastTxResponse { + // tx_response is the queried TxResponses. + cosmos.base.abci.v1beta1.TxResponse tx_response = 1; +} + +// SimulateRequest is the request type for the Service.Simulate +// RPC method. +message SimulateRequest { + // tx is the transaction to simulate. + // Deprecated. Send raw tx bytes instead. + cosmos.tx.v1beta1.Tx tx = 1 [deprecated = true]; + // tx_bytes is the raw transaction. + // + // Since: cosmos-sdk 0.43 + bytes tx_bytes = 2; +} + +// SimulateResponse is the response type for the +// Service.SimulateRPC method. +message SimulateResponse { + // gas_info is the information about gas used in the simulation. + cosmos.base.abci.v1beta1.GasInfo gas_info = 1; + // result is the result of the simulation. + cosmos.base.abci.v1beta1.Result result = 2; +} + +// GetTxRequest is the request type for the Service.GetTx +// RPC method. +message GetTxRequest { + // hash is the tx hash to query, encoded as a hex string. + string hash = 1; +} + +// GetTxResponse is the response type for the Service.GetTx method. +message GetTxResponse { + // tx is the queried transaction. + cosmos.tx.v1beta1.Tx tx = 1; + // tx_response is the queried TxResponses. + cosmos.base.abci.v1beta1.TxResponse tx_response = 2; +} + +// GetBlockWithTxsRequest is the request type for the Service.GetBlockWithTxs +// RPC method. +// +// Since: cosmos-sdk 0.45.2 +message GetBlockWithTxsRequest { + // height is the height of the block to query. + int64 height = 1; + // pagination defines a pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// GetBlockWithTxsResponse is the response type for the Service.GetBlockWithTxs method. +// +// Since: cosmos-sdk 0.45.2 +message GetBlockWithTxsResponse { + // txs are the transactions in the block. + repeated cosmos.tx.v1beta1.Tx txs = 1; + .tendermint.types.BlockID block_id = 2; + .tendermint.types.Block block = 3; + // pagination defines a pagination for the response. + cosmos.base.query.v1beta1.PageResponse pagination = 4; +} \ No newline at end of file diff --git a/ampd/proto/third_party/cosmos/tx/v1beta1/tx.proto b/ampd/proto/third_party/cosmos/tx/v1beta1/tx.proto new file mode 100644 index 000000000..6d5caf12c --- /dev/null +++ b/ampd/proto/third_party/cosmos/tx/v1beta1/tx.proto @@ -0,0 +1,183 @@ +syntax = "proto3"; +package cosmos.tx.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/crypto/multisig/v1beta1/multisig.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/tx/signing/v1beta1/signing.proto"; +import "google/protobuf/any.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/types/tx"; + +// Tx is the standard type used for broadcasting transactions. +message Tx { + // body is the processable content of the transaction + TxBody body = 1; + + // auth_info is the authorization related content of the transaction, + // specifically signers, signer modes and fee + AuthInfo auth_info = 2; + + // signatures is a list of signatures that matches the length and order of + // AuthInfo's signer_infos to allow connecting signature meta information like + // public key and signing mode by position. + repeated bytes signatures = 3; +} + +// TxRaw is a variant of Tx that pins the signer's exact binary representation +// of body and auth_info. This is used for signing, broadcasting and +// verification. The binary `serialize(tx: TxRaw)` is stored in Tendermint and +// the hash `sha256(serialize(tx: TxRaw))` becomes the "txhash", commonly used +// as the transaction ID. +message TxRaw { + // body_bytes is a protobuf serialization of a TxBody that matches the + // representation in SignDoc. + bytes body_bytes = 1; + + // auth_info_bytes is a protobuf serialization of an AuthInfo that matches the + // representation in SignDoc. + bytes auth_info_bytes = 2; + + // signatures is a list of signatures that matches the length and order of + // AuthInfo's signer_infos to allow connecting signature meta information like + // public key and signing mode by position. + repeated bytes signatures = 3; +} + +// SignDoc is the type used for generating sign bytes for SIGN_MODE_DIRECT. +message SignDoc { + // body_bytes is protobuf serialization of a TxBody that matches the + // representation in TxRaw. + bytes body_bytes = 1; + + // auth_info_bytes is a protobuf serialization of an AuthInfo that matches the + // representation in TxRaw. + bytes auth_info_bytes = 2; + + // chain_id is the unique identifier of the chain this transaction targets. + // It prevents signed transactions from being used on another chain by an + // attacker + string chain_id = 3; + + // account_number is the account number of the account in state + uint64 account_number = 4; +} + +// TxBody is the body of a transaction that all signers sign over. +message TxBody { + // messages is a list of messages to be executed. The required signers of + // those messages define the number and order of elements in AuthInfo's + // signer_infos and Tx's signatures. Each required signer address is added to + // the list only the first time it occurs. + // By convention, the first required signer (usually from the first message) + // is referred to as the primary signer and pays the fee for the whole + // transaction. + repeated google.protobuf.Any messages = 1; + + // memo is any arbitrary note/comment to be added to the transaction. + // WARNING: in clients, any publicly exposed text should not be called memo, + // but should be called `note` instead (see https://github.com/cosmos/cosmos-sdk/issues/9122). + string memo = 2; + + // timeout is the block height after which this transaction will not + // be processed by the chain + uint64 timeout_height = 3; + + // extension_options are arbitrary options that can be added by chains + // when the default options are not sufficient. If any of these are present + // and can't be handled, the transaction will be rejected + repeated google.protobuf.Any extension_options = 1023; + + // extension_options are arbitrary options that can be added by chains + // when the default options are not sufficient. If any of these are present + // and can't be handled, they will be ignored + repeated google.protobuf.Any non_critical_extension_options = 2047; +} + +// AuthInfo describes the fee and signer modes that are used to sign a +// transaction. +message AuthInfo { + // signer_infos defines the signing modes for the required signers. The number + // and order of elements must match the required signers from TxBody's + // messages. The first element is the primary signer and the one which pays + // the fee. + repeated SignerInfo signer_infos = 1; + + // Fee is the fee and gas limit for the transaction. The first signer is the + // primary signer and the one which pays the fee. The fee can be calculated + // based on the cost of evaluating the body and doing signature verification + // of the signers. This can be estimated via simulation. + Fee fee = 2; +} + +// SignerInfo describes the public key and signing mode of a single top-level +// signer. +message SignerInfo { + // public_key is the public key of the signer. It is optional for accounts + // that already exist in state. If unset, the verifier can use the required \ + // signer address for this position and lookup the public key. + google.protobuf.Any public_key = 1; + + // mode_info describes the signing mode of the signer and is a nested + // structure to support nested multisig pubkey's + ModeInfo mode_info = 2; + + // sequence is the sequence of the account, which describes the + // number of committed transactions signed by a given address. It is used to + // prevent replay attacks. + uint64 sequence = 3; +} + +// ModeInfo describes the signing mode of a single or nested multisig signer. +message ModeInfo { + // sum is the oneof that specifies whether this represents a single or nested + // multisig signer + oneof sum { + // single represents a single signer + Single single = 1; + + // multi represents a nested multisig signer + Multi multi = 2; + } + + // Single is the mode info for a single signer. It is structured as a message + // to allow for additional fields such as locale for SIGN_MODE_TEXTUAL in the + // future + message Single { + // mode is the signing mode of the single signer + cosmos.tx.signing.v1beta1.SignMode mode = 1; + } + + // Multi is the mode info for a multisig public key + message Multi { + // bitarray specifies which keys within the multisig are signing + cosmos.crypto.multisig.v1beta1.CompactBitArray bitarray = 1; + + // mode_infos is the corresponding modes of the signers of the multisig + // which could include nested multisig public keys + repeated ModeInfo mode_infos = 2; + } +} + +// Fee includes the amount of coins paid in fees and the maximum +// gas to be used by the transaction. The ratio yields an effective "gasprice", +// which must be above some miminum to be accepted into the mempool. +message Fee { + // amount is the amount of coins to be paid as a fee + repeated cosmos.base.v1beta1.Coin amount = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + + // gas_limit is the maximum gas that can be used in transaction processing + // before an out of gas error occurs + uint64 gas_limit = 2; + + // if unset, the first signer is responsible for paying the fees. If set, the specified account must pay the fees. + // the payer must be a tx signer (and thus have signed this field in AuthInfo). + // setting this field does *not* change the ordering of required signers for the transaction. + string payer = 3; + + // if set, the fee payer (either the first signer or the value of the payer field) requests that a fee grant be used + // to pay fees instead of the fee payer's own balance. If an appropriate fee grant does not exist or the chain does + // not support fee grants, this will fail + string granter = 4; +} diff --git a/ampd/proto/third_party/cosmos/upgrade/v1beta1/query.proto b/ampd/proto/third_party/cosmos/upgrade/v1beta1/query.proto new file mode 100644 index 000000000..dd14ba640 --- /dev/null +++ b/ampd/proto/third_party/cosmos/upgrade/v1beta1/query.proto @@ -0,0 +1,104 @@ +syntax = "proto3"; +package cosmos.upgrade.v1beta1; + +import "google/protobuf/any.proto"; +import "google/api/annotations.proto"; +import "cosmos/upgrade/v1beta1/upgrade.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/upgrade/types"; + +// Query defines the gRPC upgrade querier service. +service Query { + // CurrentPlan queries the current upgrade plan. + rpc CurrentPlan(QueryCurrentPlanRequest) returns (QueryCurrentPlanResponse) { + option (google.api.http).get = "/cosmos/upgrade/v1beta1/current_plan"; + } + + // AppliedPlan queries a previously applied upgrade plan by its name. + rpc AppliedPlan(QueryAppliedPlanRequest) returns (QueryAppliedPlanResponse) { + option (google.api.http).get = "/cosmos/upgrade/v1beta1/applied_plan/{name}"; + } + + // UpgradedConsensusState queries the consensus state that will serve + // as a trusted kernel for the next version of this chain. It will only be + // stored at the last height of this chain. + // UpgradedConsensusState RPC not supported with legacy querier + // This rpc is deprecated now that IBC has its own replacement + // (https://github.com/cosmos/ibc-go/blob/2c880a22e9f9cc75f62b527ca94aa75ce1106001/proto/ibc/core/client/v1/query.proto#L54) + rpc UpgradedConsensusState(QueryUpgradedConsensusStateRequest) returns (QueryUpgradedConsensusStateResponse) { + option deprecated = true; + option (google.api.http).get = "/cosmos/upgrade/v1beta1/upgraded_consensus_state/{last_height}"; + } + + // ModuleVersions queries the list of module versions from state. + // + // Since: cosmos-sdk 0.43 + rpc ModuleVersions(QueryModuleVersionsRequest) returns (QueryModuleVersionsResponse) { + option (google.api.http).get = "/cosmos/upgrade/v1beta1/module_versions"; + } +} + +// QueryCurrentPlanRequest is the request type for the Query/CurrentPlan RPC +// method. +message QueryCurrentPlanRequest {} + +// QueryCurrentPlanResponse is the response type for the Query/CurrentPlan RPC +// method. +message QueryCurrentPlanResponse { + // plan is the current upgrade plan. + Plan plan = 1; +} + +// QueryCurrentPlanRequest is the request type for the Query/AppliedPlan RPC +// method. +message QueryAppliedPlanRequest { + // name is the name of the applied plan to query for. + string name = 1; +} + +// QueryAppliedPlanResponse is the response type for the Query/AppliedPlan RPC +// method. +message QueryAppliedPlanResponse { + // height is the block height at which the plan was applied. + int64 height = 1; +} + +// QueryUpgradedConsensusStateRequest is the request type for the Query/UpgradedConsensusState +// RPC method. +message QueryUpgradedConsensusStateRequest { + option deprecated = true; + + // last height of the current chain must be sent in request + // as this is the height under which next consensus state is stored + int64 last_height = 1; +} + +// QueryUpgradedConsensusStateResponse is the response type for the Query/UpgradedConsensusState +// RPC method. +message QueryUpgradedConsensusStateResponse { + option deprecated = true; + reserved 1; + + // Since: cosmos-sdk 0.43 + bytes upgraded_consensus_state = 2; +} + +// QueryModuleVersionsRequest is the request type for the Query/ModuleVersions +// RPC method. +// +// Since: cosmos-sdk 0.43 +message QueryModuleVersionsRequest { + // module_name is a field to query a specific module + // consensus version from state. Leaving this empty will + // fetch the full list of module versions from state + string module_name = 1; +} + +// QueryModuleVersionsResponse is the response type for the Query/ModuleVersions +// RPC method. +// +// Since: cosmos-sdk 0.43 +message QueryModuleVersionsResponse { + // module_versions is a list of module names with their consensus versions. + repeated ModuleVersion module_versions = 1; +} diff --git a/ampd/proto/third_party/cosmos/upgrade/v1beta1/upgrade.proto b/ampd/proto/third_party/cosmos/upgrade/v1beta1/upgrade.proto new file mode 100644 index 000000000..e888b393d --- /dev/null +++ b/ampd/proto/third_party/cosmos/upgrade/v1beta1/upgrade.proto @@ -0,0 +1,78 @@ +syntax = "proto3"; +package cosmos.upgrade.v1beta1; + +import "google/protobuf/any.proto"; +import "gogoproto/gogo.proto"; +import "google/protobuf/timestamp.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/upgrade/types"; +option (gogoproto.goproto_getters_all) = false; + +// Plan specifies information about a planned upgrade and when it should occur. +message Plan { + option (gogoproto.equal) = true; + option (gogoproto.goproto_stringer) = false; + + // Sets the name for the upgrade. This name will be used by the upgraded + // version of the software to apply any special "on-upgrade" commands during + // the first BeginBlock method after the upgrade is applied. It is also used + // to detect whether a software version can handle a given upgrade. If no + // upgrade handler with this name has been set in the software, it will be + // assumed that the software is out-of-date when the upgrade Time or Height is + // reached and the software will exit. + string name = 1; + + // Deprecated: Time based upgrades have been deprecated. Time based upgrade logic + // has been removed from the SDK. + // If this field is not empty, an error will be thrown. + google.protobuf.Timestamp time = 2 [deprecated = true, (gogoproto.stdtime) = true, (gogoproto.nullable) = false]; + + // The height at which the upgrade must be performed. + // Only used if Time is not set. + int64 height = 3; + + // Any application specific upgrade info to be included on-chain + // such as a git commit that validators could automatically upgrade to + string info = 4; + + // Deprecated: UpgradedClientState field has been deprecated. IBC upgrade logic has been + // moved to the IBC module in the sub module 02-client. + // If this field is not empty, an error will be thrown. + google.protobuf.Any upgraded_client_state = 5 + [deprecated = true, (gogoproto.moretags) = "yaml:\"upgraded_client_state\""]; +} + +// SoftwareUpgradeProposal is a gov Content type for initiating a software +// upgrade. +message SoftwareUpgradeProposal { + option (gogoproto.equal) = true; + option (gogoproto.goproto_stringer) = false; + + string title = 1; + string description = 2; + Plan plan = 3 [(gogoproto.nullable) = false]; +} + +// CancelSoftwareUpgradeProposal is a gov Content type for cancelling a software +// upgrade. +message CancelSoftwareUpgradeProposal { + option (gogoproto.equal) = true; + option (gogoproto.goproto_stringer) = false; + + string title = 1; + string description = 2; +} + +// ModuleVersion specifies a module and its consensus version. +// +// Since: cosmos-sdk 0.43 +message ModuleVersion { + option (gogoproto.equal) = true; + option (gogoproto.goproto_stringer) = true; + + // name of the app module + string name = 1; + + // consensus version of the app module + uint64 version = 2; +} diff --git a/ampd/proto/third_party/cosmos/vesting/v1beta1/tx.proto b/ampd/proto/third_party/cosmos/vesting/v1beta1/tx.proto new file mode 100644 index 000000000..c49be802a --- /dev/null +++ b/ampd/proto/third_party/cosmos/vesting/v1beta1/tx.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; +package cosmos.vesting.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"; + +// Msg defines the bank Msg service. +service Msg { + // CreateVestingAccount defines a method that enables creating a vesting + // account. + rpc CreateVestingAccount(MsgCreateVestingAccount) returns (MsgCreateVestingAccountResponse); +} + +// MsgCreateVestingAccount defines a message that enables creating a vesting +// account. +message MsgCreateVestingAccount { + option (gogoproto.equal) = true; + + string from_address = 1 [(gogoproto.moretags) = "yaml:\"from_address\""]; + string to_address = 2 [(gogoproto.moretags) = "yaml:\"to_address\""]; + repeated cosmos.base.v1beta1.Coin amount = 3 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + + int64 end_time = 4 [(gogoproto.moretags) = "yaml:\"end_time\""]; + bool delayed = 5; +} + +// MsgCreateVestingAccountResponse defines the Msg/CreateVestingAccount response type. +message MsgCreateVestingAccountResponse {} \ No newline at end of file diff --git a/ampd/proto/third_party/cosmos/vesting/v1beta1/vesting.proto b/ampd/proto/third_party/cosmos/vesting/v1beta1/vesting.proto new file mode 100644 index 000000000..e9f661f93 --- /dev/null +++ b/ampd/proto/third_party/cosmos/vesting/v1beta1/vesting.proto @@ -0,0 +1,85 @@ +syntax = "proto3"; +package cosmos.vesting.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/auth/v1beta1/auth.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"; + +// BaseVestingAccount implements the VestingAccount interface. It contains all +// the necessary fields needed for any vesting account implementation. +message BaseVestingAccount { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + cosmos.auth.v1beta1.BaseAccount base_account = 1 [(gogoproto.embed) = true]; + repeated cosmos.base.v1beta1.Coin original_vesting = 2 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", + (gogoproto.moretags) = "yaml:\"original_vesting\"" + ]; + repeated cosmos.base.v1beta1.Coin delegated_free = 3 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", + (gogoproto.moretags) = "yaml:\"delegated_free\"" + ]; + repeated cosmos.base.v1beta1.Coin delegated_vesting = 4 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", + (gogoproto.moretags) = "yaml:\"delegated_vesting\"" + ]; + int64 end_time = 5 [(gogoproto.moretags) = "yaml:\"end_time\""]; +} + +// ContinuousVestingAccount implements the VestingAccount interface. It +// continuously vests by unlocking coins linearly with respect to time. +message ContinuousVestingAccount { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + BaseVestingAccount base_vesting_account = 1 [(gogoproto.embed) = true]; + int64 start_time = 2 [(gogoproto.moretags) = "yaml:\"start_time\""]; +} + +// DelayedVestingAccount implements the VestingAccount interface. It vests all +// coins after a specific time, but non prior. In other words, it keeps them +// locked until a specified time. +message DelayedVestingAccount { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + BaseVestingAccount base_vesting_account = 1 [(gogoproto.embed) = true]; +} + +// Period defines a length of time and amount of coins that will vest. +message Period { + option (gogoproto.goproto_stringer) = false; + + int64 length = 1; + repeated cosmos.base.v1beta1.Coin amount = 2 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; +} + +// PeriodicVestingAccount implements the VestingAccount interface. It +// periodically vests by unlocking coins during each specified period. +message PeriodicVestingAccount { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + BaseVestingAccount base_vesting_account = 1 [(gogoproto.embed) = true]; + int64 start_time = 2 [(gogoproto.moretags) = "yaml:\"start_time\""]; + repeated Period vesting_periods = 3 [(gogoproto.moretags) = "yaml:\"vesting_periods\"", (gogoproto.nullable) = false]; +} + +// PermanentLockedAccount implements the VestingAccount interface. It does +// not ever release coins, locking them indefinitely. Coins in this account can +// still be used for delegating and for governance votes even while locked. +// +// Since: cosmos-sdk 0.43 +message PermanentLockedAccount { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + BaseVestingAccount base_vesting_account = 1 [(gogoproto.embed) = true]; +} diff --git a/ampd/proto/third_party/cosmos_proto/cosmos.proto b/ampd/proto/third_party/cosmos_proto/cosmos.proto new file mode 100644 index 000000000..167b17075 --- /dev/null +++ b/ampd/proto/third_party/cosmos_proto/cosmos.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; +package cosmos_proto; + +import "google/protobuf/descriptor.proto"; + +option go_package = "github.com/regen-network/cosmos-proto"; + +extend google.protobuf.MessageOptions { + string interface_type = 93001; + + string implements_interface = 93002; +} + +extend google.protobuf.FieldOptions { + string accepts_interface = 93001; +} diff --git a/ampd/proto/third_party/cosmwasm/wasm/v1/authz.proto b/ampd/proto/third_party/cosmwasm/wasm/v1/authz.proto new file mode 100644 index 000000000..97a82275d --- /dev/null +++ b/ampd/proto/third_party/cosmwasm/wasm/v1/authz.proto @@ -0,0 +1,118 @@ +syntax = "proto3"; +package cosmwasm.wasm.v1; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "google/protobuf/any.proto"; + +option go_package = "github.com/CosmWasm/wasmd/x/wasm/types"; +option (gogoproto.goproto_getters_all) = false; + +// ContractExecutionAuthorization defines authorization for wasm execute. +// Since: wasmd 0.30 +message ContractExecutionAuthorization { + option (cosmos_proto.implements_interface) = + "cosmos.authz.v1beta1.Authorization"; + + // Grants for contract executions + repeated ContractGrant grants = 1 [ (gogoproto.nullable) = false ]; +} + +// ContractMigrationAuthorization defines authorization for wasm contract +// migration. Since: wasmd 0.30 +message ContractMigrationAuthorization { + option (cosmos_proto.implements_interface) = + "cosmos.authz.v1beta1.Authorization"; + + // Grants for contract migrations + repeated ContractGrant grants = 1 [ (gogoproto.nullable) = false ]; +} + +// ContractGrant a granted permission for a single contract +// Since: wasmd 0.30 +message ContractGrant { + // Contract is the bech32 address of the smart contract + string contract = 1; + + // Limit defines execution limits that are enforced and updated when the grant + // is applied. When the limit lapsed the grant is removed. + google.protobuf.Any limit = 2 [ (cosmos_proto.accepts_interface) = + "cosmwasm.wasm.v1.ContractAuthzLimitX" ]; + + // Filter define more fine-grained control on the message payload passed + // to the contract in the operation. When no filter applies on execution, the + // operation is prohibited. + google.protobuf.Any filter = 3 + [ (cosmos_proto.accepts_interface) = + "cosmwasm.wasm.v1.ContractAuthzFilterX" ]; +} + +// MaxCallsLimit limited number of calls to the contract. No funds transferable. +// Since: wasmd 0.30 +message MaxCallsLimit { + option (cosmos_proto.implements_interface) = + "cosmwasm.wasm.v1.ContractAuthzLimitX"; + + // Remaining number that is decremented on each execution + uint64 remaining = 1; +} + +// MaxFundsLimit defines the maximal amounts that can be sent to the contract. +// Since: wasmd 0.30 +message MaxFundsLimit { + option (cosmos_proto.implements_interface) = + "cosmwasm.wasm.v1.ContractAuthzLimitX"; + + // Amounts is the maximal amount of tokens transferable to the contract. + repeated cosmos.base.v1beta1.Coin amounts = 1 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; +} + +// CombinedLimit defines the maximal amounts that can be sent to a contract and +// the maximal number of calls executable. Both need to remain >0 to be valid. +// Since: wasmd 0.30 +message CombinedLimit { + option (cosmos_proto.implements_interface) = + "cosmwasm.wasm.v1.ContractAuthzLimitX"; + + // Remaining number that is decremented on each execution + uint64 calls_remaining = 1; + // Amounts is the maximal amount of tokens transferable to the contract. + repeated cosmos.base.v1beta1.Coin amounts = 2 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; +} + +// AllowAllMessagesFilter is a wildcard to allow any type of contract payload +// message. +// Since: wasmd 0.30 +message AllowAllMessagesFilter { + option (cosmos_proto.implements_interface) = + "cosmwasm.wasm.v1.ContractAuthzFilterX"; +} + +// AcceptedMessageKeysFilter accept only the specific contract message keys in +// the json object to be executed. +// Since: wasmd 0.30 +message AcceptedMessageKeysFilter { + option (cosmos_proto.implements_interface) = + "cosmwasm.wasm.v1.ContractAuthzFilterX"; + + // Messages is the list of unique keys + repeated string keys = 1; +} + +// AcceptedMessagesFilter accept only the specific raw contract messages to be +// executed. +// Since: wasmd 0.30 +message AcceptedMessagesFilter { + option (cosmos_proto.implements_interface) = + "cosmwasm.wasm.v1.ContractAuthzFilterX"; + + // Messages is the list of raw contract messages + repeated bytes messages = 1 [ (gogoproto.casttype) = "RawContractMessage" ]; +} diff --git a/ampd/proto/third_party/cosmwasm/wasm/v1/genesis.proto b/ampd/proto/third_party/cosmwasm/wasm/v1/genesis.proto new file mode 100644 index 000000000..4e728ff4b --- /dev/null +++ b/ampd/proto/third_party/cosmwasm/wasm/v1/genesis.proto @@ -0,0 +1,46 @@ +syntax = "proto3"; +package cosmwasm.wasm.v1; + +import "gogoproto/gogo.proto"; +import "cosmwasm/wasm/v1/types.proto"; + +option go_package = "github.com/CosmWasm/wasmd/x/wasm/types"; + +// GenesisState - genesis state of x/wasm +message GenesisState { + Params params = 1 [ (gogoproto.nullable) = false ]; + repeated Code codes = 2 + [ (gogoproto.nullable) = false, (gogoproto.jsontag) = "codes,omitempty" ]; + repeated Contract contracts = 3 [ + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "contracts,omitempty" + ]; + repeated Sequence sequences = 4 [ + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "sequences,omitempty" + ]; +} + +// Code struct encompasses CodeInfo and CodeBytes +message Code { + uint64 code_id = 1 [ (gogoproto.customname) = "CodeID" ]; + CodeInfo code_info = 2 [ (gogoproto.nullable) = false ]; + bytes code_bytes = 3; + // Pinned to wasmvm cache + bool pinned = 4; +} + +// Contract struct encompasses ContractAddress, ContractInfo, and ContractState +message Contract { + string contract_address = 1; + ContractInfo contract_info = 2 [ (gogoproto.nullable) = false ]; + repeated Model contract_state = 3 [ (gogoproto.nullable) = false ]; + repeated ContractCodeHistoryEntry contract_code_history = 4 + [ (gogoproto.nullable) = false ]; +} + +// Sequence key and value of an id generation counter +message Sequence { + bytes id_key = 1 [ (gogoproto.customname) = "IDKey" ]; + uint64 value = 2; +} \ No newline at end of file diff --git a/ampd/proto/third_party/cosmwasm/wasm/v1/ibc.proto b/ampd/proto/third_party/cosmwasm/wasm/v1/ibc.proto new file mode 100644 index 000000000..feaad2936 --- /dev/null +++ b/ampd/proto/third_party/cosmwasm/wasm/v1/ibc.proto @@ -0,0 +1,37 @@ +syntax = "proto3"; +package cosmwasm.wasm.v1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/CosmWasm/wasmd/x/wasm/types"; +option (gogoproto.goproto_getters_all) = false; + +// MsgIBCSend +message MsgIBCSend { + // the channel by which the packet will be sent + string channel = 2 [ (gogoproto.moretags) = "yaml:\"source_channel\"" ]; + + // Timeout height relative to the current block height. + // The timeout is disabled when set to 0. + uint64 timeout_height = 4 + [ (gogoproto.moretags) = "yaml:\"timeout_height\"" ]; + // Timeout timestamp (in nanoseconds) relative to the current block timestamp. + // The timeout is disabled when set to 0. + uint64 timeout_timestamp = 5 + [ (gogoproto.moretags) = "yaml:\"timeout_timestamp\"" ]; + + // Data is the payload to transfer. We must not make assumption what format or + // content is in here. + bytes data = 6; +} + +// MsgIBCSendResponse +message MsgIBCSendResponse { + // Sequence number of the IBC packet sent + uint64 sequence = 1; +} + +// MsgIBCCloseChannel port and channel need to be owned by the contract +message MsgIBCCloseChannel { + string channel = 2 [ (gogoproto.moretags) = "yaml:\"source_channel\"" ]; +} diff --git a/ampd/proto/third_party/cosmwasm/wasm/v1/proposal.proto b/ampd/proto/third_party/cosmwasm/wasm/v1/proposal.proto new file mode 100644 index 000000000..b1c484bc9 --- /dev/null +++ b/ampd/proto/third_party/cosmwasm/wasm/v1/proposal.proto @@ -0,0 +1,272 @@ +syntax = "proto3"; +package cosmwasm.wasm.v1; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmwasm/wasm/v1/types.proto"; + +option go_package = "github.com/CosmWasm/wasmd/x/wasm/types"; +option (gogoproto.goproto_stringer_all) = false; +option (gogoproto.goproto_getters_all) = false; +option (gogoproto.equal_all) = true; + +// StoreCodeProposal gov proposal content type to submit WASM code to the system +message StoreCodeProposal { + option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content"; + + // Title is a short summary + string title = 1; + // Description is a human readable text + string description = 2; + // RunAs is the address that is passed to the contract's environment as sender + string run_as = 3; + // WASMByteCode can be raw or gzip compressed + bytes wasm_byte_code = 4 [ (gogoproto.customname) = "WASMByteCode" ]; + // Used in v1beta1 + reserved 5, 6; + // InstantiatePermission to apply on contract creation, optional + AccessConfig instantiate_permission = 7; + // UnpinCode code on upload, optional + bool unpin_code = 8; + // Source is the URL where the code is hosted + string source = 9; + // Builder is the docker image used to build the code deterministically, used + // for smart contract verification + string builder = 10; + // CodeHash is the SHA256 sum of the code outputted by builder, used for smart + // contract verification + bytes code_hash = 11; +} + +// InstantiateContractProposal gov proposal content type to instantiate a +// contract. +message InstantiateContractProposal { + option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content"; + + // Title is a short summary + string title = 1; + // Description is a human readable text + string description = 2; + // RunAs is the address that is passed to the contract's environment as sender + string run_as = 3; + // Admin is an optional address that can execute migrations + string admin = 4; + // CodeID is the reference to the stored WASM code + uint64 code_id = 5 [ (gogoproto.customname) = "CodeID" ]; + // Label is optional metadata to be stored with a constract instance. + string label = 6; + // Msg json encoded message to be passed to the contract on instantiation + bytes msg = 7 [ (gogoproto.casttype) = "RawContractMessage" ]; + // Funds coins that are transferred to the contract on instantiation + repeated cosmos.base.v1beta1.Coin funds = 8 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; +} + +// InstantiateContract2Proposal gov proposal content type to instantiate +// contract 2 +message InstantiateContract2Proposal { + option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content"; + + // Title is a short summary + string title = 1; + // Description is a human readable text + string description = 2; + // RunAs is the address that is passed to the contract's enviroment as sender + string run_as = 3; + // Admin is an optional address that can execute migrations + string admin = 4; + // CodeID is the reference to the stored WASM code + uint64 code_id = 5 [ (gogoproto.customname) = "CodeID" ]; + // Label is optional metadata to be stored with a constract instance. + string label = 6; + // Msg json encode message to be passed to the contract on instantiation + bytes msg = 7 [ (gogoproto.casttype) = "RawContractMessage" ]; + // Funds coins that are transferred to the contract on instantiation + repeated cosmos.base.v1beta1.Coin funds = 8 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; + // Salt is an arbitrary value provided by the sender. Size can be 1 to 64. + bytes salt = 9; + // FixMsg include the msg value into the hash for the predictable address. + // Default is false + bool fix_msg = 10; +} + +// MigrateContractProposal gov proposal content type to migrate a contract. +message MigrateContractProposal { + option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content"; + + // Title is a short summary + string title = 1; + // Description is a human readable text + string description = 2; + // Note: skipping 3 as this was previously used for unneeded run_as + + // Contract is the address of the smart contract + string contract = 4; + // CodeID references the new WASM code + uint64 code_id = 5 [ (gogoproto.customname) = "CodeID" ]; + // Msg json encoded message to be passed to the contract on migration + bytes msg = 6 [ (gogoproto.casttype) = "RawContractMessage" ]; +} + +// SudoContractProposal gov proposal content type to call sudo on a contract. +message SudoContractProposal { + option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content"; + + // Title is a short summary + string title = 1; + // Description is a human readable text + string description = 2; + // Contract is the address of the smart contract + string contract = 3; + // Msg json encoded message to be passed to the contract as sudo + bytes msg = 4 [ (gogoproto.casttype) = "RawContractMessage" ]; +} + +// ExecuteContractProposal gov proposal content type to call execute on a +// contract. +message ExecuteContractProposal { + option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content"; + + // Title is a short summary + string title = 1; + // Description is a human readable text + string description = 2; + // RunAs is the address that is passed to the contract's environment as sender + string run_as = 3; + // Contract is the address of the smart contract + string contract = 4; + // Msg json encoded message to be passed to the contract as execute + bytes msg = 5 [ (gogoproto.casttype) = "RawContractMessage" ]; + // Funds coins that are transferred to the contract on instantiation + repeated cosmos.base.v1beta1.Coin funds = 6 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; +} + +// UpdateAdminProposal gov proposal content type to set an admin for a contract. +message UpdateAdminProposal { + option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content"; + + // Title is a short summary + string title = 1; + // Description is a human readable text + string description = 2; + // NewAdmin address to be set + string new_admin = 3 [ (gogoproto.moretags) = "yaml:\"new_admin\"" ]; + // Contract is the address of the smart contract + string contract = 4; +} + +// ClearAdminProposal gov proposal content type to clear the admin of a +// contract. +message ClearAdminProposal { + option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content"; + + // Title is a short summary + string title = 1; + // Description is a human readable text + string description = 2; + // Contract is the address of the smart contract + string contract = 3; +} + +// PinCodesProposal gov proposal content type to pin a set of code ids in the +// wasmvm cache. +message PinCodesProposal { + option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content"; + + // Title is a short summary + string title = 1 [ (gogoproto.moretags) = "yaml:\"title\"" ]; + // Description is a human readable text + string description = 2 [ (gogoproto.moretags) = "yaml:\"description\"" ]; + // CodeIDs references the new WASM codes + repeated uint64 code_ids = 3 [ + (gogoproto.customname) = "CodeIDs", + (gogoproto.moretags) = "yaml:\"code_ids\"" + ]; +} + +// UnpinCodesProposal gov proposal content type to unpin a set of code ids in +// the wasmvm cache. +message UnpinCodesProposal { + option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content"; + + // Title is a short summary + string title = 1 [ (gogoproto.moretags) = "yaml:\"title\"" ]; + // Description is a human readable text + string description = 2 [ (gogoproto.moretags) = "yaml:\"description\"" ]; + // CodeIDs references the WASM codes + repeated uint64 code_ids = 3 [ + (gogoproto.customname) = "CodeIDs", + (gogoproto.moretags) = "yaml:\"code_ids\"" + ]; +} + +// AccessConfigUpdate contains the code id and the access config to be +// applied. +message AccessConfigUpdate { + // CodeID is the reference to the stored WASM code to be updated + uint64 code_id = 1 [ (gogoproto.customname) = "CodeID" ]; + // InstantiatePermission to apply to the set of code ids + AccessConfig instantiate_permission = 2 [ (gogoproto.nullable) = false ]; +} + +// UpdateInstantiateConfigProposal gov proposal content type to update +// instantiate config to a set of code ids. +message UpdateInstantiateConfigProposal { + option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content"; + + // Title is a short summary + string title = 1 [ (gogoproto.moretags) = "yaml:\"title\"" ]; + // Description is a human readable text + string description = 2 [ (gogoproto.moretags) = "yaml:\"description\"" ]; + // AccessConfigUpdate contains the list of code ids and the access config + // to be applied. + repeated AccessConfigUpdate access_config_updates = 3 + [ (gogoproto.nullable) = false ]; +} + +// StoreAndInstantiateContractProposal gov proposal content type to store +// and instantiate the contract. +message StoreAndInstantiateContractProposal { + option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content"; + + // Title is a short summary + string title = 1; + // Description is a human readable text + string description = 2; + // RunAs is the address that is passed to the contract's environment as sender + string run_as = 3; + // WASMByteCode can be raw or gzip compressed + bytes wasm_byte_code = 4 [ (gogoproto.customname) = "WASMByteCode" ]; + // InstantiatePermission to apply on contract creation, optional + AccessConfig instantiate_permission = 5; + // UnpinCode code on upload, optional + bool unpin_code = 6; + // Admin is an optional address that can execute migrations + string admin = 7; + // Label is optional metadata to be stored with a constract instance. + string label = 8; + // Msg json encoded message to be passed to the contract on instantiation + bytes msg = 9 [ (gogoproto.casttype) = "RawContractMessage" ]; + // Funds coins that are transferred to the contract on instantiation + repeated cosmos.base.v1beta1.Coin funds = 10 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; + // Source is the URL where the code is hosted + string source = 11; + // Builder is the docker image used to build the code deterministically, used + // for smart contract verification + string builder = 12; + // CodeHash is the SHA256 sum of the code outputted by builder, used for smart + // contract verification + bytes code_hash = 13; +} diff --git a/ampd/proto/third_party/cosmwasm/wasm/v1/query.proto b/ampd/proto/third_party/cosmwasm/wasm/v1/query.proto new file mode 100644 index 000000000..ffe48d242 --- /dev/null +++ b/ampd/proto/third_party/cosmwasm/wasm/v1/query.proto @@ -0,0 +1,263 @@ +syntax = "proto3"; +package cosmwasm.wasm.v1; + +import "gogoproto/gogo.proto"; +import "cosmwasm/wasm/v1/types.proto"; +import "google/api/annotations.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; + +option go_package = "github.com/CosmWasm/wasmd/x/wasm/types"; +option (gogoproto.goproto_getters_all) = false; +option (gogoproto.equal_all) = false; + +// Query provides defines the gRPC querier service +service Query { + // ContractInfo gets the contract meta data + rpc ContractInfo(QueryContractInfoRequest) + returns (QueryContractInfoResponse) { + option (google.api.http).get = "/cosmwasm/wasm/v1/contract/{address}"; + } + // ContractHistory gets the contract code history + rpc ContractHistory(QueryContractHistoryRequest) + returns (QueryContractHistoryResponse) { + option (google.api.http).get = + "/cosmwasm/wasm/v1/contract/{address}/history"; + } + // ContractsByCode lists all smart contracts for a code id + rpc ContractsByCode(QueryContractsByCodeRequest) + returns (QueryContractsByCodeResponse) { + option (google.api.http).get = "/cosmwasm/wasm/v1/code/{code_id}/contracts"; + } + // AllContractState gets all raw store data for a single contract + rpc AllContractState(QueryAllContractStateRequest) + returns (QueryAllContractStateResponse) { + option (google.api.http).get = "/cosmwasm/wasm/v1/contract/{address}/state"; + } + // RawContractState gets single key from the raw store data of a contract + rpc RawContractState(QueryRawContractStateRequest) + returns (QueryRawContractStateResponse) { + option (google.api.http).get = + "/cosmwasm/wasm/v1/contract/{address}/raw/{query_data}"; + } + // SmartContractState get smart query result from the contract + rpc SmartContractState(QuerySmartContractStateRequest) + returns (QuerySmartContractStateResponse) { + option (google.api.http).get = + "/cosmwasm/wasm/v1/contract/{address}/smart/{query_data}"; + } + // Code gets the binary code and metadata for a singe wasm code + rpc Code(QueryCodeRequest) returns (QueryCodeResponse) { + option (google.api.http).get = "/cosmwasm/wasm/v1/code/{code_id}"; + } + // Codes gets the metadata for all stored wasm codes + rpc Codes(QueryCodesRequest) returns (QueryCodesResponse) { + option (google.api.http).get = "/cosmwasm/wasm/v1/code"; + } + + // PinnedCodes gets the pinned code ids + rpc PinnedCodes(QueryPinnedCodesRequest) returns (QueryPinnedCodesResponse) { + option (google.api.http).get = "/cosmwasm/wasm/v1/codes/pinned"; + } + + // Params gets the module params + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/cosmwasm/wasm/v1/codes/params"; + } + + // ContractsByCreator gets the contracts by creator + rpc ContractsByCreator(QueryContractsByCreatorRequest) + returns (QueryContractsByCreatorResponse) { + option (google.api.http).get = + "/cosmwasm/wasm/v1/contracts/creator/{creator_address}"; + } +} + +// QueryContractInfoRequest is the request type for the Query/ContractInfo RPC +// method +message QueryContractInfoRequest { + // address is the address of the contract to query + string address = 1; +} +// QueryContractInfoResponse is the response type for the Query/ContractInfo RPC +// method +message QueryContractInfoResponse { + option (gogoproto.equal) = true; + + // address is the address of the contract + string address = 1; + ContractInfo contract_info = 2 [ + (gogoproto.embed) = true, + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "" + ]; +} + +// QueryContractHistoryRequest is the request type for the Query/ContractHistory +// RPC method +message QueryContractHistoryRequest { + // address is the address of the contract to query + string address = 1; + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryContractHistoryResponse is the response type for the +// Query/ContractHistory RPC method +message QueryContractHistoryResponse { + repeated ContractCodeHistoryEntry entries = 1 + [ (gogoproto.nullable) = false ]; + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryContractsByCodeRequest is the request type for the Query/ContractsByCode +// RPC method +message QueryContractsByCodeRequest { + uint64 code_id = 1; // grpc-gateway_out does not support Go style CodID + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryContractsByCodeResponse is the response type for the +// Query/ContractsByCode RPC method +message QueryContractsByCodeResponse { + // contracts are a set of contract addresses + repeated string contracts = 1; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryAllContractStateRequest is the request type for the +// Query/AllContractState RPC method +message QueryAllContractStateRequest { + // address is the address of the contract + string address = 1; + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryAllContractStateResponse is the response type for the +// Query/AllContractState RPC method +message QueryAllContractStateResponse { + repeated Model models = 1 [ (gogoproto.nullable) = false ]; + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryRawContractStateRequest is the request type for the +// Query/RawContractState RPC method +message QueryRawContractStateRequest { + // address is the address of the contract + string address = 1; + bytes query_data = 2; +} + +// QueryRawContractStateResponse is the response type for the +// Query/RawContractState RPC method +message QueryRawContractStateResponse { + // Data contains the raw store data + bytes data = 1; +} + +// QuerySmartContractStateRequest is the request type for the +// Query/SmartContractState RPC method +message QuerySmartContractStateRequest { + // address is the address of the contract + string address = 1; + // QueryData contains the query data passed to the contract + bytes query_data = 2 [ (gogoproto.casttype) = "RawContractMessage" ]; +} + +// QuerySmartContractStateResponse is the response type for the +// Query/SmartContractState RPC method +message QuerySmartContractStateResponse { + // Data contains the json data returned from the smart contract + bytes data = 1 [ (gogoproto.casttype) = "RawContractMessage" ]; +} + +// QueryCodeRequest is the request type for the Query/Code RPC method +message QueryCodeRequest { + uint64 code_id = 1; // grpc-gateway_out does not support Go style CodID +} + +// CodeInfoResponse contains code meta data from CodeInfo +message CodeInfoResponse { + option (gogoproto.equal) = true; + + uint64 code_id = 1 [ + (gogoproto.customname) = "CodeID", + (gogoproto.jsontag) = "id" + ]; // id for legacy support + string creator = 2; + bytes data_hash = 3 + [ (gogoproto.casttype) = + "github.com/tendermint/tendermint/libs/bytes.HexBytes" ]; + // Used in v1beta1 + reserved 4, 5; + AccessConfig instantiate_permission = 6 [ (gogoproto.nullable) = false ]; +} + +// QueryCodeResponse is the response type for the Query/Code RPC method +message QueryCodeResponse { + option (gogoproto.equal) = true; + CodeInfoResponse code_info = 1 + [ (gogoproto.embed) = true, (gogoproto.jsontag) = "" ]; + bytes data = 2 [ (gogoproto.jsontag) = "data" ]; +} + +// QueryCodesRequest is the request type for the Query/Codes RPC method +message QueryCodesRequest { + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +// QueryCodesResponse is the response type for the Query/Codes RPC method +message QueryCodesResponse { + repeated CodeInfoResponse code_infos = 1 [ (gogoproto.nullable) = false ]; + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryPinnedCodesRequest is the request type for the Query/PinnedCodes +// RPC method +message QueryPinnedCodesRequest { + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryPinnedCodesResponse is the response type for the +// Query/PinnedCodes RPC method +message QueryPinnedCodesResponse { + repeated uint64 code_ids = 1 + [ (gogoproto.nullable) = false, (gogoproto.customname) = "CodeIDs" ]; + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryParamsRequest is the request type for the Query/Params RPC method. +message QueryParamsRequest {} + +// QueryParamsResponse is the response type for the Query/Params RPC method. +message QueryParamsResponse { + // params defines the parameters of the module. + Params params = 1 [ (gogoproto.nullable) = false ]; +} + +// QueryContractsByCreatorRequest is the request type for the +// Query/ContractsByCreator RPC method. +message QueryContractsByCreatorRequest { + // CreatorAddress is the address of contract creator + string creator_address = 1; + // Pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryContractsByCreatorResponse is the response type for the +// Query/ContractsByCreator RPC method. +message QueryContractsByCreatorResponse { + // ContractAddresses result set + repeated string contract_addresses = 1; + // Pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} \ No newline at end of file diff --git a/ampd/proto/third_party/cosmwasm/wasm/v1/tx.proto b/ampd/proto/third_party/cosmwasm/wasm/v1/tx.proto new file mode 100644 index 000000000..741fbc494 --- /dev/null +++ b/ampd/proto/third_party/cosmwasm/wasm/v1/tx.proto @@ -0,0 +1,192 @@ +syntax = "proto3"; +package cosmwasm.wasm.v1; + +import "cosmos/base/v1beta1/coin.proto"; +import "gogoproto/gogo.proto"; +import "cosmwasm/wasm/v1/types.proto"; + +option go_package = "github.com/CosmWasm/wasmd/x/wasm/types"; +option (gogoproto.goproto_getters_all) = false; + +// Msg defines the wasm Msg service. +service Msg { + // StoreCode to submit Wasm code to the system + rpc StoreCode(MsgStoreCode) returns (MsgStoreCodeResponse); + // InstantiateContract creates a new smart contract instance for the given + // code id. + rpc InstantiateContract(MsgInstantiateContract) + returns (MsgInstantiateContractResponse); + // InstantiateContract2 creates a new smart contract instance for the given + // code id with a predictable address + rpc InstantiateContract2(MsgInstantiateContract2) + returns (MsgInstantiateContract2Response); + // Execute submits the given message data to a smart contract + rpc ExecuteContract(MsgExecuteContract) returns (MsgExecuteContractResponse); + // Migrate runs a code upgrade/ downgrade for a smart contract + rpc MigrateContract(MsgMigrateContract) returns (MsgMigrateContractResponse); + // UpdateAdmin sets a new admin for a smart contract + rpc UpdateAdmin(MsgUpdateAdmin) returns (MsgUpdateAdminResponse); + // ClearAdmin removes any admin stored for a smart contract + rpc ClearAdmin(MsgClearAdmin) returns (MsgClearAdminResponse); + // UpdateInstantiateConfig updates instantiate config for a smart contract + rpc UpdateInstantiateConfig(MsgUpdateInstantiateConfig) + returns (MsgUpdateInstantiateConfigResponse); +} + +// MsgStoreCode submit Wasm code to the system +message MsgStoreCode { + // Sender is the actor that signed the messages + string sender = 1; + // WASMByteCode can be raw or gzip compressed + bytes wasm_byte_code = 2 [ (gogoproto.customname) = "WASMByteCode" ]; + // Used in v1beta1 + reserved 3, 4; + // InstantiatePermission access control to apply on contract creation, + // optional + AccessConfig instantiate_permission = 5; +} +// MsgStoreCodeResponse returns store result data. +message MsgStoreCodeResponse { + // CodeID is the reference to the stored WASM code + uint64 code_id = 1 [ (gogoproto.customname) = "CodeID" ]; + // Checksum is the sha256 hash of the stored code + bytes checksum = 2; +} + +// MsgInstantiateContract create a new smart contract instance for the given +// code id. +message MsgInstantiateContract { + // Sender is the that actor that signed the messages + string sender = 1; + // Admin is an optional address that can execute migrations + string admin = 2; + // CodeID is the reference to the stored WASM code + uint64 code_id = 3 [ (gogoproto.customname) = "CodeID" ]; + // Label is optional metadata to be stored with a contract instance. + string label = 4; + // Msg json encoded message to be passed to the contract on instantiation + bytes msg = 5 [ (gogoproto.casttype) = "RawContractMessage" ]; + // Funds coins that are transferred to the contract on instantiation + repeated cosmos.base.v1beta1.Coin funds = 6 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; +} + +// MsgInstantiateContract2 create a new smart contract instance for the given +// code id with a predicable address. +message MsgInstantiateContract2 { + // Sender is the that actor that signed the messages + string sender = 1; + // Admin is an optional address that can execute migrations + string admin = 2; + // CodeID is the reference to the stored WASM code + uint64 code_id = 3 [ (gogoproto.customname) = "CodeID" ]; + // Label is optional metadata to be stored with a contract instance. + string label = 4; + // Msg json encoded message to be passed to the contract on instantiation + bytes msg = 5 [ (gogoproto.casttype) = "RawContractMessage" ]; + // Funds coins that are transferred to the contract on instantiation + repeated cosmos.base.v1beta1.Coin funds = 6 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; + // Salt is an arbitrary value provided by the sender. Size can be 1 to 64. + bytes salt = 7; + // FixMsg include the msg value into the hash for the predictable address. + // Default is false + bool fix_msg = 8; +} + +// MsgInstantiateContractResponse return instantiation result data +message MsgInstantiateContractResponse { + // Address is the bech32 address of the new contract instance. + string address = 1; + // Data contains bytes to returned from the contract + bytes data = 2; +} + +// MsgInstantiateContract2Response return instantiation result data +message MsgInstantiateContract2Response { + // Address is the bech32 address of the new contract instance. + string address = 1; + // Data contains bytes to returned from the contract + bytes data = 2; +} + +// MsgExecuteContract submits the given message data to a smart contract +message MsgExecuteContract { + // Sender is the that actor that signed the messages + string sender = 1; + // Contract is the address of the smart contract + string contract = 2; + // Msg json encoded message to be passed to the contract + bytes msg = 3 [ (gogoproto.casttype) = "RawContractMessage" ]; + // Funds coins that are transferred to the contract on execution + repeated cosmos.base.v1beta1.Coin funds = 5 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; +} + +// MsgExecuteContractResponse returns execution result data. +message MsgExecuteContractResponse { + // Data contains bytes to returned from the contract + bytes data = 1; +} + +// MsgMigrateContract runs a code upgrade/ downgrade for a smart contract +message MsgMigrateContract { + // Sender is the that actor that signed the messages + string sender = 1; + // Contract is the address of the smart contract + string contract = 2; + // CodeID references the new WASM code + uint64 code_id = 3 [ (gogoproto.customname) = "CodeID" ]; + // Msg json encoded message to be passed to the contract on migration + bytes msg = 4 [ (gogoproto.casttype) = "RawContractMessage" ]; +} + +// MsgMigrateContractResponse returns contract migration result data. +message MsgMigrateContractResponse { + // Data contains same raw bytes returned as data from the wasm contract. + // (May be empty) + bytes data = 1; +} + +// MsgUpdateAdmin sets a new admin for a smart contract +message MsgUpdateAdmin { + // Sender is the that actor that signed the messages + string sender = 1; + // NewAdmin address to be set + string new_admin = 2; + // Contract is the address of the smart contract + string contract = 3; +} + +// MsgUpdateAdminResponse returns empty data +message MsgUpdateAdminResponse {} + +// MsgClearAdmin removes any admin stored for a smart contract +message MsgClearAdmin { + // Sender is the actor that signed the messages + string sender = 1; + // Contract is the address of the smart contract + string contract = 3; +} + +// MsgClearAdminResponse returns empty data +message MsgClearAdminResponse {} + +// MsgUpdateInstantiateConfig updates instantiate config for a smart contract +message MsgUpdateInstantiateConfig { + // Sender is the that actor that signed the messages + string sender = 1; + // CodeID references the stored WASM code + uint64 code_id = 2 [ (gogoproto.customname) = "CodeID" ]; + // NewInstantiatePermission is the new access control + AccessConfig new_instantiate_permission = 3; +} + +// MsgUpdateInstantiateConfigResponse returns empty data +message MsgUpdateInstantiateConfigResponse {} \ No newline at end of file diff --git a/ampd/proto/third_party/cosmwasm/wasm/v1/types.proto b/ampd/proto/third_party/cosmwasm/wasm/v1/types.proto new file mode 100644 index 000000000..b68179e2e --- /dev/null +++ b/ampd/proto/third_party/cosmwasm/wasm/v1/types.proto @@ -0,0 +1,145 @@ +syntax = "proto3"; +package cosmwasm.wasm.v1; + +import "cosmos_proto/cosmos.proto"; +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; + +option go_package = "github.com/CosmWasm/wasmd/x/wasm/types"; +option (gogoproto.goproto_getters_all) = false; +option (gogoproto.equal_all) = true; + +// AccessType permission types +enum AccessType { + option (gogoproto.goproto_enum_prefix) = false; + option (gogoproto.goproto_enum_stringer) = false; + // AccessTypeUnspecified placeholder for empty value + ACCESS_TYPE_UNSPECIFIED = 0 + [ (gogoproto.enumvalue_customname) = "AccessTypeUnspecified" ]; + // AccessTypeNobody forbidden + ACCESS_TYPE_NOBODY = 1 + [ (gogoproto.enumvalue_customname) = "AccessTypeNobody" ]; + // AccessTypeOnlyAddress restricted to a single address + // Deprecated: use AccessTypeAnyOfAddresses instead + ACCESS_TYPE_ONLY_ADDRESS = 2 + [ (gogoproto.enumvalue_customname) = "AccessTypeOnlyAddress" ]; + // AccessTypeEverybody unrestricted + ACCESS_TYPE_EVERYBODY = 3 + [ (gogoproto.enumvalue_customname) = "AccessTypeEverybody" ]; + // AccessTypeAnyOfAddresses allow any of the addresses + ACCESS_TYPE_ANY_OF_ADDRESSES = 4 + [ (gogoproto.enumvalue_customname) = "AccessTypeAnyOfAddresses" ]; +} + +// AccessTypeParam +message AccessTypeParam { + option (gogoproto.goproto_stringer) = true; + AccessType value = 1 [ (gogoproto.moretags) = "yaml:\"value\"" ]; +} + +// AccessConfig access control type. +message AccessConfig { + option (gogoproto.goproto_stringer) = true; + AccessType permission = 1 [ (gogoproto.moretags) = "yaml:\"permission\"" ]; + + // Address + // Deprecated: replaced by addresses + string address = 2 [ (gogoproto.moretags) = "yaml:\"address\"" ]; + repeated string addresses = 3 [ (gogoproto.moretags) = "yaml:\"addresses\"" ]; +} + +// Params defines the set of wasm parameters. +message Params { + option (gogoproto.goproto_stringer) = false; + AccessConfig code_upload_access = 1 [ + (gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"code_upload_access\"" + ]; + AccessType instantiate_default_permission = 2 + [ (gogoproto.moretags) = "yaml:\"instantiate_default_permission\"" ]; +} + +// CodeInfo is data for the uploaded contract WASM code +message CodeInfo { + // CodeHash is the unique identifier created by wasmvm + bytes code_hash = 1; + // Creator address who initially stored the code + string creator = 2; + // Used in v1beta1 + reserved 3, 4; + // InstantiateConfig access control to apply on contract creation, optional + AccessConfig instantiate_config = 5 [ (gogoproto.nullable) = false ]; +} + +// ContractInfo stores a WASM contract instance +message ContractInfo { + option (gogoproto.equal) = true; + + // CodeID is the reference to the stored Wasm code + uint64 code_id = 1 [ (gogoproto.customname) = "CodeID" ]; + // Creator address who initially instantiated the contract + string creator = 2; + // Admin is an optional address that can execute migrations + string admin = 3; + // Label is optional metadata to be stored with a contract instance. + string label = 4; + // Created Tx position when the contract was instantiated. + AbsoluteTxPosition created = 5; + string ibc_port_id = 6 [ (gogoproto.customname) = "IBCPortID" ]; + + // Extension is an extension point to store custom metadata within the + // persistence model. + google.protobuf.Any extension = 7 + [ (cosmos_proto.accepts_interface) = + "cosmwasm.wasm.v1.ContractInfoExtension" ]; +} + +// ContractCodeHistoryOperationType actions that caused a code change +enum ContractCodeHistoryOperationType { + option (gogoproto.goproto_enum_prefix) = false; + // ContractCodeHistoryOperationTypeUnspecified placeholder for empty value + CONTRACT_CODE_HISTORY_OPERATION_TYPE_UNSPECIFIED = 0 + [ (gogoproto.enumvalue_customname) = + "ContractCodeHistoryOperationTypeUnspecified" ]; + // ContractCodeHistoryOperationTypeInit on chain contract instantiation + CONTRACT_CODE_HISTORY_OPERATION_TYPE_INIT = 1 + [ (gogoproto.enumvalue_customname) = + "ContractCodeHistoryOperationTypeInit" ]; + // ContractCodeHistoryOperationTypeMigrate code migration + CONTRACT_CODE_HISTORY_OPERATION_TYPE_MIGRATE = 2 + [ (gogoproto.enumvalue_customname) = + "ContractCodeHistoryOperationTypeMigrate" ]; + // ContractCodeHistoryOperationTypeGenesis based on genesis data + CONTRACT_CODE_HISTORY_OPERATION_TYPE_GENESIS = 3 + [ (gogoproto.enumvalue_customname) = + "ContractCodeHistoryOperationTypeGenesis" ]; +} + +// ContractCodeHistoryEntry metadata to a contract. +message ContractCodeHistoryEntry { + ContractCodeHistoryOperationType operation = 1; + // CodeID is the reference to the stored WASM code + uint64 code_id = 2 [ (gogoproto.customname) = "CodeID" ]; + // Updated Tx position when the operation was executed. + AbsoluteTxPosition updated = 3; + bytes msg = 4 [ (gogoproto.casttype) = "RawContractMessage" ]; +} + +// AbsoluteTxPosition is a unique transaction position that allows for global +// ordering of transactions. +message AbsoluteTxPosition { + // BlockHeight is the block the contract was created at + uint64 block_height = 1; + // TxIndex is a monotonic counter within the block (actual transaction index, + // or gas consumed) + uint64 tx_index = 2; +} + +// Model is a struct that holds a KV pair +message Model { + // hex-encode key to read it better (this is often ascii) + bytes key = 1 [ (gogoproto.casttype) = + "github.com/tendermint/tendermint/libs/bytes.HexBytes" ]; + // base64-encode raw value + bytes value = 2; +} diff --git a/ampd/proto/third_party/gogoproto/gogo.proto b/ampd/proto/third_party/gogoproto/gogo.proto new file mode 100644 index 000000000..49e78f99f --- /dev/null +++ b/ampd/proto/third_party/gogoproto/gogo.proto @@ -0,0 +1,145 @@ +// Protocol Buffers for Go with Gadgets +// +// Copyright (c) 2013, The GoGo Authors. All rights reserved. +// http://github.com/gogo/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto2"; +package gogoproto; + +import "google/protobuf/descriptor.proto"; + +option java_package = "com.google.protobuf"; +option java_outer_classname = "GoGoProtos"; +option go_package = "github.com/gogo/protobuf/gogoproto"; + +extend google.protobuf.EnumOptions { + optional bool goproto_enum_prefix = 62001; + optional bool goproto_enum_stringer = 62021; + optional bool enum_stringer = 62022; + optional string enum_customname = 62023; + optional bool enumdecl = 62024; +} + +extend google.protobuf.EnumValueOptions { + optional string enumvalue_customname = 66001; +} + +extend google.protobuf.FileOptions { + optional bool goproto_getters_all = 63001; + optional bool goproto_enum_prefix_all = 63002; + optional bool goproto_stringer_all = 63003; + optional bool verbose_equal_all = 63004; + optional bool face_all = 63005; + optional bool gostring_all = 63006; + optional bool populate_all = 63007; + optional bool stringer_all = 63008; + optional bool onlyone_all = 63009; + + optional bool equal_all = 63013; + optional bool description_all = 63014; + optional bool testgen_all = 63015; + optional bool benchgen_all = 63016; + optional bool marshaler_all = 63017; + optional bool unmarshaler_all = 63018; + optional bool stable_marshaler_all = 63019; + + optional bool sizer_all = 63020; + + optional bool goproto_enum_stringer_all = 63021; + optional bool enum_stringer_all = 63022; + + optional bool unsafe_marshaler_all = 63023; + optional bool unsafe_unmarshaler_all = 63024; + + optional bool goproto_extensions_map_all = 63025; + optional bool goproto_unrecognized_all = 63026; + optional bool gogoproto_import = 63027; + optional bool protosizer_all = 63028; + optional bool compare_all = 63029; + optional bool typedecl_all = 63030; + optional bool enumdecl_all = 63031; + + optional bool goproto_registration = 63032; + optional bool messagename_all = 63033; + + optional bool goproto_sizecache_all = 63034; + optional bool goproto_unkeyed_all = 63035; +} + +extend google.protobuf.MessageOptions { + optional bool goproto_getters = 64001; + optional bool goproto_stringer = 64003; + optional bool verbose_equal = 64004; + optional bool face = 64005; + optional bool gostring = 64006; + optional bool populate = 64007; + optional bool stringer = 67008; + optional bool onlyone = 64009; + + optional bool equal = 64013; + optional bool description = 64014; + optional bool testgen = 64015; + optional bool benchgen = 64016; + optional bool marshaler = 64017; + optional bool unmarshaler = 64018; + optional bool stable_marshaler = 64019; + + optional bool sizer = 64020; + + optional bool unsafe_marshaler = 64023; + optional bool unsafe_unmarshaler = 64024; + + optional bool goproto_extensions_map = 64025; + optional bool goproto_unrecognized = 64026; + + optional bool protosizer = 64028; + optional bool compare = 64029; + + optional bool typedecl = 64030; + + optional bool messagename = 64033; + + optional bool goproto_sizecache = 64034; + optional bool goproto_unkeyed = 64035; +} + +extend google.protobuf.FieldOptions { + optional bool nullable = 65001; + optional bool embed = 65002; + optional string customtype = 65003; + optional string customname = 65004; + optional string jsontag = 65005; + optional string moretags = 65006; + optional string casttype = 65007; + optional string castkey = 65008; + optional string castvalue = 65009; + + optional bool stdtime = 65010; + optional bool stdduration = 65011; + optional bool wktpointer = 65012; + + optional string castrepeated = 65013; +} diff --git a/ampd/proto/third_party/google/api/annotations.proto b/ampd/proto/third_party/google/api/annotations.proto new file mode 100644 index 000000000..efdab3db6 --- /dev/null +++ b/ampd/proto/third_party/google/api/annotations.proto @@ -0,0 +1,31 @@ +// Copyright 2015 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/api/http.proto"; +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "AnnotationsProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // See `HttpRule`. + HttpRule http = 72295728; +} diff --git a/ampd/proto/third_party/google/api/http.proto b/ampd/proto/third_party/google/api/http.proto new file mode 100644 index 000000000..31d867a27 --- /dev/null +++ b/ampd/proto/third_party/google/api/http.proto @@ -0,0 +1,379 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "HttpProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Defines the HTTP configuration for an API service. It contains a list of +// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method +// to one or more HTTP REST API methods. +message Http { + // A list of HTTP configuration rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated HttpRule rules = 1; + + // When set to true, URL path parameters will be fully URI-decoded except in + // cases of single segment matches in reserved expansion, where "%2F" will be + // left encoded. + // + // The default behavior is to not decode RFC 6570 reserved characters in multi + // segment matches. + bool fully_decode_reserved_expansion = 2; +} + +// # gRPC Transcoding +// +// gRPC Transcoding is a feature for mapping between a gRPC method and one or +// more HTTP REST endpoints. It allows developers to build a single API service +// that supports both gRPC APIs and REST APIs. Many systems, including [Google +// APIs](https://github.com/googleapis/googleapis), +// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC +// Gateway](https://github.com/grpc-ecosystem/grpc-gateway), +// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature +// and use it for large scale production services. +// +// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies +// how different portions of the gRPC request message are mapped to the URL +// path, URL query parameters, and HTTP request body. It also controls how the +// gRPC response message is mapped to the HTTP response body. `HttpRule` is +// typically specified as an `google.api.http` annotation on the gRPC method. +// +// Each mapping specifies a URL path template and an HTTP method. The path +// template may refer to one or more fields in the gRPC request message, as long +// as each field is a non-repeated field with a primitive (non-message) type. +// The path template controls how fields of the request message are mapped to +// the URL path. +// +// Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/{name=messages/*}" +// }; +// } +// } +// message GetMessageRequest { +// string name = 1; // Mapped to URL path. +// } +// message Message { +// string text = 1; // The resource content. +// } +// +// This enables an HTTP REST to gRPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(name: "messages/123456")` +// +// Any fields in the request message which are not bound by the path template +// automatically become HTTP query parameters if there is no HTTP request body. +// For example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get:"/v1/messages/{message_id}" +// }; +// } +// } +// message GetMessageRequest { +// message SubMessage { +// string subfield = 1; +// } +// string message_id = 1; // Mapped to URL path. +// int64 revision = 2; // Mapped to URL query parameter `revision`. +// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`. +// } +// +// This enables a HTTP JSON to RPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | +// `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: +// "foo"))` +// +// Note that fields which are mapped to URL query parameters must have a +// primitive type or a repeated primitive type or a non-repeated message type. +// In the case of a repeated type, the parameter can be repeated in the URL +// as `...?param=A¶m=B`. In the case of a message type, each field of the +// message is mapped to a separate parameter, such as +// `...?foo.a=A&foo.b=B&foo.c=C`. +// +// For HTTP methods that allow a request body, the `body` field +// specifies the mapping. Consider a REST update method on the +// message resource collection: +// +// service Messaging { +// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "message" +// }; +// } +// } +// message UpdateMessageRequest { +// string message_id = 1; // mapped to the URL +// Message message = 2; // mapped to the body +// } +// +// The following HTTP JSON to RPC mapping is enabled, where the +// representation of the JSON in the request body is determined by +// protos JSON encoding: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" message { text: "Hi!" })` +// +// The special name `*` can be used in the body mapping to define that +// every field not bound by the path template should be mapped to the +// request body. This enables the following alternative definition of +// the update method: +// +// service Messaging { +// rpc UpdateMessage(Message) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "*" +// }; +// } +// } +// message Message { +// string message_id = 1; +// string text = 2; +// } +// +// +// The following HTTP JSON to RPC mapping is enabled: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" text: "Hi!")` +// +// Note that when using `*` in the body mapping, it is not possible to +// have HTTP parameters, as all fields not bound by the path end in +// the body. This makes this option more rarely used in practice when +// defining REST APIs. The common usage of `*` is in custom methods +// which don't use the URL at all for transferring data. +// +// It is possible to define multiple HTTP methods for one RPC by using +// the `additional_bindings` option. Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/messages/{message_id}" +// additional_bindings { +// get: "/v1/users/{user_id}/messages/{message_id}" +// } +// }; +// } +// } +// message GetMessageRequest { +// string message_id = 1; +// string user_id = 2; +// } +// +// This enables the following two alternative HTTP JSON to RPC mappings: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")` +// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: +// "123456")` +// +// ## Rules for HTTP mapping +// +// 1. Leaf request fields (recursive expansion nested messages in the request +// message) are classified into three categories: +// - Fields referred by the path template. They are passed via the URL path. +// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They +// are passed via the HTTP +// request body. +// - All other fields are passed via the URL query parameters, and the +// parameter name is the field path in the request message. A repeated +// field can be represented as multiple query parameters under the same +// name. +// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL +// query parameter, all fields +// are passed via URL path and HTTP request body. +// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP +// request body, all +// fields are passed via URL path and URL query parameters. +// +// ### Path template syntax +// +// Template = "/" Segments [ Verb ] ; +// Segments = Segment { "/" Segment } ; +// Segment = "*" | "**" | LITERAL | Variable ; +// Variable = "{" FieldPath [ "=" Segments ] "}" ; +// FieldPath = IDENT { "." IDENT } ; +// Verb = ":" LITERAL ; +// +// The syntax `*` matches a single URL path segment. The syntax `**` matches +// zero or more URL path segments, which must be the last part of the URL path +// except the `Verb`. +// +// The syntax `Variable` matches part of the URL path as specified by its +// template. A variable template must not contain other variables. If a variable +// matches a single path segment, its template may be omitted, e.g. `{var}` +// is equivalent to `{var=*}`. +// +// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL` +// contains any reserved character, such characters should be percent-encoded +// before the matching. +// +// If a variable contains exactly one path segment, such as `"{var}"` or +// `"{var=*}"`, when such a variable is expanded into a URL path on the client +// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The +// server side does the reverse decoding. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{var}`. +// +// If a variable contains multiple path segments, such as `"{var=foo/*}"` +// or `"{var=**}"`, when such a variable is expanded into a URL path on the +// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. +// The server side does the reverse decoding, except "%2F" and "%2f" are left +// unchanged. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{+var}`. +// +// ## Using gRPC API Service Configuration +// +// gRPC API Service Configuration (service config) is a configuration language +// for configuring a gRPC service to become a user-facing product. The +// service config is simply the YAML representation of the `google.api.Service` +// proto message. +// +// As an alternative to annotating your proto file, you can configure gRPC +// transcoding in your service config YAML files. You do this by specifying a +// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same +// effect as the proto annotation. This can be particularly useful if you +// have a proto that is reused in multiple services. Note that any transcoding +// specified in the service config will override any matching transcoding +// configuration in the proto. +// +// Example: +// +// http: +// rules: +// # Selects a gRPC method and applies HttpRule to it. +// - selector: example.v1.Messaging.GetMessage +// get: /v1/messages/{message_id}/{sub.subfield} +// +// ## Special notes +// +// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the +// proto to JSON conversion must follow the [proto3 +// specification](https://developers.google.com/protocol-buffers/docs/proto3#json). +// +// While the single segment variable follows the semantics of +// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String +// Expansion, the multi segment variable **does not** follow RFC 6570 Section +// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion +// does not expand special characters like `?` and `#`, which would lead +// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding +// for multi segment variables. +// +// The path variables **must not** refer to any repeated or mapped field, +// because client libraries are not capable of handling such variable expansion. +// +// The path variables **must not** capture the leading "/" character. The reason +// is that the most common use case "{var}" does not capture the leading "/" +// character. For consistency, all path variables must share the same behavior. +// +// Repeated message fields must not be mapped to URL query parameters, because +// no client library can support such complicated mapping. +// +// If an API needs to use a JSON array for request or response body, it can map +// the request or response body to a repeated field. However, some gRPC +// Transcoding implementations may not support this feature. +message HttpRule { + // Selects a method to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax + // details. + string selector = 1; + + // Determines the URL pattern is matched by this rules. This pattern can be + // used with any of the {get|put|post|delete|patch} methods. A custom method + // can be defined using the 'custom' field. + oneof pattern { + // Maps to HTTP GET. Used for listing and getting information about + // resources. + string get = 2; + + // Maps to HTTP PUT. Used for replacing a resource. + string put = 3; + + // Maps to HTTP POST. Used for creating a resource or performing an action. + string post = 4; + + // Maps to HTTP DELETE. Used for deleting a resource. + string delete = 5; + + // Maps to HTTP PATCH. Used for updating a resource. + string patch = 6; + + // The custom pattern is used for specifying an HTTP method that is not + // included in the `pattern` field, such as HEAD, or "*" to leave the + // HTTP method unspecified for this rule. The wild-card rule is useful + // for services that provide content to Web (HTML) clients. + CustomHttpPattern custom = 8; + } + + // The name of the request field whose value is mapped to the HTTP request + // body, or `*` for mapping all request fields not captured by the path + // pattern to the HTTP body, or omitted for not having any HTTP request body. + // + // NOTE: the referred field must be present at the top-level of the request + // message type. + string body = 7; + + // Optional. The name of the response field whose value is mapped to the HTTP + // response body. When omitted, the entire response message will be used + // as the HTTP response body. + // + // NOTE: The referred field must be present at the top-level of the response + // message type. + string response_body = 12; + + // Additional HTTP bindings for the selector. Nested bindings must + // not contain an `additional_bindings` field themselves (that is, + // the nesting may only be one level deep). + repeated HttpRule additional_bindings = 11; +} + +// A custom pattern is used for defining custom HTTP verb. +message CustomHttpPattern { + // The name of this custom HTTP verb. + string kind = 1; + + // The path matched by this custom verb. + string path = 2; +} diff --git a/ampd/proto/third_party/ibc/applications/transfer/v1/genesis.proto b/ampd/proto/third_party/ibc/applications/transfer/v1/genesis.proto new file mode 100644 index 000000000..34672fdeb --- /dev/null +++ b/ampd/proto/third_party/ibc/applications/transfer/v1/genesis.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package ibc.applications.transfer.v1; + +option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"; + +import "ibc/applications/transfer/v1/transfer.proto"; +import "gogoproto/gogo.proto"; + +// GenesisState defines the ibc-transfer genesis state +message GenesisState { + string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; + repeated DenomTrace denom_traces = 2 [ + (gogoproto.castrepeated) = "Traces", + (gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"denom_traces\"" + ]; + Params params = 3 [(gogoproto.nullable) = false]; +} diff --git a/ampd/proto/third_party/ibc/applications/transfer/v1/query.proto b/ampd/proto/third_party/ibc/applications/transfer/v1/query.proto new file mode 100644 index 000000000..52f2f2400 --- /dev/null +++ b/ampd/proto/third_party/ibc/applications/transfer/v1/query.proto @@ -0,0 +1,105 @@ +syntax = "proto3"; + +package ibc.applications.transfer.v1; + +import "gogoproto/gogo.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; +import "ibc/applications/transfer/v1/transfer.proto"; +import "google/api/annotations.proto"; + +option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"; + +// Query provides defines the gRPC querier service. +service Query { + // DenomTraces queries all denomination traces. + rpc DenomTraces(QueryDenomTracesRequest) returns (QueryDenomTracesResponse) { + option (google.api.http).get = "/ibc/apps/transfer/v1/denom_traces"; + } + + // DenomTrace queries a denomination trace information. + rpc DenomTrace(QueryDenomTraceRequest) returns (QueryDenomTraceResponse) { + option (google.api.http).get = "/ibc/apps/transfer/v1/denom_traces/{hash=**}"; + } + + // Params queries all parameters of the ibc-transfer module. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/ibc/apps/transfer/v1/params"; + } + + // DenomHash queries a denomination hash information. + rpc DenomHash(QueryDenomHashRequest) returns (QueryDenomHashResponse) { + option (google.api.http).get = "/ibc/apps/transfer/v1/denom_hashes/{trace=**}"; + } + + // EscrowAddress returns the escrow address for a particular port and channel id. + rpc EscrowAddress(QueryEscrowAddressRequest) returns (QueryEscrowAddressResponse) { + option (google.api.http).get = "/ibc/apps/transfer/v1/channels/{channel_id}/ports/{port_id}/escrow_address"; + } +} + +// QueryDenomTraceRequest is the request type for the Query/DenomTrace RPC +// method +message QueryDenomTraceRequest { + // hash (in hex format) or denom (full denom with ibc prefix) of the denomination trace information. + string hash = 1; +} + +// QueryDenomTraceResponse is the response type for the Query/DenomTrace RPC +// method. +message QueryDenomTraceResponse { + // denom_trace returns the requested denomination trace information. + DenomTrace denom_trace = 1; +} + +// QueryConnectionsRequest is the request type for the Query/DenomTraces RPC +// method +message QueryDenomTracesRequest { + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +// QueryConnectionsResponse is the response type for the Query/DenomTraces RPC +// method. +message QueryDenomTracesResponse { + // denom_traces returns all denominations trace information. + repeated DenomTrace denom_traces = 1 [(gogoproto.castrepeated) = "Traces", (gogoproto.nullable) = false]; + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryParamsRequest is the request type for the Query/Params RPC method. +message QueryParamsRequest {} + +// QueryParamsResponse is the response type for the Query/Params RPC method. +message QueryParamsResponse { + // params defines the parameters of the module. + Params params = 1; +} + +// QueryDenomHashRequest is the request type for the Query/DenomHash RPC +// method +message QueryDenomHashRequest { + // The denomination trace ([port_id]/[channel_id])+/[denom] + string trace = 1; +} + +// QueryDenomHashResponse is the response type for the Query/DenomHash RPC +// method. +message QueryDenomHashResponse { + // hash (in hex format) of the denomination trace information. + string hash = 1; +} + +// QueryEscrowAddressRequest is the request type for the EscrowAddress RPC method. +message QueryEscrowAddressRequest { + // unique port identifier + string port_id = 1; + // unique channel identifier + string channel_id = 2; +} + +// QueryEscrowAddressResponse is the response type of the EscrowAddress RPC method. +message QueryEscrowAddressResponse { + // the escrow account address + string escrow_address = 1; +} \ No newline at end of file diff --git a/ampd/proto/third_party/ibc/applications/transfer/v1/transfer.proto b/ampd/proto/third_party/ibc/applications/transfer/v1/transfer.proto new file mode 100644 index 000000000..1f92e81a6 --- /dev/null +++ b/ampd/proto/third_party/ibc/applications/transfer/v1/transfer.proto @@ -0,0 +1,30 @@ +syntax = "proto3"; + +package ibc.applications.transfer.v1; + +option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"; + +import "gogoproto/gogo.proto"; + +// DenomTrace contains the base denomination for ICS20 fungible tokens and the +// source tracing information path. +message DenomTrace { + // path defines the chain of port/channel identifiers used for tracing the + // source of the fungible token. + string path = 1; + // base denomination of the relayed fungible token. + string base_denom = 2; +} + +// Params defines the set of IBC transfer parameters. +// NOTE: To prevent a single token from being transferred, set the +// TransfersEnabled parameter to true and then set the bank module's SendEnabled +// parameter for the denomination to false. +message Params { + // send_enabled enables or disables all cross-chain token transfers from this + // chain. + bool send_enabled = 1 [(gogoproto.moretags) = "yaml:\"send_enabled\""]; + // receive_enabled enables or disables all cross-chain token transfers to this + // chain. + bool receive_enabled = 2 [(gogoproto.moretags) = "yaml:\"receive_enabled\""]; +} diff --git a/ampd/proto/third_party/ibc/applications/transfer/v1/tx.proto b/ampd/proto/third_party/ibc/applications/transfer/v1/tx.proto new file mode 100644 index 000000000..44e068d69 --- /dev/null +++ b/ampd/proto/third_party/ibc/applications/transfer/v1/tx.proto @@ -0,0 +1,49 @@ +syntax = "proto3"; + +package ibc.applications.transfer.v1; + +option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "ibc/core/client/v1/client.proto"; + +// Msg defines the ibc/transfer Msg service. +service Msg { + // Transfer defines a rpc handler method for MsgTransfer. + rpc Transfer(MsgTransfer) returns (MsgTransferResponse); +} + +// MsgTransfer defines a msg to transfer fungible tokens (i.e Coins) between +// ICS20 enabled chains. See ICS Spec here: +// https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transfer#data-structures +message MsgTransfer { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // the port on which the packet will be sent + string source_port = 1 [(gogoproto.moretags) = "yaml:\"source_port\""]; + // the channel by which the packet will be sent + string source_channel = 2 [(gogoproto.moretags) = "yaml:\"source_channel\""]; + // the tokens to be transferred + cosmos.base.v1beta1.Coin token = 3 [(gogoproto.nullable) = false]; + // the sender address + string sender = 4; + // the recipient address on the destination chain + string receiver = 5; + // Timeout height relative to the current block height. + // The timeout is disabled when set to 0. + ibc.core.client.v1.Height timeout_height = 6 + [(gogoproto.moretags) = "yaml:\"timeout_height\"", (gogoproto.nullable) = false]; + // Timeout timestamp in absolute nanoseconds since unix epoch. + // The timeout is disabled when set to 0. + uint64 timeout_timestamp = 7 [(gogoproto.moretags) = "yaml:\"timeout_timestamp\""]; + // optional memo + string memo = 8; +} + +// MsgTransferResponse defines the Msg/Transfer response type. +message MsgTransferResponse { + // sequence number of the transfer packet sent + uint64 sequence = 1; +} diff --git a/ampd/proto/third_party/ibc/applications/transfer/v2/packet.proto b/ampd/proto/third_party/ibc/applications/transfer/v2/packet.proto new file mode 100644 index 000000000..129815ebc --- /dev/null +++ b/ampd/proto/third_party/ibc/applications/transfer/v2/packet.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +package ibc.applications.transfer.v2; + +option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"; + +// FungibleTokenPacketData defines a struct for the packet payload +// See FungibleTokenPacketData spec: +// https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transfer#data-structures +message FungibleTokenPacketData { + // the token denomination to be transferred + string denom = 1; + // the token amount to be transferred + string amount = 2; + // the sender address + string sender = 3; + // the recipient address on the destination chain + string receiver = 4; + // optional memo + string memo = 5; +} diff --git a/ampd/proto/third_party/ibc/core/channel/v1/channel.proto b/ampd/proto/third_party/ibc/core/channel/v1/channel.proto new file mode 100644 index 000000000..646884d57 --- /dev/null +++ b/ampd/proto/third_party/ibc/core/channel/v1/channel.proto @@ -0,0 +1,162 @@ +syntax = "proto3"; + +package ibc.core.channel.v1; + +option go_package = "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"; + +import "gogoproto/gogo.proto"; +import "ibc/core/client/v1/client.proto"; + +// Channel defines pipeline for exactly-once packet delivery between specific +// modules on separate blockchains, which has at least one end capable of +// sending packets and one end capable of receiving packets. +message Channel { + option (gogoproto.goproto_getters) = false; + + // current state of the channel end + State state = 1; + // whether the channel is ordered or unordered + Order ordering = 2; + // counterparty channel end + Counterparty counterparty = 3 [(gogoproto.nullable) = false]; + // list of connection identifiers, in order, along which packets sent on + // this channel will travel + repeated string connection_hops = 4 [(gogoproto.moretags) = "yaml:\"connection_hops\""]; + // opaque channel version, which is agreed upon during the handshake + string version = 5; +} + +// IdentifiedChannel defines a channel with additional port and channel +// identifier fields. +message IdentifiedChannel { + option (gogoproto.goproto_getters) = false; + + // current state of the channel end + State state = 1; + // whether the channel is ordered or unordered + Order ordering = 2; + // counterparty channel end + Counterparty counterparty = 3 [(gogoproto.nullable) = false]; + // list of connection identifiers, in order, along which packets sent on + // this channel will travel + repeated string connection_hops = 4 [(gogoproto.moretags) = "yaml:\"connection_hops\""]; + // opaque channel version, which is agreed upon during the handshake + string version = 5; + // port identifier + string port_id = 6; + // channel identifier + string channel_id = 7; +} + +// State defines if a channel is in one of the following states: +// CLOSED, INIT, TRYOPEN, OPEN or UNINITIALIZED. +enum State { + option (gogoproto.goproto_enum_prefix) = false; + + // Default State + STATE_UNINITIALIZED_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "UNINITIALIZED"]; + // A channel has just started the opening handshake. + STATE_INIT = 1 [(gogoproto.enumvalue_customname) = "INIT"]; + // A channel has acknowledged the handshake step on the counterparty chain. + STATE_TRYOPEN = 2 [(gogoproto.enumvalue_customname) = "TRYOPEN"]; + // A channel has completed the handshake. Open channels are + // ready to send and receive packets. + STATE_OPEN = 3 [(gogoproto.enumvalue_customname) = "OPEN"]; + // A channel has been closed and can no longer be used to send or receive + // packets. + STATE_CLOSED = 4 [(gogoproto.enumvalue_customname) = "CLOSED"]; +} + +// Order defines if a channel is ORDERED or UNORDERED +enum Order { + option (gogoproto.goproto_enum_prefix) = false; + + // zero-value for channel ordering + ORDER_NONE_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "NONE"]; + // packets can be delivered in any order, which may differ from the order in + // which they were sent. + ORDER_UNORDERED = 1 [(gogoproto.enumvalue_customname) = "UNORDERED"]; + // packets are delivered exactly in the order which they were sent + ORDER_ORDERED = 2 [(gogoproto.enumvalue_customname) = "ORDERED"]; +} + +// Counterparty defines a channel end counterparty +message Counterparty { + option (gogoproto.goproto_getters) = false; + + // port on the counterparty chain which owns the other end of the channel. + string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; + // channel end on the counterparty chain + string channel_id = 2 [(gogoproto.moretags) = "yaml:\"channel_id\""]; +} + +// Packet defines a type that carries data across different chains through IBC +message Packet { + option (gogoproto.goproto_getters) = false; + + // number corresponds to the order of sends and receives, where a Packet + // with an earlier sequence number must be sent and received before a Packet + // with a later sequence number. + uint64 sequence = 1; + // identifies the port on the sending chain. + string source_port = 2 [(gogoproto.moretags) = "yaml:\"source_port\""]; + // identifies the channel end on the sending chain. + string source_channel = 3 [(gogoproto.moretags) = "yaml:\"source_channel\""]; + // identifies the port on the receiving chain. + string destination_port = 4 [(gogoproto.moretags) = "yaml:\"destination_port\""]; + // identifies the channel end on the receiving chain. + string destination_channel = 5 [(gogoproto.moretags) = "yaml:\"destination_channel\""]; + // actual opaque bytes transferred directly to the application module + bytes data = 6; + // block height after which the packet times out + ibc.core.client.v1.Height timeout_height = 7 + [(gogoproto.moretags) = "yaml:\"timeout_height\"", (gogoproto.nullable) = false]; + // block timestamp (in nanoseconds) after which the packet times out + uint64 timeout_timestamp = 8 [(gogoproto.moretags) = "yaml:\"timeout_timestamp\""]; +} + +// PacketState defines the generic type necessary to retrieve and store +// packet commitments, acknowledgements, and receipts. +// Caller is responsible for knowing the context necessary to interpret this +// state as a commitment, acknowledgement, or a receipt. +message PacketState { + option (gogoproto.goproto_getters) = false; + + // channel port identifier. + string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; + // channel unique identifier. + string channel_id = 2 [(gogoproto.moretags) = "yaml:\"channel_id\""]; + // packet sequence. + uint64 sequence = 3; + // embedded data that represents packet state. + bytes data = 4; +} + +// PacketId is an identifer for a unique Packet +// Source chains refer to packets by source port/channel +// Destination chains refer to packets by destination port/channel +message PacketId { + option (gogoproto.goproto_getters) = false; + + // channel port identifier + string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; + // channel unique identifier + string channel_id = 2 [(gogoproto.moretags) = "yaml:\"channel_id\""]; + // packet sequence + uint64 sequence = 3; +} + +// Acknowledgement is the recommended acknowledgement format to be used by +// app-specific protocols. +// NOTE: The field numbers 21 and 22 were explicitly chosen to avoid accidental +// conflicts with other protobuf message formats used for acknowledgements. +// The first byte of any message with this format will be the non-ASCII values +// `0xaa` (result) or `0xb2` (error). Implemented as defined by ICS: +// https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#acknowledgement-envelope +message Acknowledgement { + // response contains either a result or an error and must be non-empty + oneof response { + bytes result = 21; + string error = 22; + } +} diff --git a/ampd/proto/third_party/ibc/core/channel/v1/genesis.proto b/ampd/proto/third_party/ibc/core/channel/v1/genesis.proto new file mode 100644 index 000000000..1c0ff6ee8 --- /dev/null +++ b/ampd/proto/third_party/ibc/core/channel/v1/genesis.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; + +package ibc.core.channel.v1; + +option go_package = "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"; + +import "gogoproto/gogo.proto"; +import "ibc/core/channel/v1/channel.proto"; + +// GenesisState defines the ibc channel submodule's genesis state. +message GenesisState { + repeated IdentifiedChannel channels = 1 [(gogoproto.casttype) = "IdentifiedChannel", (gogoproto.nullable) = false]; + repeated PacketState acknowledgements = 2 [(gogoproto.nullable) = false]; + repeated PacketState commitments = 3 [(gogoproto.nullable) = false]; + repeated PacketState receipts = 4 [(gogoproto.nullable) = false]; + repeated PacketSequence send_sequences = 5 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"send_sequences\""]; + repeated PacketSequence recv_sequences = 6 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"recv_sequences\""]; + repeated PacketSequence ack_sequences = 7 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"ack_sequences\""]; + // the sequence for the next generated channel identifier + uint64 next_channel_sequence = 8 [(gogoproto.moretags) = "yaml:\"next_channel_sequence\""]; +} + +// PacketSequence defines the genesis type necessary to retrieve and store +// next send and receive sequences. +message PacketSequence { + string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; + string channel_id = 2 [(gogoproto.moretags) = "yaml:\"channel_id\""]; + uint64 sequence = 3; +} diff --git a/ampd/proto/third_party/ibc/core/channel/v1/query.proto b/ampd/proto/third_party/ibc/core/channel/v1/query.proto new file mode 100644 index 000000000..986633173 --- /dev/null +++ b/ampd/proto/third_party/ibc/core/channel/v1/query.proto @@ -0,0 +1,376 @@ +syntax = "proto3"; + +package ibc.core.channel.v1; + +option go_package = "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"; + +import "ibc/core/client/v1/client.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; +import "ibc/core/channel/v1/channel.proto"; +import "google/api/annotations.proto"; +import "google/protobuf/any.proto"; +import "gogoproto/gogo.proto"; + +// Query provides defines the gRPC querier service +service Query { + // Channel queries an IBC Channel. + rpc Channel(QueryChannelRequest) returns (QueryChannelResponse) { + option (google.api.http).get = "/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}"; + } + + // Channels queries all the IBC channels of a chain. + rpc Channels(QueryChannelsRequest) returns (QueryChannelsResponse) { + option (google.api.http).get = "/ibc/core/channel/v1/channels"; + } + + // ConnectionChannels queries all the channels associated with a connection + // end. + rpc ConnectionChannels(QueryConnectionChannelsRequest) returns (QueryConnectionChannelsResponse) { + option (google.api.http).get = "/ibc/core/channel/v1/connections/{connection}/channels"; + } + + // ChannelClientState queries for the client state for the channel associated + // with the provided channel identifiers. + rpc ChannelClientState(QueryChannelClientStateRequest) returns (QueryChannelClientStateResponse) { + option (google.api.http).get = "/ibc/core/channel/v1/channels/{channel_id}/" + "ports/{port_id}/client_state"; + } + + // ChannelConsensusState queries for the consensus state for the channel + // associated with the provided channel identifiers. + rpc ChannelConsensusState(QueryChannelConsensusStateRequest) returns (QueryChannelConsensusStateResponse) { + option (google.api.http).get = "/ibc/core/channel/v1/channels/{channel_id}/" + "ports/{port_id}/consensus_state/revision/" + "{revision_number}/height/{revision_height}"; + } + + // PacketCommitment queries a stored packet commitment hash. + rpc PacketCommitment(QueryPacketCommitmentRequest) returns (QueryPacketCommitmentResponse) { + option (google.api.http).get = "/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/" + "packet_commitments/{sequence}"; + } + + // PacketCommitments returns all the packet commitments hashes associated + // with a channel. + rpc PacketCommitments(QueryPacketCommitmentsRequest) returns (QueryPacketCommitmentsResponse) { + option (google.api.http).get = "/ibc/core/channel/v1/channels/{channel_id}/" + "ports/{port_id}/packet_commitments"; + } + + // PacketReceipt queries if a given packet sequence has been received on the + // queried chain + rpc PacketReceipt(QueryPacketReceiptRequest) returns (QueryPacketReceiptResponse) { + option (google.api.http).get = "/ibc/core/channel/v1/channels/{channel_id}/" + "ports/{port_id}/packet_receipts/{sequence}"; + } + + // PacketAcknowledgement queries a stored packet acknowledgement hash. + rpc PacketAcknowledgement(QueryPacketAcknowledgementRequest) returns (QueryPacketAcknowledgementResponse) { + option (google.api.http).get = "/ibc/core/channel/v1/channels/{channel_id}/" + "ports/{port_id}/packet_acks/{sequence}"; + } + + // PacketAcknowledgements returns all the packet acknowledgements associated + // with a channel. + rpc PacketAcknowledgements(QueryPacketAcknowledgementsRequest) returns (QueryPacketAcknowledgementsResponse) { + option (google.api.http).get = "/ibc/core/channel/v1/channels/{channel_id}/" + "ports/{port_id}/packet_acknowledgements"; + } + + // UnreceivedPackets returns all the unreceived IBC packets associated with a + // channel and sequences. + rpc UnreceivedPackets(QueryUnreceivedPacketsRequest) returns (QueryUnreceivedPacketsResponse) { + option (google.api.http).get = "/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/" + "packet_commitments/" + "{packet_commitment_sequences}/unreceived_packets"; + } + + // UnreceivedAcks returns all the unreceived IBC acknowledgements associated + // with a channel and sequences. + rpc UnreceivedAcks(QueryUnreceivedAcksRequest) returns (QueryUnreceivedAcksResponse) { + option (google.api.http).get = "/ibc/core/channel/v1/channels/{channel_id}/" + "ports/{port_id}/packet_commitments/" + "{packet_ack_sequences}/unreceived_acks"; + } + + // NextSequenceReceive returns the next receive sequence for a given channel. + rpc NextSequenceReceive(QueryNextSequenceReceiveRequest) returns (QueryNextSequenceReceiveResponse) { + option (google.api.http).get = "/ibc/core/channel/v1/channels/{channel_id}/" + "ports/{port_id}/next_sequence"; + } +} + +// QueryChannelRequest is the request type for the Query/Channel RPC method +message QueryChannelRequest { + // port unique identifier + string port_id = 1; + // channel unique identifier + string channel_id = 2; +} + +// QueryChannelResponse is the response type for the Query/Channel RPC method. +// Besides the Channel end, it includes a proof and the height from which the +// proof was retrieved. +message QueryChannelResponse { + // channel associated with the request identifiers + ibc.core.channel.v1.Channel channel = 1; + // merkle proof of existence + bytes proof = 2; + // height at which the proof was retrieved + ibc.core.client.v1.Height proof_height = 3 [(gogoproto.nullable) = false]; +} + +// QueryChannelsRequest is the request type for the Query/Channels RPC method +message QueryChannelsRequest { + // pagination request + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +// QueryChannelsResponse is the response type for the Query/Channels RPC method. +message QueryChannelsResponse { + // list of stored channels of the chain. + repeated ibc.core.channel.v1.IdentifiedChannel channels = 1; + // pagination response + cosmos.base.query.v1beta1.PageResponse pagination = 2; + // query block height + ibc.core.client.v1.Height height = 3 [(gogoproto.nullable) = false]; +} + +// QueryConnectionChannelsRequest is the request type for the +// Query/QueryConnectionChannels RPC method +message QueryConnectionChannelsRequest { + // connection unique identifier + string connection = 1; + // pagination request + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryConnectionChannelsResponse is the Response type for the +// Query/QueryConnectionChannels RPC method +message QueryConnectionChannelsResponse { + // list of channels associated with a connection. + repeated ibc.core.channel.v1.IdentifiedChannel channels = 1; + // pagination response + cosmos.base.query.v1beta1.PageResponse pagination = 2; + // query block height + ibc.core.client.v1.Height height = 3 [(gogoproto.nullable) = false]; +} + +// QueryChannelClientStateRequest is the request type for the Query/ClientState +// RPC method +message QueryChannelClientStateRequest { + // port unique identifier + string port_id = 1; + // channel unique identifier + string channel_id = 2; +} + +// QueryChannelClientStateResponse is the Response type for the +// Query/QueryChannelClientState RPC method +message QueryChannelClientStateResponse { + // client state associated with the channel + ibc.core.client.v1.IdentifiedClientState identified_client_state = 1; + // merkle proof of existence + bytes proof = 2; + // height at which the proof was retrieved + ibc.core.client.v1.Height proof_height = 3 [(gogoproto.nullable) = false]; +} + +// QueryChannelConsensusStateRequest is the request type for the +// Query/ConsensusState RPC method +message QueryChannelConsensusStateRequest { + // port unique identifier + string port_id = 1; + // channel unique identifier + string channel_id = 2; + // revision number of the consensus state + uint64 revision_number = 3; + // revision height of the consensus state + uint64 revision_height = 4; +} + +// QueryChannelClientStateResponse is the Response type for the +// Query/QueryChannelClientState RPC method +message QueryChannelConsensusStateResponse { + // consensus state associated with the channel + google.protobuf.Any consensus_state = 1; + // client ID associated with the consensus state + string client_id = 2; + // merkle proof of existence + bytes proof = 3; + // height at which the proof was retrieved + ibc.core.client.v1.Height proof_height = 4 [(gogoproto.nullable) = false]; +} + +// QueryPacketCommitmentRequest is the request type for the +// Query/PacketCommitment RPC method +message QueryPacketCommitmentRequest { + // port unique identifier + string port_id = 1; + // channel unique identifier + string channel_id = 2; + // packet sequence + uint64 sequence = 3; +} + +// QueryPacketCommitmentResponse defines the client query response for a packet +// which also includes a proof and the height from which the proof was +// retrieved +message QueryPacketCommitmentResponse { + // packet associated with the request fields + bytes commitment = 1; + // merkle proof of existence + bytes proof = 2; + // height at which the proof was retrieved + ibc.core.client.v1.Height proof_height = 3 [(gogoproto.nullable) = false]; +} + +// QueryPacketCommitmentsRequest is the request type for the +// Query/QueryPacketCommitments RPC method +message QueryPacketCommitmentsRequest { + // port unique identifier + string port_id = 1; + // channel unique identifier + string channel_id = 2; + // pagination request + cosmos.base.query.v1beta1.PageRequest pagination = 3; +} + +// QueryPacketCommitmentsResponse is the request type for the +// Query/QueryPacketCommitments RPC method +message QueryPacketCommitmentsResponse { + repeated ibc.core.channel.v1.PacketState commitments = 1; + // pagination response + cosmos.base.query.v1beta1.PageResponse pagination = 2; + // query block height + ibc.core.client.v1.Height height = 3 [(gogoproto.nullable) = false]; +} + +// QueryPacketReceiptRequest is the request type for the +// Query/PacketReceipt RPC method +message QueryPacketReceiptRequest { + // port unique identifier + string port_id = 1; + // channel unique identifier + string channel_id = 2; + // packet sequence + uint64 sequence = 3; +} + +// QueryPacketReceiptResponse defines the client query response for a packet +// receipt which also includes a proof, and the height from which the proof was +// retrieved +message QueryPacketReceiptResponse { + // success flag for if receipt exists + bool received = 2; + // merkle proof of existence + bytes proof = 3; + // height at which the proof was retrieved + ibc.core.client.v1.Height proof_height = 4 [(gogoproto.nullable) = false]; +} + +// QueryPacketAcknowledgementRequest is the request type for the +// Query/PacketAcknowledgement RPC method +message QueryPacketAcknowledgementRequest { + // port unique identifier + string port_id = 1; + // channel unique identifier + string channel_id = 2; + // packet sequence + uint64 sequence = 3; +} + +// QueryPacketAcknowledgementResponse defines the client query response for a +// packet which also includes a proof and the height from which the +// proof was retrieved +message QueryPacketAcknowledgementResponse { + // packet associated with the request fields + bytes acknowledgement = 1; + // merkle proof of existence + bytes proof = 2; + // height at which the proof was retrieved + ibc.core.client.v1.Height proof_height = 3 [(gogoproto.nullable) = false]; +} + +// QueryPacketAcknowledgementsRequest is the request type for the +// Query/QueryPacketCommitments RPC method +message QueryPacketAcknowledgementsRequest { + // port unique identifier + string port_id = 1; + // channel unique identifier + string channel_id = 2; + // pagination request + cosmos.base.query.v1beta1.PageRequest pagination = 3; + // list of packet sequences + repeated uint64 packet_commitment_sequences = 4; +} + +// QueryPacketAcknowledgemetsResponse is the request type for the +// Query/QueryPacketAcknowledgements RPC method +message QueryPacketAcknowledgementsResponse { + repeated ibc.core.channel.v1.PacketState acknowledgements = 1; + // pagination response + cosmos.base.query.v1beta1.PageResponse pagination = 2; + // query block height + ibc.core.client.v1.Height height = 3 [(gogoproto.nullable) = false]; +} + +// QueryUnreceivedPacketsRequest is the request type for the +// Query/UnreceivedPackets RPC method +message QueryUnreceivedPacketsRequest { + // port unique identifier + string port_id = 1; + // channel unique identifier + string channel_id = 2; + // list of packet sequences + repeated uint64 packet_commitment_sequences = 3; +} + +// QueryUnreceivedPacketsResponse is the response type for the +// Query/UnreceivedPacketCommitments RPC method +message QueryUnreceivedPacketsResponse { + // list of unreceived packet sequences + repeated uint64 sequences = 1; + // query block height + ibc.core.client.v1.Height height = 2 [(gogoproto.nullable) = false]; +} + +// QueryUnreceivedAcks is the request type for the +// Query/UnreceivedAcks RPC method +message QueryUnreceivedAcksRequest { + // port unique identifier + string port_id = 1; + // channel unique identifier + string channel_id = 2; + // list of acknowledgement sequences + repeated uint64 packet_ack_sequences = 3; +} + +// QueryUnreceivedAcksResponse is the response type for the +// Query/UnreceivedAcks RPC method +message QueryUnreceivedAcksResponse { + // list of unreceived acknowledgement sequences + repeated uint64 sequences = 1; + // query block height + ibc.core.client.v1.Height height = 2 [(gogoproto.nullable) = false]; +} + +// QueryNextSequenceReceiveRequest is the request type for the +// Query/QueryNextSequenceReceiveRequest RPC method +message QueryNextSequenceReceiveRequest { + // port unique identifier + string port_id = 1; + // channel unique identifier + string channel_id = 2; +} + +// QuerySequenceResponse is the request type for the +// Query/QueryNextSequenceReceiveResponse RPC method +message QueryNextSequenceReceiveResponse { + // next sequence receive number + uint64 next_sequence_receive = 1; + // merkle proof of existence + bytes proof = 2; + // height at which the proof was retrieved + ibc.core.client.v1.Height proof_height = 3 [(gogoproto.nullable) = false]; +} diff --git a/ampd/proto/third_party/ibc/core/channel/v1/tx.proto b/ampd/proto/third_party/ibc/core/channel/v1/tx.proto new file mode 100644 index 000000000..75248aeb5 --- /dev/null +++ b/ampd/proto/third_party/ibc/core/channel/v1/tx.proto @@ -0,0 +1,245 @@ +syntax = "proto3"; + +package ibc.core.channel.v1; + +option go_package = "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"; + +import "gogoproto/gogo.proto"; +import "ibc/core/client/v1/client.proto"; +import "ibc/core/channel/v1/channel.proto"; + +// Msg defines the ibc/channel Msg service. +service Msg { + // ChannelOpenInit defines a rpc handler method for MsgChannelOpenInit. + rpc ChannelOpenInit(MsgChannelOpenInit) returns (MsgChannelOpenInitResponse); + + // ChannelOpenTry defines a rpc handler method for MsgChannelOpenTry. + rpc ChannelOpenTry(MsgChannelOpenTry) returns (MsgChannelOpenTryResponse); + + // ChannelOpenAck defines a rpc handler method for MsgChannelOpenAck. + rpc ChannelOpenAck(MsgChannelOpenAck) returns (MsgChannelOpenAckResponse); + + // ChannelOpenConfirm defines a rpc handler method for MsgChannelOpenConfirm. + rpc ChannelOpenConfirm(MsgChannelOpenConfirm) returns (MsgChannelOpenConfirmResponse); + + // ChannelCloseInit defines a rpc handler method for MsgChannelCloseInit. + rpc ChannelCloseInit(MsgChannelCloseInit) returns (MsgChannelCloseInitResponse); + + // ChannelCloseConfirm defines a rpc handler method for + // MsgChannelCloseConfirm. + rpc ChannelCloseConfirm(MsgChannelCloseConfirm) returns (MsgChannelCloseConfirmResponse); + + // RecvPacket defines a rpc handler method for MsgRecvPacket. + rpc RecvPacket(MsgRecvPacket) returns (MsgRecvPacketResponse); + + // Timeout defines a rpc handler method for MsgTimeout. + rpc Timeout(MsgTimeout) returns (MsgTimeoutResponse); + + // TimeoutOnClose defines a rpc handler method for MsgTimeoutOnClose. + rpc TimeoutOnClose(MsgTimeoutOnClose) returns (MsgTimeoutOnCloseResponse); + + // Acknowledgement defines a rpc handler method for MsgAcknowledgement. + rpc Acknowledgement(MsgAcknowledgement) returns (MsgAcknowledgementResponse); +} + +// ResponseResultType defines the possible outcomes of the execution of a message +enum ResponseResultType { + option (gogoproto.goproto_enum_prefix) = false; + + // Default zero value enumeration + RESPONSE_RESULT_TYPE_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "UNSPECIFIED"]; + // The message did not call the IBC application callbacks (because, for example, the packet had already been relayed) + RESPONSE_RESULT_TYPE_NOOP = 1 [(gogoproto.enumvalue_customname) = "NOOP"]; + // The message was executed successfully + RESPONSE_RESULT_TYPE_SUCCESS = 2 [(gogoproto.enumvalue_customname) = "SUCCESS"]; +} + +// MsgChannelOpenInit defines an sdk.Msg to initialize a channel handshake. It +// is called by a relayer on Chain A. +message MsgChannelOpenInit { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; + Channel channel = 2 [(gogoproto.nullable) = false]; + string signer = 3; +} + +// MsgChannelOpenInitResponse defines the Msg/ChannelOpenInit response type. +message MsgChannelOpenInitResponse { + string channel_id = 1 [(gogoproto.moretags) = "yaml:\"channel_id\""]; + string version = 2; +} + +// MsgChannelOpenInit defines a msg sent by a Relayer to try to open a channel +// on Chain B. The version field within the Channel field has been deprecated. Its +// value will be ignored by core IBC. +message MsgChannelOpenTry { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; + // Deprecated: this field is unused. Crossing hello's are no longer supported in core IBC. + string previous_channel_id = 2 [deprecated = true, (gogoproto.moretags) = "yaml:\"previous_channel_id\""]; + // NOTE: the version field within the channel has been deprecated. Its value will be ignored by core IBC. + Channel channel = 3 [(gogoproto.nullable) = false]; + string counterparty_version = 4 [(gogoproto.moretags) = "yaml:\"counterparty_version\""]; + bytes proof_init = 5 [(gogoproto.moretags) = "yaml:\"proof_init\""]; + ibc.core.client.v1.Height proof_height = 6 + [(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false]; + string signer = 7; +} + +// MsgChannelOpenTryResponse defines the Msg/ChannelOpenTry response type. +message MsgChannelOpenTryResponse { + string version = 1; +} + +// MsgChannelOpenAck defines a msg sent by a Relayer to Chain A to acknowledge +// the change of channel state to TRYOPEN on Chain B. +message MsgChannelOpenAck { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; + string channel_id = 2 [(gogoproto.moretags) = "yaml:\"channel_id\""]; + string counterparty_channel_id = 3 [(gogoproto.moretags) = "yaml:\"counterparty_channel_id\""]; + string counterparty_version = 4 [(gogoproto.moretags) = "yaml:\"counterparty_version\""]; + bytes proof_try = 5 [(gogoproto.moretags) = "yaml:\"proof_try\""]; + ibc.core.client.v1.Height proof_height = 6 + [(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false]; + string signer = 7; +} + +// MsgChannelOpenAckResponse defines the Msg/ChannelOpenAck response type. +message MsgChannelOpenAckResponse {} + +// MsgChannelOpenConfirm defines a msg sent by a Relayer to Chain B to +// acknowledge the change of channel state to OPEN on Chain A. +message MsgChannelOpenConfirm { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; + string channel_id = 2 [(gogoproto.moretags) = "yaml:\"channel_id\""]; + bytes proof_ack = 3 [(gogoproto.moretags) = "yaml:\"proof_ack\""]; + ibc.core.client.v1.Height proof_height = 4 + [(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false]; + string signer = 5; +} + +// MsgChannelOpenConfirmResponse defines the Msg/ChannelOpenConfirm response +// type. +message MsgChannelOpenConfirmResponse {} + +// MsgChannelCloseInit defines a msg sent by a Relayer to Chain A +// to close a channel with Chain B. +message MsgChannelCloseInit { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; + string channel_id = 2 [(gogoproto.moretags) = "yaml:\"channel_id\""]; + string signer = 3; +} + +// MsgChannelCloseInitResponse defines the Msg/ChannelCloseInit response type. +message MsgChannelCloseInitResponse {} + +// MsgChannelCloseConfirm defines a msg sent by a Relayer to Chain B +// to acknowledge the change of channel state to CLOSED on Chain A. +message MsgChannelCloseConfirm { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; + string channel_id = 2 [(gogoproto.moretags) = "yaml:\"channel_id\""]; + bytes proof_init = 3 [(gogoproto.moretags) = "yaml:\"proof_init\""]; + ibc.core.client.v1.Height proof_height = 4 + [(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false]; + string signer = 5; +} + +// MsgChannelCloseConfirmResponse defines the Msg/ChannelCloseConfirm response +// type. +message MsgChannelCloseConfirmResponse {} + +// MsgRecvPacket receives incoming IBC packet +message MsgRecvPacket { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + Packet packet = 1 [(gogoproto.nullable) = false]; + bytes proof_commitment = 2 [(gogoproto.moretags) = "yaml:\"proof_commitment\""]; + ibc.core.client.v1.Height proof_height = 3 + [(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false]; + string signer = 4; +} + +// MsgRecvPacketResponse defines the Msg/RecvPacket response type. +message MsgRecvPacketResponse { + option (gogoproto.goproto_getters) = false; + + ResponseResultType result = 1; +} + +// MsgTimeout receives timed-out packet +message MsgTimeout { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + Packet packet = 1 [(gogoproto.nullable) = false]; + bytes proof_unreceived = 2 [(gogoproto.moretags) = "yaml:\"proof_unreceived\""]; + ibc.core.client.v1.Height proof_height = 3 + [(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false]; + uint64 next_sequence_recv = 4 [(gogoproto.moretags) = "yaml:\"next_sequence_recv\""]; + string signer = 5; +} + +// MsgTimeoutResponse defines the Msg/Timeout response type. +message MsgTimeoutResponse { + option (gogoproto.goproto_getters) = false; + + ResponseResultType result = 1; +} + +// MsgTimeoutOnClose timed-out packet upon counterparty channel closure. +message MsgTimeoutOnClose { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + Packet packet = 1 [(gogoproto.nullable) = false]; + bytes proof_unreceived = 2 [(gogoproto.moretags) = "yaml:\"proof_unreceived\""]; + bytes proof_close = 3 [(gogoproto.moretags) = "yaml:\"proof_close\""]; + ibc.core.client.v1.Height proof_height = 4 + [(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false]; + uint64 next_sequence_recv = 5 [(gogoproto.moretags) = "yaml:\"next_sequence_recv\""]; + string signer = 6; +} + +// MsgTimeoutOnCloseResponse defines the Msg/TimeoutOnClose response type. +message MsgTimeoutOnCloseResponse { + option (gogoproto.goproto_getters) = false; + + ResponseResultType result = 1; +} + +// MsgAcknowledgement receives incoming IBC acknowledgement +message MsgAcknowledgement { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + Packet packet = 1 [(gogoproto.nullable) = false]; + bytes acknowledgement = 2; + bytes proof_acked = 3 [(gogoproto.moretags) = "yaml:\"proof_acked\""]; + ibc.core.client.v1.Height proof_height = 4 + [(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false]; + string signer = 5; +} + +// MsgAcknowledgementResponse defines the Msg/Acknowledgement response type. +message MsgAcknowledgementResponse { + option (gogoproto.goproto_getters) = false; + + ResponseResultType result = 1; +} diff --git a/ampd/proto/third_party/ibc/core/client/v1/client.proto b/ampd/proto/third_party/ibc/core/client/v1/client.proto new file mode 100644 index 000000000..2ec41ed0c --- /dev/null +++ b/ampd/proto/third_party/ibc/core/client/v1/client.proto @@ -0,0 +1,103 @@ +syntax = "proto3"; + +package ibc.core.client.v1; + +option go_package = "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "cosmos/upgrade/v1beta1/upgrade.proto"; +import "cosmos_proto/cosmos.proto"; + +// IdentifiedClientState defines a client state with an additional client +// identifier field. +message IdentifiedClientState { + // client identifier + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + // client state + google.protobuf.Any client_state = 2 [(gogoproto.moretags) = "yaml:\"client_state\""]; +} + +// ConsensusStateWithHeight defines a consensus state with an additional height +// field. +message ConsensusStateWithHeight { + // consensus state height + Height height = 1 [(gogoproto.nullable) = false]; + // consensus state + google.protobuf.Any consensus_state = 2 [(gogoproto.moretags) = "yaml:\"consensus_state\""]; +} + +// ClientConsensusStates defines all the stored consensus states for a given +// client. +message ClientConsensusStates { + // client identifier + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + // consensus states and their heights associated with the client + repeated ConsensusStateWithHeight consensus_states = 2 + [(gogoproto.moretags) = "yaml:\"consensus_states\"", (gogoproto.nullable) = false]; +} + +// ClientUpdateProposal is a governance proposal. If it passes, the substitute +// client's latest consensus state is copied over to the subject client. The proposal +// handler may fail if the subject and the substitute do not match in client and +// chain parameters (with exception to latest height, frozen height, and chain-id). +message ClientUpdateProposal { + option (gogoproto.goproto_getters) = false; + option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content"; + // the title of the update proposal + string title = 1; + // the description of the proposal + string description = 2; + // the client identifier for the client to be updated if the proposal passes + string subject_client_id = 3 [(gogoproto.moretags) = "yaml:\"subject_client_id\""]; + // the substitute client identifier for the client standing in for the subject + // client + string substitute_client_id = 4 [(gogoproto.moretags) = "yaml:\"substitute_client_id\""]; +} + +// UpgradeProposal is a gov Content type for initiating an IBC breaking +// upgrade. +message UpgradeProposal { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + option (gogoproto.equal) = true; + option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content"; + + string title = 1; + string description = 2; + cosmos.upgrade.v1beta1.Plan plan = 3 [(gogoproto.nullable) = false]; + + // An UpgradedClientState must be provided to perform an IBC breaking upgrade. + // This will make the chain commit to the correct upgraded (self) client state + // before the upgrade occurs, so that connecting chains can verify that the + // new upgraded client is valid by verifying a proof on the previous version + // of the chain. This will allow IBC connections to persist smoothly across + // planned chain upgrades + google.protobuf.Any upgraded_client_state = 4 [(gogoproto.moretags) = "yaml:\"upgraded_client_state\""]; +} + +// Height is a monotonically increasing data type +// that can be compared against another Height for the purposes of updating and +// freezing clients +// +// Normally the RevisionHeight is incremented at each height while keeping +// RevisionNumber the same. However some consensus algorithms may choose to +// reset the height in certain conditions e.g. hard forks, state-machine +// breaking changes In these cases, the RevisionNumber is incremented so that +// height continues to be monitonically increasing even as the RevisionHeight +// gets reset +message Height { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + // the revision that the client is currently on + uint64 revision_number = 1 [(gogoproto.moretags) = "yaml:\"revision_number\""]; + // the height within the given revision + uint64 revision_height = 2 [(gogoproto.moretags) = "yaml:\"revision_height\""]; +} + +// Params defines the set of IBC light client parameters. +message Params { + // allowed_clients defines the list of allowed client state types. + repeated string allowed_clients = 1 [(gogoproto.moretags) = "yaml:\"allowed_clients\""]; +} diff --git a/ampd/proto/third_party/ibc/core/client/v1/genesis.proto b/ampd/proto/third_party/ibc/core/client/v1/genesis.proto new file mode 100644 index 000000000..b2930c484 --- /dev/null +++ b/ampd/proto/third_party/ibc/core/client/v1/genesis.proto @@ -0,0 +1,48 @@ +syntax = "proto3"; + +package ibc.core.client.v1; + +option go_package = "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"; + +import "ibc/core/client/v1/client.proto"; +import "gogoproto/gogo.proto"; + +// GenesisState defines the ibc client submodule's genesis state. +message GenesisState { + // client states with their corresponding identifiers + repeated IdentifiedClientState clients = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "IdentifiedClientStates"]; + // consensus states from each client + repeated ClientConsensusStates clients_consensus = 2 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "ClientsConsensusStates", + (gogoproto.moretags) = "yaml:\"clients_consensus\"" + ]; + // metadata from each client + repeated IdentifiedGenesisMetadata clients_metadata = 3 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"clients_metadata\""]; + Params params = 4 [(gogoproto.nullable) = false]; + // create localhost on initialization + bool create_localhost = 5 [(gogoproto.moretags) = "yaml:\"create_localhost\""]; + // the sequence for the next generated client identifier + uint64 next_client_sequence = 6 [(gogoproto.moretags) = "yaml:\"next_client_sequence\""]; +} + +// GenesisMetadata defines the genesis type for metadata that clients may return +// with ExportMetadata +message GenesisMetadata { + option (gogoproto.goproto_getters) = false; + + // store key of metadata without clientID-prefix + bytes key = 1; + // metadata value + bytes value = 2; +} + +// IdentifiedGenesisMetadata has the client metadata with the corresponding +// client id. +message IdentifiedGenesisMetadata { + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + repeated GenesisMetadata client_metadata = 2 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"client_metadata\""]; +} diff --git a/ampd/proto/third_party/ibc/core/client/v1/query.proto b/ampd/proto/third_party/ibc/core/client/v1/query.proto new file mode 100644 index 000000000..2c9618bc8 --- /dev/null +++ b/ampd/proto/third_party/ibc/core/client/v1/query.proto @@ -0,0 +1,207 @@ +syntax = "proto3"; + +package ibc.core.client.v1; + +option go_package = "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"; + +import "cosmos/base/query/v1beta1/pagination.proto"; +import "ibc/core/client/v1/client.proto"; +import "google/protobuf/any.proto"; +import "google/api/annotations.proto"; +import "gogoproto/gogo.proto"; + +// Query provides defines the gRPC querier service +service Query { + // ClientState queries an IBC light client. + rpc ClientState(QueryClientStateRequest) returns (QueryClientStateResponse) { + option (google.api.http).get = "/ibc/core/client/v1/client_states/{client_id}"; + } + + // ClientStates queries all the IBC light clients of a chain. + rpc ClientStates(QueryClientStatesRequest) returns (QueryClientStatesResponse) { + option (google.api.http).get = "/ibc/core/client/v1/client_states"; + } + + // ConsensusState queries a consensus state associated with a client state at + // a given height. + rpc ConsensusState(QueryConsensusStateRequest) returns (QueryConsensusStateResponse) { + option (google.api.http).get = "/ibc/core/client/v1/consensus_states/" + "{client_id}/revision/{revision_number}/" + "height/{revision_height}"; + } + + // ConsensusStates queries all the consensus state associated with a given + // client. + rpc ConsensusStates(QueryConsensusStatesRequest) returns (QueryConsensusStatesResponse) { + option (google.api.http).get = "/ibc/core/client/v1/consensus_states/{client_id}"; + } + + // ConsensusStateHeights queries the height of every consensus states associated with a given client. + rpc ConsensusStateHeights(QueryConsensusStateHeightsRequest) returns (QueryConsensusStateHeightsResponse) { + option (google.api.http).get = "/ibc/core/client/v1/consensus_states/{client_id}/heights"; + } + + // Status queries the status of an IBC client. + rpc ClientStatus(QueryClientStatusRequest) returns (QueryClientStatusResponse) { + option (google.api.http).get = "/ibc/core/client/v1/client_status/{client_id}"; + } + + // ClientParams queries all parameters of the ibc client. + rpc ClientParams(QueryClientParamsRequest) returns (QueryClientParamsResponse) { + option (google.api.http).get = "/ibc/client/v1/params"; + } + + // UpgradedClientState queries an Upgraded IBC light client. + rpc UpgradedClientState(QueryUpgradedClientStateRequest) returns (QueryUpgradedClientStateResponse) { + option (google.api.http).get = "/ibc/core/client/v1/upgraded_client_states"; + } + + // UpgradedConsensusState queries an Upgraded IBC consensus state. + rpc UpgradedConsensusState(QueryUpgradedConsensusStateRequest) returns (QueryUpgradedConsensusStateResponse) { + option (google.api.http).get = "/ibc/core/client/v1/upgraded_consensus_states"; + } +} + +// QueryClientStateRequest is the request type for the Query/ClientState RPC +// method +message QueryClientStateRequest { + // client state unique identifier + string client_id = 1; +} + +// QueryClientStateResponse is the response type for the Query/ClientState RPC +// method. Besides the client state, it includes a proof and the height from +// which the proof was retrieved. +message QueryClientStateResponse { + // client state associated with the request identifier + google.protobuf.Any client_state = 1; + // merkle proof of existence + bytes proof = 2; + // height at which the proof was retrieved + ibc.core.client.v1.Height proof_height = 3 [(gogoproto.nullable) = false]; +} + +// QueryClientStatesRequest is the request type for the Query/ClientStates RPC +// method +message QueryClientStatesRequest { + // pagination request + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +// QueryClientStatesResponse is the response type for the Query/ClientStates RPC +// method. +message QueryClientStatesResponse { + // list of stored ClientStates of the chain. + repeated IdentifiedClientState client_states = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "IdentifiedClientStates"]; + // pagination response + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryConsensusStateRequest is the request type for the Query/ConsensusState +// RPC method. Besides the consensus state, it includes a proof and the height +// from which the proof was retrieved. +message QueryConsensusStateRequest { + // client identifier + string client_id = 1; + // consensus state revision number + uint64 revision_number = 2; + // consensus state revision height + uint64 revision_height = 3; + // latest_height overrrides the height field and queries the latest stored + // ConsensusState + bool latest_height = 4; +} + +// QueryConsensusStateResponse is the response type for the Query/ConsensusState +// RPC method +message QueryConsensusStateResponse { + // consensus state associated with the client identifier at the given height + google.protobuf.Any consensus_state = 1; + // merkle proof of existence + bytes proof = 2; + // height at which the proof was retrieved + ibc.core.client.v1.Height proof_height = 3 [(gogoproto.nullable) = false]; +} + +// QueryConsensusStatesRequest is the request type for the Query/ConsensusStates +// RPC method. +message QueryConsensusStatesRequest { + // client identifier + string client_id = 1; + // pagination request + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryConsensusStatesResponse is the response type for the +// Query/ConsensusStates RPC method +message QueryConsensusStatesResponse { + // consensus states associated with the identifier + repeated ConsensusStateWithHeight consensus_states = 1 [(gogoproto.nullable) = false]; + // pagination response + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryConsensusStateHeightsRequest is the request type for Query/ConsensusStateHeights +// RPC method. +message QueryConsensusStateHeightsRequest { + // client identifier + string client_id = 1; + // pagination request + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryConsensusStateHeightsResponse is the response type for the +// Query/ConsensusStateHeights RPC method +message QueryConsensusStateHeightsResponse { + // consensus state heights + repeated Height consensus_state_heights = 1 [(gogoproto.nullable) = false]; + // pagination response + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryClientStatusRequest is the request type for the Query/ClientStatus RPC +// method +message QueryClientStatusRequest { + // client unique identifier + string client_id = 1; +} + +// QueryClientStatusResponse is the response type for the Query/ClientStatus RPC +// method. It returns the current status of the IBC client. +message QueryClientStatusResponse { + string status = 1; +} + +// QueryClientParamsRequest is the request type for the Query/ClientParams RPC +// method. +message QueryClientParamsRequest {} + +// QueryClientParamsResponse is the response type for the Query/ClientParams RPC +// method. +message QueryClientParamsResponse { + // params defines the parameters of the module. + Params params = 1; +} + +// QueryUpgradedClientStateRequest is the request type for the +// Query/UpgradedClientState RPC method +message QueryUpgradedClientStateRequest {} + +// QueryUpgradedClientStateResponse is the response type for the +// Query/UpgradedClientState RPC method. +message QueryUpgradedClientStateResponse { + // client state associated with the request identifier + google.protobuf.Any upgraded_client_state = 1; +} + +// QueryUpgradedConsensusStateRequest is the request type for the +// Query/UpgradedConsensusState RPC method +message QueryUpgradedConsensusStateRequest {} + +// QueryUpgradedConsensusStateResponse is the response type for the +// Query/UpgradedConsensusState RPC method. +message QueryUpgradedConsensusStateResponse { + // Consensus state associated with the request identifier + google.protobuf.Any upgraded_consensus_state = 1; +} diff --git a/ampd/proto/third_party/ibc/core/client/v1/tx.proto b/ampd/proto/third_party/ibc/core/client/v1/tx.proto new file mode 100644 index 000000000..11dfdadea --- /dev/null +++ b/ampd/proto/third_party/ibc/core/client/v1/tx.proto @@ -0,0 +1,99 @@ +syntax = "proto3"; + +package ibc.core.client.v1; + +option go_package = "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; + +// Msg defines the ibc/client Msg service. +service Msg { + // CreateClient defines a rpc handler method for MsgCreateClient. + rpc CreateClient(MsgCreateClient) returns (MsgCreateClientResponse); + + // UpdateClient defines a rpc handler method for MsgUpdateClient. + rpc UpdateClient(MsgUpdateClient) returns (MsgUpdateClientResponse); + + // UpgradeClient defines a rpc handler method for MsgUpgradeClient. + rpc UpgradeClient(MsgUpgradeClient) returns (MsgUpgradeClientResponse); + + // SubmitMisbehaviour defines a rpc handler method for MsgSubmitMisbehaviour. + rpc SubmitMisbehaviour(MsgSubmitMisbehaviour) returns (MsgSubmitMisbehaviourResponse); +} + +// MsgCreateClient defines a message to create an IBC client +message MsgCreateClient { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // light client state + google.protobuf.Any client_state = 1 [(gogoproto.moretags) = "yaml:\"client_state\""]; + // consensus state associated with the client that corresponds to a given + // height. + google.protobuf.Any consensus_state = 2 [(gogoproto.moretags) = "yaml:\"consensus_state\""]; + // signer address + string signer = 3; +} + +// MsgCreateClientResponse defines the Msg/CreateClient response type. +message MsgCreateClientResponse {} + +// MsgUpdateClient defines an sdk.Msg to update a IBC client state using +// the given header. +message MsgUpdateClient { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // client unique identifier + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + // header to update the light client + google.protobuf.Any header = 2; + // signer address + string signer = 3; +} + +// MsgUpdateClientResponse defines the Msg/UpdateClient response type. +message MsgUpdateClientResponse {} + +// MsgUpgradeClient defines an sdk.Msg to upgrade an IBC client to a new client +// state +message MsgUpgradeClient { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // client unique identifier + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + // upgraded client state + google.protobuf.Any client_state = 2 [(gogoproto.moretags) = "yaml:\"client_state\""]; + // upgraded consensus state, only contains enough information to serve as a + // basis of trust in update logic + google.protobuf.Any consensus_state = 3 [(gogoproto.moretags) = "yaml:\"consensus_state\""]; + // proof that old chain committed to new client + bytes proof_upgrade_client = 4 [(gogoproto.moretags) = "yaml:\"proof_upgrade_client\""]; + // proof that old chain committed to new consensus state + bytes proof_upgrade_consensus_state = 5 [(gogoproto.moretags) = "yaml:\"proof_upgrade_consensus_state\""]; + // signer address + string signer = 6; +} + +// MsgUpgradeClientResponse defines the Msg/UpgradeClient response type. +message MsgUpgradeClientResponse {} + +// MsgSubmitMisbehaviour defines an sdk.Msg type that submits Evidence for +// light client misbehaviour. +message MsgSubmitMisbehaviour { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // client unique identifier + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + // misbehaviour used for freezing the light client + google.protobuf.Any misbehaviour = 2; + // signer address + string signer = 3; +} + +// MsgSubmitMisbehaviourResponse defines the Msg/SubmitMisbehaviour response +// type. +message MsgSubmitMisbehaviourResponse {} diff --git a/ampd/proto/third_party/ibc/core/commitment/v1/commitment.proto b/ampd/proto/third_party/ibc/core/commitment/v1/commitment.proto new file mode 100644 index 000000000..b6a68a99f --- /dev/null +++ b/ampd/proto/third_party/ibc/core/commitment/v1/commitment.proto @@ -0,0 +1,41 @@ +syntax = "proto3"; + +package ibc.core.commitment.v1; + +option go_package = "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types"; + +import "gogoproto/gogo.proto"; +import "proofs.proto"; + +// MerkleRoot defines a merkle root hash. +// In the Cosmos SDK, the AppHash of a block header becomes the root. +message MerkleRoot { + option (gogoproto.goproto_getters) = false; + + bytes hash = 1; +} + +// MerklePrefix is merkle path prefixed to the key. +// The constructed key from the Path and the key will be append(Path.KeyPath, +// append(Path.KeyPrefix, key...)) +message MerklePrefix { + bytes key_prefix = 1 [(gogoproto.moretags) = "yaml:\"key_prefix\""]; +} + +// MerklePath is the path used to verify commitment proofs, which can be an +// arbitrary structured object (defined by a commitment type). +// MerklePath is represented from root-to-leaf +message MerklePath { + option (gogoproto.goproto_stringer) = false; + + repeated string key_path = 1 [(gogoproto.moretags) = "yaml:\"key_path\""]; +} + +// MerkleProof is a wrapper type over a chain of CommitmentProofs. +// It demonstrates membership or non-membership for an element or set of +// elements, verifiable in conjunction with a known commitment root. Proofs +// should be succinct. +// MerkleProofs are ordered from leaf-to-root +message MerkleProof { + repeated ics23.CommitmentProof proofs = 1; +} diff --git a/ampd/proto/third_party/ibc/core/connection/v1/connection.proto b/ampd/proto/third_party/ibc/core/connection/v1/connection.proto new file mode 100644 index 000000000..8360af988 --- /dev/null +++ b/ampd/proto/third_party/ibc/core/connection/v1/connection.proto @@ -0,0 +1,114 @@ +syntax = "proto3"; + +package ibc.core.connection.v1; + +option go_package = "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types"; + +import "gogoproto/gogo.proto"; +import "ibc/core/commitment/v1/commitment.proto"; + +// ICS03 - Connection Data Structures as defined in +// https://github.com/cosmos/ibc/blob/master/spec/core/ics-003-connection-semantics#data-structures + +// ConnectionEnd defines a stateful object on a chain connected to another +// separate one. +// NOTE: there must only be 2 defined ConnectionEnds to establish +// a connection between two chains. +message ConnectionEnd { + option (gogoproto.goproto_getters) = false; + // client associated with this connection. + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + // IBC version which can be utilised to determine encodings or protocols for + // channels or packets utilising this connection. + repeated Version versions = 2; + // current state of the connection end. + State state = 3; + // counterparty chain associated with this connection. + Counterparty counterparty = 4 [(gogoproto.nullable) = false]; + // delay period that must pass before a consensus state can be used for + // packet-verification NOTE: delay period logic is only implemented by some + // clients. + uint64 delay_period = 5 [(gogoproto.moretags) = "yaml:\"delay_period\""]; +} + +// IdentifiedConnection defines a connection with additional connection +// identifier field. +message IdentifiedConnection { + option (gogoproto.goproto_getters) = false; + // connection identifier. + string id = 1 [(gogoproto.moretags) = "yaml:\"id\""]; + // client associated with this connection. + string client_id = 2 [(gogoproto.moretags) = "yaml:\"client_id\""]; + // IBC version which can be utilised to determine encodings or protocols for + // channels or packets utilising this connection + repeated Version versions = 3; + // current state of the connection end. + State state = 4; + // counterparty chain associated with this connection. + Counterparty counterparty = 5 [(gogoproto.nullable) = false]; + // delay period associated with this connection. + uint64 delay_period = 6 [(gogoproto.moretags) = "yaml:\"delay_period\""]; +} + +// State defines if a connection is in one of the following states: +// INIT, TRYOPEN, OPEN or UNINITIALIZED. +enum State { + option (gogoproto.goproto_enum_prefix) = false; + + // Default State + STATE_UNINITIALIZED_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "UNINITIALIZED"]; + // A connection end has just started the opening handshake. + STATE_INIT = 1 [(gogoproto.enumvalue_customname) = "INIT"]; + // A connection end has acknowledged the handshake step on the counterparty + // chain. + STATE_TRYOPEN = 2 [(gogoproto.enumvalue_customname) = "TRYOPEN"]; + // A connection end has completed the handshake. + STATE_OPEN = 3 [(gogoproto.enumvalue_customname) = "OPEN"]; +} + +// Counterparty defines the counterparty chain associated with a connection end. +message Counterparty { + option (gogoproto.goproto_getters) = false; + + // identifies the client on the counterparty chain associated with a given + // connection. + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + // identifies the connection end on the counterparty chain associated with a + // given connection. + string connection_id = 2 [(gogoproto.moretags) = "yaml:\"connection_id\""]; + // commitment merkle prefix of the counterparty chain. + ibc.core.commitment.v1.MerklePrefix prefix = 3 [(gogoproto.nullable) = false]; +} + +// ClientPaths define all the connection paths for a client state. +message ClientPaths { + // list of connection paths + repeated string paths = 1; +} + +// ConnectionPaths define all the connection paths for a given client state. +message ConnectionPaths { + // client state unique identifier + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + // list of connection paths + repeated string paths = 2; +} + +// Version defines the versioning scheme used to negotiate the IBC verison in +// the connection handshake. +message Version { + option (gogoproto.goproto_getters) = false; + + // unique version identifier + string identifier = 1; + // list of features compatible with the specified identifier + repeated string features = 2; +} + +// Params defines the set of Connection parameters. +message Params { + // maximum expected time per block (in nanoseconds), used to enforce block delay. This parameter should reflect the + // largest amount of time that the chain might reasonably take to produce the next block under normal operating + // conditions. A safe choice is 3-5x the expected time per block. + uint64 max_expected_time_per_block = 1 [(gogoproto.moretags) = "yaml:\"max_expected_time_per_block\""]; +} diff --git a/ampd/proto/third_party/ibc/core/connection/v1/genesis.proto b/ampd/proto/third_party/ibc/core/connection/v1/genesis.proto new file mode 100644 index 000000000..f616ae67e --- /dev/null +++ b/ampd/proto/third_party/ibc/core/connection/v1/genesis.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package ibc.core.connection.v1; + +option go_package = "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types"; + +import "gogoproto/gogo.proto"; +import "ibc/core/connection/v1/connection.proto"; + +// GenesisState defines the ibc connection submodule's genesis state. +message GenesisState { + repeated IdentifiedConnection connections = 1 [(gogoproto.nullable) = false]; + repeated ConnectionPaths client_connection_paths = 2 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"client_connection_paths\""]; + // the sequence for the next generated connection identifier + uint64 next_connection_sequence = 3 [(gogoproto.moretags) = "yaml:\"next_connection_sequence\""]; + Params params = 4 [(gogoproto.nullable) = false]; +} diff --git a/ampd/proto/third_party/ibc/core/connection/v1/query.proto b/ampd/proto/third_party/ibc/core/connection/v1/query.proto new file mode 100644 index 000000000..129f30a71 --- /dev/null +++ b/ampd/proto/third_party/ibc/core/connection/v1/query.proto @@ -0,0 +1,138 @@ +syntax = "proto3"; + +package ibc.core.connection.v1; + +option go_package = "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types"; + +import "gogoproto/gogo.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; +import "ibc/core/client/v1/client.proto"; +import "ibc/core/connection/v1/connection.proto"; +import "google/api/annotations.proto"; +import "google/protobuf/any.proto"; + +// Query provides defines the gRPC querier service +service Query { + // Connection queries an IBC connection end. + rpc Connection(QueryConnectionRequest) returns (QueryConnectionResponse) { + option (google.api.http).get = "/ibc/core/connection/v1/connections/{connection_id}"; + } + + // Connections queries all the IBC connections of a chain. + rpc Connections(QueryConnectionsRequest) returns (QueryConnectionsResponse) { + option (google.api.http).get = "/ibc/core/connection/v1/connections"; + } + + // ClientConnections queries the connection paths associated with a client + // state. + rpc ClientConnections(QueryClientConnectionsRequest) returns (QueryClientConnectionsResponse) { + option (google.api.http).get = "/ibc/core/connection/v1/client_connections/{client_id}"; + } + + // ConnectionClientState queries the client state associated with the + // connection. + rpc ConnectionClientState(QueryConnectionClientStateRequest) returns (QueryConnectionClientStateResponse) { + option (google.api.http).get = "/ibc/core/connection/v1/connections/{connection_id}/client_state"; + } + + // ConnectionConsensusState queries the consensus state associated with the + // connection. + rpc ConnectionConsensusState(QueryConnectionConsensusStateRequest) returns (QueryConnectionConsensusStateResponse) { + option (google.api.http).get = "/ibc/core/connection/v1/connections/{connection_id}/consensus_state/" + "revision/{revision_number}/height/{revision_height}"; + } +} + +// QueryConnectionRequest is the request type for the Query/Connection RPC +// method +message QueryConnectionRequest { + // connection unique identifier + string connection_id = 1; +} + +// QueryConnectionResponse is the response type for the Query/Connection RPC +// method. Besides the connection end, it includes a proof and the height from +// which the proof was retrieved. +message QueryConnectionResponse { + // connection associated with the request identifier + ibc.core.connection.v1.ConnectionEnd connection = 1; + // merkle proof of existence + bytes proof = 2; + // height at which the proof was retrieved + ibc.core.client.v1.Height proof_height = 3 [(gogoproto.nullable) = false]; +} + +// QueryConnectionsRequest is the request type for the Query/Connections RPC +// method +message QueryConnectionsRequest { + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +// QueryConnectionsResponse is the response type for the Query/Connections RPC +// method. +message QueryConnectionsResponse { + // list of stored connections of the chain. + repeated ibc.core.connection.v1.IdentifiedConnection connections = 1; + // pagination response + cosmos.base.query.v1beta1.PageResponse pagination = 2; + // query block height + ibc.core.client.v1.Height height = 3 [(gogoproto.nullable) = false]; +} + +// QueryClientConnectionsRequest is the request type for the +// Query/ClientConnections RPC method +message QueryClientConnectionsRequest { + // client identifier associated with a connection + string client_id = 1; +} + +// QueryClientConnectionsResponse is the response type for the +// Query/ClientConnections RPC method +message QueryClientConnectionsResponse { + // slice of all the connection paths associated with a client. + repeated string connection_paths = 1; + // merkle proof of existence + bytes proof = 2; + // height at which the proof was generated + ibc.core.client.v1.Height proof_height = 3 [(gogoproto.nullable) = false]; +} + +// QueryConnectionClientStateRequest is the request type for the +// Query/ConnectionClientState RPC method +message QueryConnectionClientStateRequest { + // connection identifier + string connection_id = 1 [(gogoproto.moretags) = "yaml:\"connection_id\""]; +} + +// QueryConnectionClientStateResponse is the response type for the +// Query/ConnectionClientState RPC method +message QueryConnectionClientStateResponse { + // client state associated with the channel + ibc.core.client.v1.IdentifiedClientState identified_client_state = 1; + // merkle proof of existence + bytes proof = 2; + // height at which the proof was retrieved + ibc.core.client.v1.Height proof_height = 3 [(gogoproto.nullable) = false]; +} + +// QueryConnectionConsensusStateRequest is the request type for the +// Query/ConnectionConsensusState RPC method +message QueryConnectionConsensusStateRequest { + // connection identifier + string connection_id = 1 [(gogoproto.moretags) = "yaml:\"connection_id\""]; + uint64 revision_number = 2; + uint64 revision_height = 3; +} + +// QueryConnectionConsensusStateResponse is the response type for the +// Query/ConnectionConsensusState RPC method +message QueryConnectionConsensusStateResponse { + // consensus state associated with the channel + google.protobuf.Any consensus_state = 1; + // client ID associated with the consensus state + string client_id = 2; + // merkle proof of existence + bytes proof = 3; + // height at which the proof was retrieved + ibc.core.client.v1.Height proof_height = 4 [(gogoproto.nullable) = false]; +} diff --git a/ampd/proto/third_party/ibc/core/connection/v1/tx.proto b/ampd/proto/third_party/ibc/core/connection/v1/tx.proto new file mode 100644 index 000000000..b2fea632c --- /dev/null +++ b/ampd/proto/third_party/ibc/core/connection/v1/tx.proto @@ -0,0 +1,118 @@ +syntax = "proto3"; + +package ibc.core.connection.v1; + +option go_package = "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types"; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "ibc/core/client/v1/client.proto"; +import "ibc/core/connection/v1/connection.proto"; + +// Msg defines the ibc/connection Msg service. +service Msg { + // ConnectionOpenInit defines a rpc handler method for MsgConnectionOpenInit. + rpc ConnectionOpenInit(MsgConnectionOpenInit) returns (MsgConnectionOpenInitResponse); + + // ConnectionOpenTry defines a rpc handler method for MsgConnectionOpenTry. + rpc ConnectionOpenTry(MsgConnectionOpenTry) returns (MsgConnectionOpenTryResponse); + + // ConnectionOpenAck defines a rpc handler method for MsgConnectionOpenAck. + rpc ConnectionOpenAck(MsgConnectionOpenAck) returns (MsgConnectionOpenAckResponse); + + // ConnectionOpenConfirm defines a rpc handler method for + // MsgConnectionOpenConfirm. + rpc ConnectionOpenConfirm(MsgConnectionOpenConfirm) returns (MsgConnectionOpenConfirmResponse); +} + +// MsgConnectionOpenInit defines the msg sent by an account on Chain A to +// initialize a connection with Chain B. +message MsgConnectionOpenInit { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + Counterparty counterparty = 2 [(gogoproto.nullable) = false]; + Version version = 3; + uint64 delay_period = 4 [(gogoproto.moretags) = "yaml:\"delay_period\""]; + string signer = 5; +} + +// MsgConnectionOpenInitResponse defines the Msg/ConnectionOpenInit response +// type. +message MsgConnectionOpenInitResponse {} + +// MsgConnectionOpenTry defines a msg sent by a Relayer to try to open a +// connection on Chain B. +message MsgConnectionOpenTry { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + // Deprecated: this field is unused. Crossing hellos are no longer supported in core IBC. + string previous_connection_id = 2 [deprecated = true, (gogoproto.moretags) = "yaml:\"previous_connection_id\""]; + google.protobuf.Any client_state = 3 [(gogoproto.moretags) = "yaml:\"client_state\""]; + Counterparty counterparty = 4 [(gogoproto.nullable) = false]; + uint64 delay_period = 5 [(gogoproto.moretags) = "yaml:\"delay_period\""]; + repeated Version counterparty_versions = 6 [(gogoproto.moretags) = "yaml:\"counterparty_versions\""]; + ibc.core.client.v1.Height proof_height = 7 + [(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false]; + // proof of the initialization the connection on Chain A: `UNITIALIZED -> + // INIT` + bytes proof_init = 8 [(gogoproto.moretags) = "yaml:\"proof_init\""]; + // proof of client state included in message + bytes proof_client = 9 [(gogoproto.moretags) = "yaml:\"proof_client\""]; + // proof of client consensus state + bytes proof_consensus = 10 [(gogoproto.moretags) = "yaml:\"proof_consensus\""]; + ibc.core.client.v1.Height consensus_height = 11 + [(gogoproto.moretags) = "yaml:\"consensus_height\"", (gogoproto.nullable) = false]; + string signer = 12; +} + +// MsgConnectionOpenTryResponse defines the Msg/ConnectionOpenTry response type. +message MsgConnectionOpenTryResponse {} + +// MsgConnectionOpenAck defines a msg sent by a Relayer to Chain A to +// acknowledge the change of connection state to TRYOPEN on Chain B. +message MsgConnectionOpenAck { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string connection_id = 1 [(gogoproto.moretags) = "yaml:\"connection_id\""]; + string counterparty_connection_id = 2 [(gogoproto.moretags) = "yaml:\"counterparty_connection_id\""]; + Version version = 3; + google.protobuf.Any client_state = 4 [(gogoproto.moretags) = "yaml:\"client_state\""]; + ibc.core.client.v1.Height proof_height = 5 + [(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false]; + // proof of the initialization the connection on Chain B: `UNITIALIZED -> + // TRYOPEN` + bytes proof_try = 6 [(gogoproto.moretags) = "yaml:\"proof_try\""]; + // proof of client state included in message + bytes proof_client = 7 [(gogoproto.moretags) = "yaml:\"proof_client\""]; + // proof of client consensus state + bytes proof_consensus = 8 [(gogoproto.moretags) = "yaml:\"proof_consensus\""]; + ibc.core.client.v1.Height consensus_height = 9 + [(gogoproto.moretags) = "yaml:\"consensus_height\"", (gogoproto.nullable) = false]; + string signer = 10; +} + +// MsgConnectionOpenAckResponse defines the Msg/ConnectionOpenAck response type. +message MsgConnectionOpenAckResponse {} + +// MsgConnectionOpenConfirm defines a msg sent by a Relayer to Chain B to +// acknowledge the change of connection state to OPEN on Chain A. +message MsgConnectionOpenConfirm { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string connection_id = 1 [(gogoproto.moretags) = "yaml:\"connection_id\""]; + // proof for the change of the connection state on Chain A: `INIT -> OPEN` + bytes proof_ack = 2 [(gogoproto.moretags) = "yaml:\"proof_ack\""]; + ibc.core.client.v1.Height proof_height = 3 + [(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false]; + string signer = 4; +} + +// MsgConnectionOpenConfirmResponse defines the Msg/ConnectionOpenConfirm +// response type. +message MsgConnectionOpenConfirmResponse {} diff --git a/ampd/proto/third_party/ibc/core/types/v1/genesis.proto b/ampd/proto/third_party/ibc/core/types/v1/genesis.proto new file mode 100644 index 000000000..4cc931d32 --- /dev/null +++ b/ampd/proto/third_party/ibc/core/types/v1/genesis.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; + +package ibc.core.types.v1; + +option go_package = "github.com/cosmos/ibc-go/v4/modules/core/types"; + +import "gogoproto/gogo.proto"; +import "ibc/core/client/v1/genesis.proto"; +import "ibc/core/connection/v1/genesis.proto"; +import "ibc/core/channel/v1/genesis.proto"; + +// GenesisState defines the ibc module's genesis state. +message GenesisState { + // ICS002 - Clients genesis state + ibc.core.client.v1.GenesisState client_genesis = 1 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"client_genesis\""]; + // ICS003 - Connections genesis state + ibc.core.connection.v1.GenesisState connection_genesis = 2 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"connection_genesis\""]; + // ICS004 - Channel genesis state + ibc.core.channel.v1.GenesisState channel_genesis = 3 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"channel_genesis\""]; +} diff --git a/ampd/proto/third_party/ibc/lightclients/localhost/v1/localhost.proto b/ampd/proto/third_party/ibc/lightclients/localhost/v1/localhost.proto new file mode 100644 index 000000000..9eda835eb --- /dev/null +++ b/ampd/proto/third_party/ibc/lightclients/localhost/v1/localhost.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package ibc.lightclients.localhost.v1; + +option go_package = "github.com/cosmos/ibc-go/v4/modules/light-clients/09-localhost/types"; + +import "gogoproto/gogo.proto"; +import "ibc/core/client/v1/client.proto"; + +// ClientState defines a loopback (localhost) client. It requires (read-only) +// access to keys outside the client prefix. +message ClientState { + option (gogoproto.goproto_getters) = false; + // self chain ID + string chain_id = 1 [(gogoproto.moretags) = "yaml:\"chain_id\""]; + // self latest block height + ibc.core.client.v1.Height height = 2 [(gogoproto.nullable) = false]; +} diff --git a/ampd/proto/third_party/ibc/lightclients/solomachine/v1/solomachine.proto b/ampd/proto/third_party/ibc/lightclients/solomachine/v1/solomachine.proto new file mode 100644 index 000000000..37bd81e92 --- /dev/null +++ b/ampd/proto/third_party/ibc/lightclients/solomachine/v1/solomachine.proto @@ -0,0 +1,189 @@ +syntax = "proto3"; + +package ibc.lightclients.solomachine.v1; + +option go_package = "github.com/cosmos/ibc-go/v4/modules/core/02-client/legacy/v100"; + +import "ibc/core/connection/v1/connection.proto"; +import "ibc/core/channel/v1/channel.proto"; +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; + +// ClientState defines a solo machine client that tracks the current consensus +// state and if the client is frozen. +message ClientState { + option (gogoproto.goproto_getters) = false; + // latest sequence of the client state + uint64 sequence = 1; + // frozen sequence of the solo machine + uint64 frozen_sequence = 2 [(gogoproto.moretags) = "yaml:\"frozen_sequence\""]; + ConsensusState consensus_state = 3 [(gogoproto.moretags) = "yaml:\"consensus_state\""]; + // when set to true, will allow governance to update a solo machine client. + // The client will be unfrozen if it is frozen. + bool allow_update_after_proposal = 4 [(gogoproto.moretags) = "yaml:\"allow_update_after_proposal\""]; +} + +// ConsensusState defines a solo machine consensus state. The sequence of a +// consensus state is contained in the "height" key used in storing the +// consensus state. +message ConsensusState { + option (gogoproto.goproto_getters) = false; + // public key of the solo machine + google.protobuf.Any public_key = 1 [(gogoproto.moretags) = "yaml:\"public_key\""]; + // diversifier allows the same public key to be re-used across different solo + // machine clients (potentially on different chains) without being considered + // misbehaviour. + string diversifier = 2; + uint64 timestamp = 3; +} + +// Header defines a solo machine consensus header +message Header { + option (gogoproto.goproto_getters) = false; + // sequence to update solo machine public key at + uint64 sequence = 1; + uint64 timestamp = 2; + bytes signature = 3; + google.protobuf.Any new_public_key = 4 [(gogoproto.moretags) = "yaml:\"new_public_key\""]; + string new_diversifier = 5 [(gogoproto.moretags) = "yaml:\"new_diversifier\""]; +} + +// Misbehaviour defines misbehaviour for a solo machine which consists +// of a sequence and two signatures over different messages at that sequence. +message Misbehaviour { + option (gogoproto.goproto_getters) = false; + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + uint64 sequence = 2; + SignatureAndData signature_one = 3 [(gogoproto.moretags) = "yaml:\"signature_one\""]; + SignatureAndData signature_two = 4 [(gogoproto.moretags) = "yaml:\"signature_two\""]; +} + +// SignatureAndData contains a signature and the data signed over to create that +// signature. +message SignatureAndData { + option (gogoproto.goproto_getters) = false; + bytes signature = 1; + DataType data_type = 2 [(gogoproto.moretags) = "yaml:\"data_type\""]; + bytes data = 3; + uint64 timestamp = 4; +} + +// TimestampedSignatureData contains the signature data and the timestamp of the +// signature. +message TimestampedSignatureData { + option (gogoproto.goproto_getters) = false; + bytes signature_data = 1 [(gogoproto.moretags) = "yaml:\"signature_data\""]; + uint64 timestamp = 2; +} + +// SignBytes defines the signed bytes used for signature verification. +message SignBytes { + option (gogoproto.goproto_getters) = false; + + uint64 sequence = 1; + uint64 timestamp = 2; + string diversifier = 3; + // type of the data used + DataType data_type = 4 [(gogoproto.moretags) = "yaml:\"data_type\""]; + // marshaled data + bytes data = 5; +} + +// DataType defines the type of solo machine proof being created. This is done +// to preserve uniqueness of different data sign byte encodings. +enum DataType { + option (gogoproto.goproto_enum_prefix) = false; + + // Default State + DATA_TYPE_UNINITIALIZED_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "UNSPECIFIED"]; + // Data type for client state verification + DATA_TYPE_CLIENT_STATE = 1 [(gogoproto.enumvalue_customname) = "CLIENT"]; + // Data type for consensus state verification + DATA_TYPE_CONSENSUS_STATE = 2 [(gogoproto.enumvalue_customname) = "CONSENSUS"]; + // Data type for connection state verification + DATA_TYPE_CONNECTION_STATE = 3 [(gogoproto.enumvalue_customname) = "CONNECTION"]; + // Data type for channel state verification + DATA_TYPE_CHANNEL_STATE = 4 [(gogoproto.enumvalue_customname) = "CHANNEL"]; + // Data type for packet commitment verification + DATA_TYPE_PACKET_COMMITMENT = 5 [(gogoproto.enumvalue_customname) = "PACKETCOMMITMENT"]; + // Data type for packet acknowledgement verification + DATA_TYPE_PACKET_ACKNOWLEDGEMENT = 6 [(gogoproto.enumvalue_customname) = "PACKETACKNOWLEDGEMENT"]; + // Data type for packet receipt absence verification + DATA_TYPE_PACKET_RECEIPT_ABSENCE = 7 [(gogoproto.enumvalue_customname) = "PACKETRECEIPTABSENCE"]; + // Data type for next sequence recv verification + DATA_TYPE_NEXT_SEQUENCE_RECV = 8 [(gogoproto.enumvalue_customname) = "NEXTSEQUENCERECV"]; + // Data type for header verification + DATA_TYPE_HEADER = 9 [(gogoproto.enumvalue_customname) = "HEADER"]; +} + +// HeaderData returns the SignBytes data for update verification. +message HeaderData { + option (gogoproto.goproto_getters) = false; + + // header public key + google.protobuf.Any new_pub_key = 1 [(gogoproto.moretags) = "yaml:\"new_pub_key\""]; + // header diversifier + string new_diversifier = 2 [(gogoproto.moretags) = "yaml:\"new_diversifier\""]; +} + +// ClientStateData returns the SignBytes data for client state verification. +message ClientStateData { + option (gogoproto.goproto_getters) = false; + + bytes path = 1; + google.protobuf.Any client_state = 2 [(gogoproto.moretags) = "yaml:\"client_state\""]; +} + +// ConsensusStateData returns the SignBytes data for consensus state +// verification. +message ConsensusStateData { + option (gogoproto.goproto_getters) = false; + + bytes path = 1; + google.protobuf.Any consensus_state = 2 [(gogoproto.moretags) = "yaml:\"consensus_state\""]; +} + +// ConnectionStateData returns the SignBytes data for connection state +// verification. +message ConnectionStateData { + option (gogoproto.goproto_getters) = false; + + bytes path = 1; + ibc.core.connection.v1.ConnectionEnd connection = 2; +} + +// ChannelStateData returns the SignBytes data for channel state +// verification. +message ChannelStateData { + option (gogoproto.goproto_getters) = false; + + bytes path = 1; + ibc.core.channel.v1.Channel channel = 2; +} + +// PacketCommitmentData returns the SignBytes data for packet commitment +// verification. +message PacketCommitmentData { + bytes path = 1; + bytes commitment = 2; +} + +// PacketAcknowledgementData returns the SignBytes data for acknowledgement +// verification. +message PacketAcknowledgementData { + bytes path = 1; + bytes acknowledgement = 2; +} + +// PacketReceiptAbsenceData returns the SignBytes data for +// packet receipt absence verification. +message PacketReceiptAbsenceData { + bytes path = 1; +} + +// NextSequenceRecvData returns the SignBytes data for verification of the next +// sequence to be received. +message NextSequenceRecvData { + bytes path = 1; + uint64 next_seq_recv = 2 [(gogoproto.moretags) = "yaml:\"next_seq_recv\""]; +} diff --git a/ampd/proto/third_party/ibc/lightclients/solomachine/v2/solomachine.proto b/ampd/proto/third_party/ibc/lightclients/solomachine/v2/solomachine.proto new file mode 100644 index 000000000..c735fdddd --- /dev/null +++ b/ampd/proto/third_party/ibc/lightclients/solomachine/v2/solomachine.proto @@ -0,0 +1,189 @@ +syntax = "proto3"; + +package ibc.lightclients.solomachine.v2; + +option go_package = "github.com/cosmos/ibc-go/v4/modules/light-clients/06-solomachine/types"; + +import "ibc/core/connection/v1/connection.proto"; +import "ibc/core/channel/v1/channel.proto"; +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; + +// ClientState defines a solo machine client that tracks the current consensus +// state and if the client is frozen. +message ClientState { + option (gogoproto.goproto_getters) = false; + // latest sequence of the client state + uint64 sequence = 1; + // frozen sequence of the solo machine + bool is_frozen = 2 [(gogoproto.moretags) = "yaml:\"is_frozen\""]; + ConsensusState consensus_state = 3 [(gogoproto.moretags) = "yaml:\"consensus_state\""]; + // when set to true, will allow governance to update a solo machine client. + // The client will be unfrozen if it is frozen. + bool allow_update_after_proposal = 4 [(gogoproto.moretags) = "yaml:\"allow_update_after_proposal\""]; +} + +// ConsensusState defines a solo machine consensus state. The sequence of a +// consensus state is contained in the "height" key used in storing the +// consensus state. +message ConsensusState { + option (gogoproto.goproto_getters) = false; + // public key of the solo machine + google.protobuf.Any public_key = 1 [(gogoproto.moretags) = "yaml:\"public_key\""]; + // diversifier allows the same public key to be re-used across different solo + // machine clients (potentially on different chains) without being considered + // misbehaviour. + string diversifier = 2; + uint64 timestamp = 3; +} + +// Header defines a solo machine consensus header +message Header { + option (gogoproto.goproto_getters) = false; + // sequence to update solo machine public key at + uint64 sequence = 1; + uint64 timestamp = 2; + bytes signature = 3; + google.protobuf.Any new_public_key = 4 [(gogoproto.moretags) = "yaml:\"new_public_key\""]; + string new_diversifier = 5 [(gogoproto.moretags) = "yaml:\"new_diversifier\""]; +} + +// Misbehaviour defines misbehaviour for a solo machine which consists +// of a sequence and two signatures over different messages at that sequence. +message Misbehaviour { + option (gogoproto.goproto_getters) = false; + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + uint64 sequence = 2; + SignatureAndData signature_one = 3 [(gogoproto.moretags) = "yaml:\"signature_one\""]; + SignatureAndData signature_two = 4 [(gogoproto.moretags) = "yaml:\"signature_two\""]; +} + +// SignatureAndData contains a signature and the data signed over to create that +// signature. +message SignatureAndData { + option (gogoproto.goproto_getters) = false; + bytes signature = 1; + DataType data_type = 2 [(gogoproto.moretags) = "yaml:\"data_type\""]; + bytes data = 3; + uint64 timestamp = 4; +} + +// TimestampedSignatureData contains the signature data and the timestamp of the +// signature. +message TimestampedSignatureData { + option (gogoproto.goproto_getters) = false; + bytes signature_data = 1 [(gogoproto.moretags) = "yaml:\"signature_data\""]; + uint64 timestamp = 2; +} + +// SignBytes defines the signed bytes used for signature verification. +message SignBytes { + option (gogoproto.goproto_getters) = false; + + uint64 sequence = 1; + uint64 timestamp = 2; + string diversifier = 3; + // type of the data used + DataType data_type = 4 [(gogoproto.moretags) = "yaml:\"data_type\""]; + // marshaled data + bytes data = 5; +} + +// DataType defines the type of solo machine proof being created. This is done +// to preserve uniqueness of different data sign byte encodings. +enum DataType { + option (gogoproto.goproto_enum_prefix) = false; + + // Default State + DATA_TYPE_UNINITIALIZED_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "UNSPECIFIED"]; + // Data type for client state verification + DATA_TYPE_CLIENT_STATE = 1 [(gogoproto.enumvalue_customname) = "CLIENT"]; + // Data type for consensus state verification + DATA_TYPE_CONSENSUS_STATE = 2 [(gogoproto.enumvalue_customname) = "CONSENSUS"]; + // Data type for connection state verification + DATA_TYPE_CONNECTION_STATE = 3 [(gogoproto.enumvalue_customname) = "CONNECTION"]; + // Data type for channel state verification + DATA_TYPE_CHANNEL_STATE = 4 [(gogoproto.enumvalue_customname) = "CHANNEL"]; + // Data type for packet commitment verification + DATA_TYPE_PACKET_COMMITMENT = 5 [(gogoproto.enumvalue_customname) = "PACKETCOMMITMENT"]; + // Data type for packet acknowledgement verification + DATA_TYPE_PACKET_ACKNOWLEDGEMENT = 6 [(gogoproto.enumvalue_customname) = "PACKETACKNOWLEDGEMENT"]; + // Data type for packet receipt absence verification + DATA_TYPE_PACKET_RECEIPT_ABSENCE = 7 [(gogoproto.enumvalue_customname) = "PACKETRECEIPTABSENCE"]; + // Data type for next sequence recv verification + DATA_TYPE_NEXT_SEQUENCE_RECV = 8 [(gogoproto.enumvalue_customname) = "NEXTSEQUENCERECV"]; + // Data type for header verification + DATA_TYPE_HEADER = 9 [(gogoproto.enumvalue_customname) = "HEADER"]; +} + +// HeaderData returns the SignBytes data for update verification. +message HeaderData { + option (gogoproto.goproto_getters) = false; + + // header public key + google.protobuf.Any new_pub_key = 1 [(gogoproto.moretags) = "yaml:\"new_pub_key\""]; + // header diversifier + string new_diversifier = 2 [(gogoproto.moretags) = "yaml:\"new_diversifier\""]; +} + +// ClientStateData returns the SignBytes data for client state verification. +message ClientStateData { + option (gogoproto.goproto_getters) = false; + + bytes path = 1; + google.protobuf.Any client_state = 2 [(gogoproto.moretags) = "yaml:\"client_state\""]; +} + +// ConsensusStateData returns the SignBytes data for consensus state +// verification. +message ConsensusStateData { + option (gogoproto.goproto_getters) = false; + + bytes path = 1; + google.protobuf.Any consensus_state = 2 [(gogoproto.moretags) = "yaml:\"consensus_state\""]; +} + +// ConnectionStateData returns the SignBytes data for connection state +// verification. +message ConnectionStateData { + option (gogoproto.goproto_getters) = false; + + bytes path = 1; + ibc.core.connection.v1.ConnectionEnd connection = 2; +} + +// ChannelStateData returns the SignBytes data for channel state +// verification. +message ChannelStateData { + option (gogoproto.goproto_getters) = false; + + bytes path = 1; + ibc.core.channel.v1.Channel channel = 2; +} + +// PacketCommitmentData returns the SignBytes data for packet commitment +// verification. +message PacketCommitmentData { + bytes path = 1; + bytes commitment = 2; +} + +// PacketAcknowledgementData returns the SignBytes data for acknowledgement +// verification. +message PacketAcknowledgementData { + bytes path = 1; + bytes acknowledgement = 2; +} + +// PacketReceiptAbsenceData returns the SignBytes data for +// packet receipt absence verification. +message PacketReceiptAbsenceData { + bytes path = 1; +} + +// NextSequenceRecvData returns the SignBytes data for verification of the next +// sequence to be received. +message NextSequenceRecvData { + bytes path = 1; + uint64 next_seq_recv = 2 [(gogoproto.moretags) = "yaml:\"next_seq_recv\""]; +} diff --git a/ampd/proto/third_party/ibc/lightclients/tendermint/v1/tendermint.proto b/ampd/proto/third_party/ibc/lightclients/tendermint/v1/tendermint.proto new file mode 100644 index 000000000..55a4e0690 --- /dev/null +++ b/ampd/proto/third_party/ibc/lightclients/tendermint/v1/tendermint.proto @@ -0,0 +1,114 @@ +syntax = "proto3"; + +package ibc.lightclients.tendermint.v1; + +option go_package = "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types"; + +import "tendermint/types/validator.proto"; +import "tendermint/types/types.proto"; +import "proofs.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/timestamp.proto"; +import "ibc/core/client/v1/client.proto"; +import "ibc/core/commitment/v1/commitment.proto"; +import "gogoproto/gogo.proto"; + +// ClientState from Tendermint tracks the current validator set, latest height, +// and a possible frozen height. +message ClientState { + option (gogoproto.goproto_getters) = false; + + string chain_id = 1; + Fraction trust_level = 2 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"trust_level\""]; + // duration of the period since the LastestTimestamp during which the + // submitted headers are valid for upgrade + google.protobuf.Duration trusting_period = 3 + [(gogoproto.nullable) = false, (gogoproto.stdduration) = true, (gogoproto.moretags) = "yaml:\"trusting_period\""]; + // duration of the staking unbonding period + google.protobuf.Duration unbonding_period = 4 [ + (gogoproto.nullable) = false, + (gogoproto.stdduration) = true, + (gogoproto.moretags) = "yaml:\"unbonding_period\"" + ]; + // defines how much new (untrusted) header's Time can drift into the future. + google.protobuf.Duration max_clock_drift = 5 + [(gogoproto.nullable) = false, (gogoproto.stdduration) = true, (gogoproto.moretags) = "yaml:\"max_clock_drift\""]; + // Block height when the client was frozen due to a misbehaviour + ibc.core.client.v1.Height frozen_height = 6 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"frozen_height\""]; + // Latest height the client was updated to + ibc.core.client.v1.Height latest_height = 7 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"latest_height\""]; + + // Proof specifications used in verifying counterparty state + repeated ics23.ProofSpec proof_specs = 8 [(gogoproto.moretags) = "yaml:\"proof_specs\""]; + + // Path at which next upgraded client will be committed. + // Each element corresponds to the key for a single CommitmentProof in the + // chained proof. NOTE: ClientState must stored under + // `{upgradePath}/{upgradeHeight}/clientState` ConsensusState must be stored + // under `{upgradepath}/{upgradeHeight}/consensusState` For SDK chains using + // the default upgrade module, upgrade_path should be []string{"upgrade", + // "upgradedIBCState"}` + repeated string upgrade_path = 9 [(gogoproto.moretags) = "yaml:\"upgrade_path\""]; + + // allow_update_after_expiry is deprecated + bool allow_update_after_expiry = 10 [deprecated = true, (gogoproto.moretags) = "yaml:\"allow_update_after_expiry\""]; + // allow_update_after_misbehaviour is deprecated + bool allow_update_after_misbehaviour = 11 + [deprecated = true, (gogoproto.moretags) = "yaml:\"allow_update_after_misbehaviour\""]; +} + +// ConsensusState defines the consensus state from Tendermint. +message ConsensusState { + option (gogoproto.goproto_getters) = false; + + // timestamp that corresponds to the block height in which the ConsensusState + // was stored. + google.protobuf.Timestamp timestamp = 1 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + // commitment root (i.e app hash) + ibc.core.commitment.v1.MerkleRoot root = 2 [(gogoproto.nullable) = false]; + bytes next_validators_hash = 3 [ + (gogoproto.casttype) = "github.com/tendermint/tendermint/libs/bytes.HexBytes", + (gogoproto.moretags) = "yaml:\"next_validators_hash\"" + ]; +} + +// Misbehaviour is a wrapper over two conflicting Headers +// that implements Misbehaviour interface expected by ICS-02 +message Misbehaviour { + option (gogoproto.goproto_getters) = false; + + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + Header header_1 = 2 [(gogoproto.customname) = "Header1", (gogoproto.moretags) = "yaml:\"header_1\""]; + Header header_2 = 3 [(gogoproto.customname) = "Header2", (gogoproto.moretags) = "yaml:\"header_2\""]; +} + +// Header defines the Tendermint client consensus Header. +// It encapsulates all the information necessary to update from a trusted +// Tendermint ConsensusState. The inclusion of TrustedHeight and +// TrustedValidators allows this update to process correctly, so long as the +// ConsensusState for the TrustedHeight exists, this removes race conditions +// among relayers The SignedHeader and ValidatorSet are the new untrusted update +// fields for the client. The TrustedHeight is the height of a stored +// ConsensusState on the client that will be used to verify the new untrusted +// header. The Trusted ConsensusState must be within the unbonding period of +// current time in order to correctly verify, and the TrustedValidators must +// hash to TrustedConsensusState.NextValidatorsHash since that is the last +// trusted validator set at the TrustedHeight. +message Header { + .tendermint.types.SignedHeader signed_header = 1 + [(gogoproto.embed) = true, (gogoproto.moretags) = "yaml:\"signed_header\""]; + + .tendermint.types.ValidatorSet validator_set = 2 [(gogoproto.moretags) = "yaml:\"validator_set\""]; + ibc.core.client.v1.Height trusted_height = 3 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"trusted_height\""]; + .tendermint.types.ValidatorSet trusted_validators = 4 [(gogoproto.moretags) = "yaml:\"trusted_validators\""]; +} + +// Fraction defines the protobuf message type for tmmath.Fraction that only +// supports positive values. +message Fraction { + uint64 numerator = 1; + uint64 denominator = 2; +} diff --git a/ampd/proto/third_party/proofs.proto b/ampd/proto/third_party/proofs.proto new file mode 100644 index 000000000..88b50c1b3 --- /dev/null +++ b/ampd/proto/third_party/proofs.proto @@ -0,0 +1,234 @@ +syntax = "proto3"; + +package ics23; +option go_package = "github.com/confio/ics23/go"; +enum HashOp { + // NO_HASH is the default if no data passed. Note this is an illegal argument some places. + NO_HASH = 0; + SHA256 = 1; + SHA512 = 2; + KECCAK = 3; + RIPEMD160 = 4; + BITCOIN = 5; // ripemd160(sha256(x)) + SHA512_256 = 6; +} + +/** +LengthOp defines how to process the key and value of the LeafOp +to include length information. After encoding the length with the given +algorithm, the length will be prepended to the key and value bytes. +(Each one with it's own encoded length) +*/ +enum LengthOp { + // NO_PREFIX don't include any length info + NO_PREFIX = 0; + // VAR_PROTO uses protobuf (and go-amino) varint encoding of the length + VAR_PROTO = 1; + // VAR_RLP uses rlp int encoding of the length + VAR_RLP = 2; + // FIXED32_BIG uses big-endian encoding of the length as a 32 bit integer + FIXED32_BIG = 3; + // FIXED32_LITTLE uses little-endian encoding of the length as a 32 bit integer + FIXED32_LITTLE = 4; + // FIXED64_BIG uses big-endian encoding of the length as a 64 bit integer + FIXED64_BIG = 5; + // FIXED64_LITTLE uses little-endian encoding of the length as a 64 bit integer + FIXED64_LITTLE = 6; + // REQUIRE_32_BYTES is like NONE, but will fail if the input is not exactly 32 bytes (sha256 output) + REQUIRE_32_BYTES = 7; + // REQUIRE_64_BYTES is like NONE, but will fail if the input is not exactly 64 bytes (sha512 output) + REQUIRE_64_BYTES = 8; +} + +/** +ExistenceProof takes a key and a value and a set of steps to perform on it. +The result of peforming all these steps will provide a "root hash", which can +be compared to the value in a header. + +Since it is computationally infeasible to produce a hash collission for any of the used +cryptographic hash functions, if someone can provide a series of operations to transform +a given key and value into a root hash that matches some trusted root, these key and values +must be in the referenced merkle tree. + +The only possible issue is maliablity in LeafOp, such as providing extra prefix data, +which should be controlled by a spec. Eg. with lengthOp as NONE, + prefix = FOO, key = BAR, value = CHOICE +and + prefix = F, key = OOBAR, value = CHOICE +would produce the same value. + +With LengthOp this is tricker but not impossible. Which is why the "leafPrefixEqual" field +in the ProofSpec is valuable to prevent this mutability. And why all trees should +length-prefix the data before hashing it. +*/ +message ExistenceProof { + bytes key = 1; + bytes value = 2; + LeafOp leaf = 3; + repeated InnerOp path = 4; +} + +/* +NonExistenceProof takes a proof of two neighbors, one left of the desired key, +one right of the desired key. If both proofs are valid AND they are neighbors, +then there is no valid proof for the given key. +*/ +message NonExistenceProof { + bytes key = 1; // TODO: remove this as unnecessary??? we prove a range + ExistenceProof left = 2; + ExistenceProof right = 3; +} + +/* +CommitmentProof is either an ExistenceProof or a NonExistenceProof, or a Batch of such messages +*/ +message CommitmentProof { + oneof proof { + ExistenceProof exist = 1; + NonExistenceProof nonexist = 2; + BatchProof batch = 3; + CompressedBatchProof compressed = 4; + } +} + +/** +LeafOp represents the raw key-value data we wish to prove, and +must be flexible to represent the internal transformation from +the original key-value pairs into the basis hash, for many existing +merkle trees. + +key and value are passed in. So that the signature of this operation is: + leafOp(key, value) -> output + +To process this, first prehash the keys and values if needed (ANY means no hash in this case): + hkey = prehashKey(key) + hvalue = prehashValue(value) + +Then combine the bytes, and hash it + output = hash(prefix || length(hkey) || hkey || length(hvalue) || hvalue) +*/ +message LeafOp { + HashOp hash = 1; + HashOp prehash_key = 2; + HashOp prehash_value = 3; + LengthOp length = 4; + // prefix is a fixed bytes that may optionally be included at the beginning to differentiate + // a leaf node from an inner node. + bytes prefix = 5; +} + +/** +InnerOp represents a merkle-proof step that is not a leaf. +It represents concatenating two children and hashing them to provide the next result. + +The result of the previous step is passed in, so the signature of this op is: + innerOp(child) -> output + +The result of applying InnerOp should be: + output = op.hash(op.prefix || child || op.suffix) + + where the || operator is concatenation of binary data, +and child is the result of hashing all the tree below this step. + +Any special data, like prepending child with the length, or prepending the entire operation with +some value to differentiate from leaf nodes, should be included in prefix and suffix. +If either of prefix or suffix is empty, we just treat it as an empty string +*/ +message InnerOp { + HashOp hash = 1; + bytes prefix = 2; + bytes suffix = 3; +} + + +/** +ProofSpec defines what the expected parameters are for a given proof type. +This can be stored in the client and used to validate any incoming proofs. + + verify(ProofSpec, Proof) -> Proof | Error + +As demonstrated in tests, if we don't fix the algorithm used to calculate the +LeafHash for a given tree, there are many possible key-value pairs that can +generate a given hash (by interpretting the preimage differently). +We need this for proper security, requires client knows a priori what +tree format server uses. But not in code, rather a configuration object. +*/ +message ProofSpec { + // any field in the ExistenceProof must be the same as in this spec. + // except Prefix, which is just the first bytes of prefix (spec can be longer) + LeafOp leaf_spec = 1; + InnerSpec inner_spec = 2; + // max_depth (if > 0) is the maximum number of InnerOps allowed (mainly for fixed-depth tries) + int32 max_depth = 3; + // min_depth (if > 0) is the minimum number of InnerOps allowed (mainly for fixed-depth tries) + int32 min_depth = 4; +} + +/* +InnerSpec contains all store-specific structure info to determine if two proofs from a +given store are neighbors. + +This enables: + + isLeftMost(spec: InnerSpec, op: InnerOp) + isRightMost(spec: InnerSpec, op: InnerOp) + isLeftNeighbor(spec: InnerSpec, left: InnerOp, right: InnerOp) +*/ +message InnerSpec { + // Child order is the ordering of the children node, must count from 0 + // iavl tree is [0, 1] (left then right) + // merk is [0, 2, 1] (left, right, here) + repeated int32 child_order = 1; + int32 child_size = 2; + int32 min_prefix_length = 3; + int32 max_prefix_length = 4; + // empty child is the prehash image that is used when one child is nil (eg. 20 bytes of 0) + bytes empty_child = 5; + // hash is the algorithm that must be used for each InnerOp + HashOp hash = 6; +} + +/* +BatchProof is a group of multiple proof types than can be compressed +*/ +message BatchProof { + repeated BatchEntry entries = 1; +} + +// Use BatchEntry not CommitmentProof, to avoid recursion +message BatchEntry { + oneof proof { + ExistenceProof exist = 1; + NonExistenceProof nonexist = 2; + } +} + + +/****** all items here are compressed forms *******/ + +message CompressedBatchProof { + repeated CompressedBatchEntry entries = 1; + repeated InnerOp lookup_inners = 2; +} + +// Use BatchEntry not CommitmentProof, to avoid recursion +message CompressedBatchEntry { + oneof proof { + CompressedExistenceProof exist = 1; + CompressedNonExistenceProof nonexist = 2; + } +} + +message CompressedExistenceProof { + bytes key = 1; + bytes value = 2; + LeafOp leaf = 3; + // these are indexes into the lookup_inners table in CompressedBatchProof + repeated int32 path = 4; +} + +message CompressedNonExistenceProof { + bytes key = 1; // TODO: remove this as unnecessary??? we prove a range + CompressedExistenceProof left = 2; + CompressedExistenceProof right = 3; +} diff --git a/ampd/proto/third_party/tendermint/abci/types.proto b/ampd/proto/third_party/tendermint/abci/types.proto new file mode 100644 index 000000000..44f861129 --- /dev/null +++ b/ampd/proto/third_party/tendermint/abci/types.proto @@ -0,0 +1,413 @@ +syntax = "proto3"; +package tendermint.abci; + +option go_package = "github.com/tendermint/tendermint/abci/types"; + +// For more information on gogo.proto, see: +// https://github.com/gogo/protobuf/blob/master/extensions.md +import "tendermint/crypto/proof.proto"; +import "tendermint/types/types.proto"; +import "tendermint/crypto/keys.proto"; +import "tendermint/types/params.proto"; +import "google/protobuf/timestamp.proto"; +import "gogoproto/gogo.proto"; + +// This file is copied from http://github.com/tendermint/abci +// NOTE: When using custom types, mind the warnings. +// https://github.com/gogo/protobuf/blob/master/custom_types.md#warnings-and-issues + +//---------------------------------------- +// Request types + +message Request { + oneof value { + RequestEcho echo = 1; + RequestFlush flush = 2; + RequestInfo info = 3; + RequestSetOption set_option = 4; + RequestInitChain init_chain = 5; + RequestQuery query = 6; + RequestBeginBlock begin_block = 7; + RequestCheckTx check_tx = 8; + RequestDeliverTx deliver_tx = 9; + RequestEndBlock end_block = 10; + RequestCommit commit = 11; + RequestListSnapshots list_snapshots = 12; + RequestOfferSnapshot offer_snapshot = 13; + RequestLoadSnapshotChunk load_snapshot_chunk = 14; + RequestApplySnapshotChunk apply_snapshot_chunk = 15; + } +} + +message RequestEcho { + string message = 1; +} + +message RequestFlush {} + +message RequestInfo { + string version = 1; + uint64 block_version = 2; + uint64 p2p_version = 3; +} + +// nondeterministic +message RequestSetOption { + string key = 1; + string value = 2; +} + +message RequestInitChain { + google.protobuf.Timestamp time = 1 + [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + string chain_id = 2; + ConsensusParams consensus_params = 3; + repeated ValidatorUpdate validators = 4 [(gogoproto.nullable) = false]; + bytes app_state_bytes = 5; + int64 initial_height = 6; +} + +message RequestQuery { + bytes data = 1; + string path = 2; + int64 height = 3; + bool prove = 4; +} + +message RequestBeginBlock { + bytes hash = 1; + tendermint.types.Header header = 2 [(gogoproto.nullable) = false]; + LastCommitInfo last_commit_info = 3 [(gogoproto.nullable) = false]; + repeated Evidence byzantine_validators = 4 [(gogoproto.nullable) = false]; +} + +enum CheckTxType { + NEW = 0 [(gogoproto.enumvalue_customname) = "New"]; + RECHECK = 1 [(gogoproto.enumvalue_customname) = "Recheck"]; +} + +message RequestCheckTx { + bytes tx = 1; + CheckTxType type = 2; +} + +message RequestDeliverTx { + bytes tx = 1; +} + +message RequestEndBlock { + int64 height = 1; +} + +message RequestCommit {} + +// lists available snapshots +message RequestListSnapshots {} + +// offers a snapshot to the application +message RequestOfferSnapshot { + Snapshot snapshot = 1; // snapshot offered by peers + bytes app_hash = 2; // light client-verified app hash for snapshot height +} + +// loads a snapshot chunk +message RequestLoadSnapshotChunk { + uint64 height = 1; + uint32 format = 2; + uint32 chunk = 3; +} + +// Applies a snapshot chunk +message RequestApplySnapshotChunk { + uint32 index = 1; + bytes chunk = 2; + string sender = 3; +} + +//---------------------------------------- +// Response types + +message Response { + oneof value { + ResponseException exception = 1; + ResponseEcho echo = 2; + ResponseFlush flush = 3; + ResponseInfo info = 4; + ResponseSetOption set_option = 5; + ResponseInitChain init_chain = 6; + ResponseQuery query = 7; + ResponseBeginBlock begin_block = 8; + ResponseCheckTx check_tx = 9; + ResponseDeliverTx deliver_tx = 10; + ResponseEndBlock end_block = 11; + ResponseCommit commit = 12; + ResponseListSnapshots list_snapshots = 13; + ResponseOfferSnapshot offer_snapshot = 14; + ResponseLoadSnapshotChunk load_snapshot_chunk = 15; + ResponseApplySnapshotChunk apply_snapshot_chunk = 16; + } +} + +// nondeterministic +message ResponseException { + string error = 1; +} + +message ResponseEcho { + string message = 1; +} + +message ResponseFlush {} + +message ResponseInfo { + string data = 1; + + string version = 2; + uint64 app_version = 3; + + int64 last_block_height = 4; + bytes last_block_app_hash = 5; +} + +// nondeterministic +message ResponseSetOption { + uint32 code = 1; + // bytes data = 2; + string log = 3; + string info = 4; +} + +message ResponseInitChain { + ConsensusParams consensus_params = 1; + repeated ValidatorUpdate validators = 2 [(gogoproto.nullable) = false]; + bytes app_hash = 3; +} + +message ResponseQuery { + uint32 code = 1; + // bytes data = 2; // use "value" instead. + string log = 3; // nondeterministic + string info = 4; // nondeterministic + int64 index = 5; + bytes key = 6; + bytes value = 7; + tendermint.crypto.ProofOps proof_ops = 8; + int64 height = 9; + string codespace = 10; +} + +message ResponseBeginBlock { + repeated Event events = 1 + [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; +} + +message ResponseCheckTx { + uint32 code = 1; + bytes data = 2; + string log = 3; // nondeterministic + string info = 4; // nondeterministic + int64 gas_wanted = 5 [json_name = "gas_wanted"]; + int64 gas_used = 6 [json_name = "gas_used"]; + repeated Event events = 7 + [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; + string codespace = 8; + string sender = 9; + int64 priority = 10; + + // mempool_error is set by CometBFT. + // ABCI applictions creating a ResponseCheckTX should not set mempool_error. + string mempool_error = 11; +} + +message ResponseDeliverTx { + uint32 code = 1; + bytes data = 2; + string log = 3; // nondeterministic + string info = 4; // nondeterministic + int64 gas_wanted = 5 [json_name = "gas_wanted"]; + int64 gas_used = 6 [json_name = "gas_used"]; + repeated Event events = 7 [ + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "events,omitempty" + ]; // nondeterministic + string codespace = 8; +} + +message ResponseEndBlock { + repeated ValidatorUpdate validator_updates = 1 [(gogoproto.nullable) = false]; + ConsensusParams consensus_param_updates = 2; + repeated Event events = 3 + [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; +} + +message ResponseCommit { + // reserve 1 + bytes data = 2; + int64 retain_height = 3; +} + +message ResponseListSnapshots { + repeated Snapshot snapshots = 1; +} + +message ResponseOfferSnapshot { + Result result = 1; + + enum Result { + UNKNOWN = 0; // Unknown result, abort all snapshot restoration + ACCEPT = 1; // Snapshot accepted, apply chunks + ABORT = 2; // Abort all snapshot restoration + REJECT = 3; // Reject this specific snapshot, try others + REJECT_FORMAT = 4; // Reject all snapshots of this format, try others + REJECT_SENDER = 5; // Reject all snapshots from the sender(s), try others + } +} + +message ResponseLoadSnapshotChunk { + bytes chunk = 1; +} + +message ResponseApplySnapshotChunk { + Result result = 1; + repeated uint32 refetch_chunks = 2; // Chunks to refetch and reapply + repeated string reject_senders = 3; // Chunk senders to reject and ban + + enum Result { + UNKNOWN = 0; // Unknown result, abort all snapshot restoration + ACCEPT = 1; // Chunk successfully accepted + ABORT = 2; // Abort all snapshot restoration + RETRY = 3; // Retry chunk (combine with refetch and reject) + RETRY_SNAPSHOT = 4; // Retry snapshot (combine with refetch and reject) + REJECT_SNAPSHOT = 5; // Reject this snapshot, try others + } +} + +//---------------------------------------- +// Misc. + +// ConsensusParams contains all consensus-relevant parameters +// that can be adjusted by the abci app +message ConsensusParams { + BlockParams block = 1; + tendermint.types.EvidenceParams evidence = 2; + tendermint.types.ValidatorParams validator = 3; + tendermint.types.VersionParams version = 4; +} + +// BlockParams contains limits on the block size. +message BlockParams { + // Note: must be greater than 0 + int64 max_bytes = 1; + // Note: must be greater or equal to -1 + int64 max_gas = 2; +} + +message LastCommitInfo { + int32 round = 1; + repeated VoteInfo votes = 2 [(gogoproto.nullable) = false]; +} + +// Event allows application developers to attach additional information to +// ResponseBeginBlock, ResponseEndBlock, ResponseCheckTx and ResponseDeliverTx. +// Later, transactions may be queried using these events. +message Event { + string type = 1; + repeated EventAttribute attributes = 2 [ + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "attributes,omitempty" + ]; +} + +// EventAttribute is a single key-value pair, associated with an event. +message EventAttribute { + bytes key = 1; + bytes value = 2; + bool index = 3; // nondeterministic +} + +// TxResult contains results of executing the transaction. +// +// One usage is indexing transaction results. +message TxResult { + int64 height = 1; + uint32 index = 2; + bytes tx = 3; + ResponseDeliverTx result = 4 [(gogoproto.nullable) = false]; +} + +//---------------------------------------- +// Blockchain Types + +// Validator +message Validator { + bytes address = 1; // The first 20 bytes of SHA256(public key) + // PubKey pub_key = 2 [(gogoproto.nullable)=false]; + int64 power = 3; // The voting power +} + +// ValidatorUpdate +message ValidatorUpdate { + tendermint.crypto.PublicKey pub_key = 1 [(gogoproto.nullable) = false]; + int64 power = 2; +} + +// VoteInfo +message VoteInfo { + Validator validator = 1 [(gogoproto.nullable) = false]; + bool signed_last_block = 2; +} + +enum EvidenceType { + UNKNOWN = 0; + DUPLICATE_VOTE = 1; + LIGHT_CLIENT_ATTACK = 2; +} + +message Evidence { + EvidenceType type = 1; + // The offending validator + Validator validator = 2 [(gogoproto.nullable) = false]; + // The height when the offense occurred + int64 height = 3; + // The corresponding time where the offense occurred + google.protobuf.Timestamp time = 4 + [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + // Total voting power of the validator set in case the ABCI application does + // not store historical validators. + // https://github.com/tendermint/tendermint/issues/4581 + int64 total_voting_power = 5; +} + +//---------------------------------------- +// State Sync Types + +message Snapshot { + uint64 height = 1; // The height at which the snapshot was taken + uint32 format = 2; // The application-specific snapshot format + uint32 chunks = 3; // Number of chunks in the snapshot + bytes hash = 4; // Arbitrary snapshot hash, equal only if identical + bytes metadata = 5; // Arbitrary application metadata +} + +//---------------------------------------- +// Service Definition + +service ABCIApplication { + rpc Echo(RequestEcho) returns (ResponseEcho); + rpc Flush(RequestFlush) returns (ResponseFlush); + rpc Info(RequestInfo) returns (ResponseInfo); + rpc SetOption(RequestSetOption) returns (ResponseSetOption); + rpc DeliverTx(RequestDeliverTx) returns (ResponseDeliverTx); + rpc CheckTx(RequestCheckTx) returns (ResponseCheckTx); + rpc Query(RequestQuery) returns (ResponseQuery); + rpc Commit(RequestCommit) returns (ResponseCommit); + rpc InitChain(RequestInitChain) returns (ResponseInitChain); + rpc BeginBlock(RequestBeginBlock) returns (ResponseBeginBlock); + rpc EndBlock(RequestEndBlock) returns (ResponseEndBlock); + rpc ListSnapshots(RequestListSnapshots) returns (ResponseListSnapshots); + rpc OfferSnapshot(RequestOfferSnapshot) returns (ResponseOfferSnapshot); + rpc LoadSnapshotChunk(RequestLoadSnapshotChunk) + returns (ResponseLoadSnapshotChunk); + rpc ApplySnapshotChunk(RequestApplySnapshotChunk) + returns (ResponseApplySnapshotChunk); +} diff --git a/ampd/proto/third_party/tendermint/crypto/keys.proto b/ampd/proto/third_party/tendermint/crypto/keys.proto new file mode 100644 index 000000000..5b94ddaec --- /dev/null +++ b/ampd/proto/third_party/tendermint/crypto/keys.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; +package tendermint.crypto; + +option go_package = "github.com/tendermint/tendermint/proto/tendermint/crypto"; + +import "gogoproto/gogo.proto"; + +// PublicKey defines the keys available for use with Validators +message PublicKey { + option (gogoproto.compare) = true; + option (gogoproto.equal) = true; + + oneof sum { + bytes ed25519 = 1; + bytes secp256k1 = 2; + } +} diff --git a/ampd/proto/third_party/tendermint/crypto/proof.proto b/ampd/proto/third_party/tendermint/crypto/proof.proto new file mode 100644 index 000000000..975df7685 --- /dev/null +++ b/ampd/proto/third_party/tendermint/crypto/proof.proto @@ -0,0 +1,41 @@ +syntax = "proto3"; +package tendermint.crypto; + +option go_package = "github.com/tendermint/tendermint/proto/tendermint/crypto"; + +import "gogoproto/gogo.proto"; + +message Proof { + int64 total = 1; + int64 index = 2; + bytes leaf_hash = 3; + repeated bytes aunts = 4; +} + +message ValueOp { + // Encoded in ProofOp.Key. + bytes key = 1; + + // To encode in ProofOp.Data + Proof proof = 2; +} + +message DominoOp { + string key = 1; + string input = 2; + string output = 3; +} + +// ProofOp defines an operation used for calculating Merkle root +// The data could be arbitrary format, providing nessecary data +// for example neighbouring node hash +message ProofOp { + string type = 1; + bytes key = 2; + bytes data = 3; +} + +// ProofOps is Merkle proof defined by the list of ProofOps +message ProofOps { + repeated ProofOp ops = 1 [(gogoproto.nullable) = false]; +} diff --git a/ampd/proto/third_party/tendermint/libs/bits/types.proto b/ampd/proto/third_party/tendermint/libs/bits/types.proto new file mode 100644 index 000000000..3111d113a --- /dev/null +++ b/ampd/proto/third_party/tendermint/libs/bits/types.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; +package tendermint.libs.bits; + +option go_package = "github.com/tendermint/tendermint/proto/tendermint/libs/bits"; + +message BitArray { + int64 bits = 1; + repeated uint64 elems = 2; +} diff --git a/ampd/proto/third_party/tendermint/p2p/types.proto b/ampd/proto/third_party/tendermint/p2p/types.proto new file mode 100644 index 000000000..0d42ea400 --- /dev/null +++ b/ampd/proto/third_party/tendermint/p2p/types.proto @@ -0,0 +1,34 @@ +syntax = "proto3"; +package tendermint.p2p; + +option go_package = "github.com/tendermint/tendermint/proto/tendermint/p2p"; + +import "gogoproto/gogo.proto"; + +message NetAddress { + string id = 1 [(gogoproto.customname) = "ID"]; + string ip = 2 [(gogoproto.customname) = "IP"]; + uint32 port = 3; +} + +message ProtocolVersion { + uint64 p2p = 1 [(gogoproto.customname) = "P2P"]; + uint64 block = 2; + uint64 app = 3; +} + +message DefaultNodeInfo { + ProtocolVersion protocol_version = 1 [(gogoproto.nullable) = false]; + string default_node_id = 2 [(gogoproto.customname) = "DefaultNodeID"]; + string listen_addr = 3; + string network = 4; + string version = 5; + bytes channels = 6; + string moniker = 7; + DefaultNodeInfoOther other = 8 [(gogoproto.nullable) = false]; +} + +message DefaultNodeInfoOther { + string tx_index = 1; + string rpc_address = 2 [(gogoproto.customname) = "RPCAddress"]; +} diff --git a/ampd/proto/third_party/tendermint/types/block.proto b/ampd/proto/third_party/tendermint/types/block.proto new file mode 100644 index 000000000..84e9bb15d --- /dev/null +++ b/ampd/proto/third_party/tendermint/types/block.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; +package tendermint.types; + +option go_package = "github.com/tendermint/tendermint/proto/tendermint/types"; + +import "gogoproto/gogo.proto"; +import "tendermint/types/types.proto"; +import "tendermint/types/evidence.proto"; + +message Block { + Header header = 1 [(gogoproto.nullable) = false]; + Data data = 2 [(gogoproto.nullable) = false]; + tendermint.types.EvidenceList evidence = 3 [(gogoproto.nullable) = false]; + Commit last_commit = 4; +} diff --git a/ampd/proto/third_party/tendermint/types/evidence.proto b/ampd/proto/third_party/tendermint/types/evidence.proto new file mode 100644 index 000000000..451b8dca3 --- /dev/null +++ b/ampd/proto/third_party/tendermint/types/evidence.proto @@ -0,0 +1,38 @@ +syntax = "proto3"; +package tendermint.types; + +option go_package = "github.com/tendermint/tendermint/proto/tendermint/types"; + +import "gogoproto/gogo.proto"; +import "google/protobuf/timestamp.proto"; +import "tendermint/types/types.proto"; +import "tendermint/types/validator.proto"; + +message Evidence { + oneof sum { + DuplicateVoteEvidence duplicate_vote_evidence = 1; + LightClientAttackEvidence light_client_attack_evidence = 2; + } +} + +// DuplicateVoteEvidence contains evidence of a validator signed two conflicting votes. +message DuplicateVoteEvidence { + tendermint.types.Vote vote_a = 1; + tendermint.types.Vote vote_b = 2; + int64 total_voting_power = 3; + int64 validator_power = 4; + google.protobuf.Timestamp timestamp = 5 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; +} + +// LightClientAttackEvidence contains evidence of a set of validators attempting to mislead a light client. +message LightClientAttackEvidence { + tendermint.types.LightBlock conflicting_block = 1; + int64 common_height = 2; + repeated tendermint.types.Validator byzantine_validators = 3; + int64 total_voting_power = 4; + google.protobuf.Timestamp timestamp = 5 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; +} + +message EvidenceList { + repeated Evidence evidence = 1 [(gogoproto.nullable) = false]; +} diff --git a/ampd/proto/third_party/tendermint/types/params.proto b/ampd/proto/third_party/tendermint/types/params.proto new file mode 100644 index 000000000..0de7d846f --- /dev/null +++ b/ampd/proto/third_party/tendermint/types/params.proto @@ -0,0 +1,80 @@ +syntax = "proto3"; +package tendermint.types; + +option go_package = "github.com/tendermint/tendermint/proto/tendermint/types"; + +import "gogoproto/gogo.proto"; +import "google/protobuf/duration.proto"; + +option (gogoproto.equal_all) = true; + +// ConsensusParams contains consensus critical parameters that determine the +// validity of blocks. +message ConsensusParams { + BlockParams block = 1 [(gogoproto.nullable) = false]; + EvidenceParams evidence = 2 [(gogoproto.nullable) = false]; + ValidatorParams validator = 3 [(gogoproto.nullable) = false]; + VersionParams version = 4 [(gogoproto.nullable) = false]; +} + +// BlockParams contains limits on the block size. +message BlockParams { + // Max block size, in bytes. + // Note: must be greater than 0 + int64 max_bytes = 1; + // Max gas per block. + // Note: must be greater or equal to -1 + int64 max_gas = 2; + // Minimum time increment between consecutive blocks (in milliseconds) If the + // block header timestamp is ahead of the system clock, decrease this value. + // + // Not exposed to the application. + int64 time_iota_ms = 3; +} + +// EvidenceParams determine how we handle evidence of malfeasance. +message EvidenceParams { + // Max age of evidence, in blocks. + // + // The basic formula for calculating this is: MaxAgeDuration / {average block + // time}. + int64 max_age_num_blocks = 1; + + // Max age of evidence, in time. + // + // It should correspond with an app's "unbonding period" or other similar + // mechanism for handling [Nothing-At-Stake + // attacks](https://github.com/ethereum/wiki/wiki/Proof-of-Stake-FAQ#what-is-the-nothing-at-stake-problem-and-how-can-it-be-fixed). + google.protobuf.Duration max_age_duration = 2 + [(gogoproto.nullable) = false, (gogoproto.stdduration) = true]; + + // This sets the maximum size of total evidence in bytes that can be committed in a single block. + // and should fall comfortably under the max block bytes. + // Default is 1048576 or 1MB + int64 max_bytes = 3; +} + +// ValidatorParams restrict the public key types validators can use. +// NOTE: uses ABCI pubkey naming, not Amino names. +message ValidatorParams { + option (gogoproto.populate) = true; + option (gogoproto.equal) = true; + + repeated string pub_key_types = 1; +} + +// VersionParams contains the ABCI application version. +message VersionParams { + option (gogoproto.populate) = true; + option (gogoproto.equal) = true; + + uint64 app_version = 1; +} + +// HashedParams is a subset of ConsensusParams. +// +// It is hashed into the Header.ConsensusHash. +message HashedParams { + int64 block_max_bytes = 1; + int64 block_max_gas = 2; +} diff --git a/ampd/proto/third_party/tendermint/types/types.proto b/ampd/proto/third_party/tendermint/types/types.proto new file mode 100644 index 000000000..3ce169459 --- /dev/null +++ b/ampd/proto/third_party/tendermint/types/types.proto @@ -0,0 +1,157 @@ +syntax = "proto3"; +package tendermint.types; + +option go_package = "github.com/tendermint/tendermint/proto/tendermint/types"; + +import "gogoproto/gogo.proto"; +import "google/protobuf/timestamp.proto"; +import "tendermint/crypto/proof.proto"; +import "tendermint/version/types.proto"; +import "tendermint/types/validator.proto"; + +// BlockIdFlag indicates which BlcokID the signature is for +enum BlockIDFlag { + option (gogoproto.goproto_enum_stringer) = true; + option (gogoproto.goproto_enum_prefix) = false; + + BLOCK_ID_FLAG_UNKNOWN = 0 [(gogoproto.enumvalue_customname) = "BlockIDFlagUnknown"]; + BLOCK_ID_FLAG_ABSENT = 1 [(gogoproto.enumvalue_customname) = "BlockIDFlagAbsent"]; + BLOCK_ID_FLAG_COMMIT = 2 [(gogoproto.enumvalue_customname) = "BlockIDFlagCommit"]; + BLOCK_ID_FLAG_NIL = 3 [(gogoproto.enumvalue_customname) = "BlockIDFlagNil"]; +} + +// SignedMsgType is a type of signed message in the consensus. +enum SignedMsgType { + option (gogoproto.goproto_enum_stringer) = true; + option (gogoproto.goproto_enum_prefix) = false; + + SIGNED_MSG_TYPE_UNKNOWN = 0 [(gogoproto.enumvalue_customname) = "UnknownType"]; + // Votes + SIGNED_MSG_TYPE_PREVOTE = 1 [(gogoproto.enumvalue_customname) = "PrevoteType"]; + SIGNED_MSG_TYPE_PRECOMMIT = 2 [(gogoproto.enumvalue_customname) = "PrecommitType"]; + + // Proposals + SIGNED_MSG_TYPE_PROPOSAL = 32 [(gogoproto.enumvalue_customname) = "ProposalType"]; +} + +// PartsetHeader +message PartSetHeader { + uint32 total = 1; + bytes hash = 2; +} + +message Part { + uint32 index = 1; + bytes bytes = 2; + tendermint.crypto.Proof proof = 3 [(gogoproto.nullable) = false]; +} + +// BlockID +message BlockID { + bytes hash = 1; + PartSetHeader part_set_header = 2 [(gogoproto.nullable) = false]; +} + +// -------------------------------- + +// Header defines the structure of a block header. +message Header { + // basic block info + tendermint.version.Consensus version = 1 [(gogoproto.nullable) = false]; + string chain_id = 2 [(gogoproto.customname) = "ChainID"]; + int64 height = 3; + google.protobuf.Timestamp time = 4 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + + // prev block info + BlockID last_block_id = 5 [(gogoproto.nullable) = false]; + + // hashes of block data + bytes last_commit_hash = 6; // commit from validators from the last block + bytes data_hash = 7; // transactions + + // hashes from the app output from the prev block + bytes validators_hash = 8; // validators for the current block + bytes next_validators_hash = 9; // validators for the next block + bytes consensus_hash = 10; // consensus params for current block + bytes app_hash = 11; // state after txs from the previous block + bytes last_results_hash = 12; // root hash of all results from the txs from the previous block + + // consensus info + bytes evidence_hash = 13; // evidence included in the block + bytes proposer_address = 14; // original proposer of the block +} + +// Data contains the set of transactions included in the block +message Data { + // Txs that will be applied by state @ block.Height+1. + // NOTE: not all txs here are valid. We're just agreeing on the order first. + // This means that block.AppHash does not include these txs. + repeated bytes txs = 1; +} + +// Vote represents a prevote, precommit, or commit vote from validators for +// consensus. +message Vote { + SignedMsgType type = 1; + int64 height = 2; + int32 round = 3; + BlockID block_id = 4 + [(gogoproto.nullable) = false, (gogoproto.customname) = "BlockID"]; // zero if vote is nil. + google.protobuf.Timestamp timestamp = 5 + [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + bytes validator_address = 6; + int32 validator_index = 7; + bytes signature = 8; +} + +// Commit contains the evidence that a block was committed by a set of validators. +message Commit { + int64 height = 1; + int32 round = 2; + BlockID block_id = 3 [(gogoproto.nullable) = false, (gogoproto.customname) = "BlockID"]; + repeated CommitSig signatures = 4 [(gogoproto.nullable) = false]; +} + +// CommitSig is a part of the Vote included in a Commit. +message CommitSig { + BlockIDFlag block_id_flag = 1; + bytes validator_address = 2; + google.protobuf.Timestamp timestamp = 3 + [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + bytes signature = 4; +} + +message Proposal { + SignedMsgType type = 1; + int64 height = 2; + int32 round = 3; + int32 pol_round = 4; + BlockID block_id = 5 [(gogoproto.customname) = "BlockID", (gogoproto.nullable) = false]; + google.protobuf.Timestamp timestamp = 6 + [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + bytes signature = 7; +} + +message SignedHeader { + Header header = 1; + Commit commit = 2; +} + +message LightBlock { + SignedHeader signed_header = 1; + tendermint.types.ValidatorSet validator_set = 2; +} + +message BlockMeta { + BlockID block_id = 1 [(gogoproto.customname) = "BlockID", (gogoproto.nullable) = false]; + int64 block_size = 2; + Header header = 3 [(gogoproto.nullable) = false]; + int64 num_txs = 4; +} + +// TxProof represents a Merkle proof of the presence of a transaction in the Merkle tree. +message TxProof { + bytes root_hash = 1; + bytes data = 2; + tendermint.crypto.Proof proof = 3; +} diff --git a/ampd/proto/third_party/tendermint/types/validator.proto b/ampd/proto/third_party/tendermint/types/validator.proto new file mode 100644 index 000000000..49860b96d --- /dev/null +++ b/ampd/proto/third_party/tendermint/types/validator.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; +package tendermint.types; + +option go_package = "github.com/tendermint/tendermint/proto/tendermint/types"; + +import "gogoproto/gogo.proto"; +import "tendermint/crypto/keys.proto"; + +message ValidatorSet { + repeated Validator validators = 1; + Validator proposer = 2; + int64 total_voting_power = 3; +} + +message Validator { + bytes address = 1; + tendermint.crypto.PublicKey pub_key = 2 [(gogoproto.nullable) = false]; + int64 voting_power = 3; + int64 proposer_priority = 4; +} + +message SimpleValidator { + tendermint.crypto.PublicKey pub_key = 1; + int64 voting_power = 2; +} diff --git a/ampd/proto/third_party/tendermint/version/types.proto b/ampd/proto/third_party/tendermint/version/types.proto new file mode 100644 index 000000000..6061868bd --- /dev/null +++ b/ampd/proto/third_party/tendermint/version/types.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; +package tendermint.version; + +option go_package = "github.com/tendermint/tendermint/proto/tendermint/version"; + +import "gogoproto/gogo.proto"; + +// App includes the protocol and software version for the application. +// This information is included in ResponseInfo. The App.Protocol can be +// updated in ResponseEndBlock. +message App { + uint64 protocol = 1; + string software = 2; +} + +// Consensus captures the consensus rules for processing a block in the blockchain, +// including all blockchain data structures and the rules of the application's +// state transition machine. +message Consensus { + option (gogoproto.equal) = true; + + uint64 block = 1; + uint64 app = 2; +} diff --git a/ampd/proto/tofnd/common.proto b/ampd/proto/tofnd/common.proto index 0ddf272e2..12793c106 100644 --- a/ampd/proto/tofnd/common.proto +++ b/ampd/proto/tofnd/common.proto @@ -1,20 +1,29 @@ syntax = "proto3"; +option go_package = "tofnd;tofnd"; + package tofnd; +enum Algorithm { + ALGORITHM_ECDSA = 0; + ALGORITHM_ED25519 = 1; +} + // Key presence check types message KeyPresenceRequest { - string key_uid = 1; - bytes pub_key = 2; // SEC1-encoded compressed pub key bytes to find the right mnemonic. Latest is used, if empty. + string key_uid = 1; + bytes pub_key = 2; // SEC1-encoded compressed pub key bytes to find the right + // mnemonic. Latest is used, if empty. + Algorithm algorithm = 3; } message KeyPresenceResponse { - enum Response { - RESPONSE_UNSPECIFIED = 0; - RESPONSE_PRESENT = 1; - RESPONSE_ABSENT = 2; - RESPONSE_FAIL = 3; - } + enum Response { + RESPONSE_UNSPECIFIED = 0; + RESPONSE_PRESENT = 1; + RESPONSE_ABSENT = 2; + RESPONSE_FAIL = 3; + } - Response response = 1; + Response response = 1; } diff --git a/ampd/proto/tofnd/multisig.proto b/ampd/proto/tofnd/multisig.proto index c7ce22977..f6b4cc0b8 100644 --- a/ampd/proto/tofnd/multisig.proto +++ b/ampd/proto/tofnd/multisig.proto @@ -5,33 +5,36 @@ package tofnd; import "common.proto"; // import key presence request/response service Multisig { - rpc KeyPresence(KeyPresenceRequest) returns (KeyPresenceResponse); - rpc Keygen(KeygenRequest) returns (KeygenResponse); - rpc Sign(SignRequest) returns (SignResponse); + rpc KeyPresence(KeyPresenceRequest) returns (KeyPresenceResponse); + rpc Keygen(KeygenRequest) returns (KeygenResponse); + rpc Sign(SignRequest) returns (SignResponse); } message KeygenRequest { - string key_uid = 1; - string party_uid = 2; // used only for logging + string key_uid = 1; + string party_uid = 2; // used only for logging + Algorithm algorithm = 3; } -message KeygenResponse { - oneof keygen_response { - bytes pub_key = 1; // SEC1-encoded compressed curve point - string error = 2; // reply with an error message if keygen fails - } +message KeygenResponse { + oneof keygen_response { + bytes pub_key = 1; // SEC1-encoded compressed curve point + string error = 2; // reply with an error message if keygen fails + } } message SignRequest { - string key_uid = 1; - bytes msg_to_sign = 2; // 32-byte pre-hashed message digest - string party_uid = 3; // used only for logging - bytes pub_key = 4; // SEC1-encoded compressed pub key bytes to find the right mnemonic. Latest is used, if empty. + string key_uid = 1; + bytes msg_to_sign = 2; // 32-byte pre-hashed message digest + string party_uid = 3; // used only for logging + bytes pub_key = 4; // SEC1-encoded compressed pub key bytes to find the right + // mnemonic. Latest is used, if empty. + Algorithm algorithm = 5; } -message SignResponse { - oneof sign_response { - bytes signature = 1; // ASN.1 DER-encoded ECDSA signature - string error = 2; // reply with an error message if sign fails - } +message SignResponse { + oneof sign_response { + bytes signature = 1; // ASN.1 DER-encoded ECDSA signature + string error = 2; // reply with an error message if sign fails + } } diff --git a/ampd/src/asyncutil/future.rs b/ampd/src/asyncutil/future.rs index 5e0eb672a..645ff6b43 100644 --- a/ampd/src/asyncutil/future.rs +++ b/ampd/src/asyncutil/future.rs @@ -1,107 +1,59 @@ -use futures::{Future, FutureExt}; -use std::pin::Pin; -use std::task::{Context, Poll}; use std::time::Duration; +use futures::Future; use tokio::time; -pub fn with_retry( - future: F, - policy: RetryPolicy, -) -> impl Future> +pub async fn with_retry(mut future: F, policy: RetryPolicy) -> Result where - F: Fn() -> Fut, + F: FnMut() -> Fut, Fut: Future>, { - RetriableFuture::new(future, policy) -} - -pub enum RetryPolicy { - RepeatConstant { sleep: Duration, max_attempts: u64 }, -} - -struct RetriableFuture -where - F: Fn() -> Fut, - Fut: Future>, -{ - future: F, - inner: Pin>, - policy: RetryPolicy, - err_count: u64, -} - -impl Unpin for RetriableFuture -where - F: Fn() -> Fut, - Fut: Future>, -{ -} - -impl RetriableFuture -where - F: Fn() -> Fut, - Fut: Future>, -{ - fn new(get_future: F, policy: RetryPolicy) -> Self { - let future = get_future(); - - Self { - future: get_future, - inner: Box::pin(future), - policy, - err_count: 0, + let mut attempt_count = 0u64; + loop { + match future().await { + Ok(result) => return Ok(result), + Err(err) => { + attempt_count = attempt_count.saturating_add(1); + + match enact_policy(attempt_count, policy).await { + PolicyAction::Retry => continue, + PolicyAction::Abort => return Err(err), + } + } } } +} - fn handle_err( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - error: Err, - ) -> Poll> { - self.err_count = self.err_count.saturating_add(1); - - match self.policy { - RetryPolicy::RepeatConstant { - sleep, - max_attempts, - } => { - if self.err_count >= max_attempts { - return Poll::Ready(Err(error)); - } - - self.inner = Box::pin((self.future)()); - - let waker = cx.waker().clone(); - tokio::spawn(time::sleep(sleep).then(|_| async { - waker.wake(); - })); - - Poll::Pending +async fn enact_policy(attempt_count: u64, policy: RetryPolicy) -> PolicyAction { + match policy { + RetryPolicy::RepeatConstant { + sleep, + max_attempts, + } => { + if attempt_count >= max_attempts { + PolicyAction::Abort + } else { + time::sleep(sleep).await; + PolicyAction::Retry } } } } -impl Future for RetriableFuture -where - F: Fn() -> Fut, - Fut: Future>, -{ - type Output = Result; +enum PolicyAction { + Retry, + Abort, +} - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.inner.as_mut().poll(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(Ok(result)) => Poll::Ready(Ok(result)), - Poll::Ready(Err(error)) => self.handle_err(cx, error), - } - } +#[derive(Copy, Clone)] +pub enum RetryPolicy { + RepeatConstant { sleep: Duration, max_attempts: u64 }, } #[cfg(test)] mod tests { - use std::{future, sync::Mutex}; + use std::future; + use std::sync::Mutex; use tokio::time::Instant; diff --git a/ampd/src/asyncutil/task.rs b/ampd/src/asyncutil/task.rs index 1eae600d4..39d6f424a 100644 --- a/ampd/src/asyncutil/task.rs +++ b/ampd/src/asyncutil/task.rs @@ -1,7 +1,8 @@ -use axelar_wasm_std::error::extend_err; -use error_stack::{Context, Result, ResultExt}; use std::future::Future; use std::pin::Pin; + +use axelar_wasm_std::error::extend_err; +use error_stack::{Context, Result, ResultExt}; use thiserror::Error; use tokio::task::JoinSet; use tokio_util::sync::CancellationToken; @@ -109,10 +110,11 @@ pub struct TaskError; #[cfg(test)] mod test { - use crate::asyncutil::task::{CancellableTask, TaskError, TaskGroup}; use error_stack::report; use tokio_util::sync::CancellationToken; + use crate::asyncutil::task::{CancellableTask, TaskError, TaskGroup}; + #[tokio::test] async fn running_no_tasks_returns_no_error() { let tasks: TaskGroup = TaskGroup::new(); diff --git a/ampd/src/block_height_monitor.rs b/ampd/src/block_height_monitor.rs index 5ed7e38f4..5a80cf670 100644 --- a/ampd/src/block_height_monitor.rs +++ b/ampd/src/block_height_monitor.rs @@ -1,11 +1,9 @@ -use error_stack::{Result, ResultExt}; use std::time::Duration; + +use error_stack::{Result, ResultExt}; use thiserror::Error; -use tokio::{ - select, - sync::watch::{self, Receiver, Sender}, - time, -}; +use tokio::sync::watch::{self, Receiver, Sender}; +use tokio::{select, time}; use tokio_util::sync::CancellationToken; use tracing::info; @@ -66,7 +64,6 @@ impl BlockHeightMonitor { } } - #[allow(dead_code)] pub fn latest_block_height(&self) -> Receiver { self.latest_height_rx.clone() } @@ -77,18 +74,16 @@ mod tests { use std::convert::TryInto; use std::time::Duration; + use async_trait::async_trait; use mockall::mock; use tendermint::block::Height; - use tokio::test; - use tokio::time; + use tokio::{test, time}; use tokio_util::sync::CancellationToken; - use crate::tm_client; - - use crate::BlockHeightMonitor; - use async_trait::async_trait; + use crate::{tm_client, BlockHeightMonitor}; #[test] + #[allow(clippy::cast_possible_truncation)] async fn latest_block_height_should_work() { let block: tendermint::Block = serde_json::from_str(include_str!("tests/axelar_block.json")).unwrap(); diff --git a/ampd/src/broadcaster.rs b/ampd/src/broadcaster.rs deleted file mode 100644 index 5348a3930..000000000 --- a/ampd/src/broadcaster.rs +++ /dev/null @@ -1,830 +0,0 @@ -use std::convert::TryInto; -use std::ops::Mul; -use std::time::Duration; -use std::{cmp, thread}; - -use async_trait::async_trait; -use cosmos_sdk_proto::cosmos::base::abci::v1beta1::TxResponse; -use cosmos_sdk_proto::cosmos::tx::v1beta1::{ - BroadcastMode, BroadcastTxRequest, GetTxRequest, GetTxResponse, SimulateRequest, -}; -use cosmos_sdk_proto::traits::MessageExt; -use cosmrs::tendermint::chain::Id; -use cosmrs::tx::Fee; -use cosmrs::{Coin, Gas}; -use error_stack::{FutureExt, Report, Result, ResultExt}; -use futures::TryFutureExt; -use k256::sha2::{Digest, Sha256}; -use mockall::automock; -use num_traits::cast; -use serde::{Deserialize, Serialize}; -use thiserror::Error; -use tonic::Status; -use tracing::debug; -use tracing::info; -use typed_builder::TypedBuilder; -use valuable::Valuable; - -use dec_coin::DecCoin; -use report::LoggableError; -use tx::Tx; - -use crate::tofnd::grpc::SharableEcdsaClient; -use crate::types::{PublicKey, TMAddress}; - -pub mod accounts; -pub mod clients; -mod dec_coin; -mod tx; - -#[derive(Error, Debug)] -pub enum Error { - #[error("failed building tx")] - TxBuilding, - #[error("failed to estimate gas")] - GasEstimation, - #[error("failed to estimate fee")] - FeeEstimation, - #[error("broadcast failed")] - Broadcast, - #[error("failed to confirm tx inclusion in block")] - TxConfirmation, - #[error("failed to execute tx")] - Execution { response: TxResponse }, - #[error("failed to query account information for address {address}")] - QueryAccount { address: TMAddress }, -} - -#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] -pub struct Config { - pub chain_id: Id, - #[serde(with = "humantime_serde")] - pub tx_fetch_interval: Duration, - pub tx_fetch_max_retries: u32, - pub gas_adjustment: f64, - pub gas_price: DecCoin, - pub batch_gas_limit: Gas, - pub queue_cap: usize, - #[serde(with = "humantime_serde")] - pub broadcast_interval: Duration, -} - -impl Default for Config { - fn default() -> Self { - Self { - chain_id: "axelar-dojo-1".parse().unwrap(), - tx_fetch_interval: Duration::from_millis(500), - tx_fetch_max_retries: 10, - gas_adjustment: 1.0, - gas_price: DecCoin::new(0.00005, "uaxl").unwrap(), - batch_gas_limit: 1000000, - queue_cap: 1000, - broadcast_interval: Duration::from_secs(5), - } - } -} - -#[automock] -#[async_trait] -pub trait Broadcaster { - async fn broadcast(&mut self, msgs: Vec) -> Result; - async fn estimate_fee(&mut self, msgs: Vec) -> Result; -} - -#[derive(TypedBuilder)] -pub struct BroadcastClient { - client: T, - signer: SharableEcdsaClient, - query_client: Q, - address: TMAddress, - #[builder(default, setter(skip))] - acc_sequence: Option, - pub_key: (String, PublicKey), - config: Config, -} - -#[async_trait] -impl Broadcaster for BroadcastClient -where - T: clients::BroadcastClient + Send, - Q: clients::AccountQueryClient + Send, -{ - async fn broadcast(&mut self, msgs: Vec) -> Result { - let (acc_number, acc_sequence) = self.acc_number_and_sequence().await?; - let tx = Tx::builder() - .msgs(msgs.clone()) - .fee(self.estimate_fee(msgs, acc_sequence).await?) - .pub_key(self.pub_key.1) - .acc_sequence(acc_sequence) - .build() - .sign_with(&self.config.chain_id, acc_number, |sign_doc| { - let mut hasher = Sha256::new(); - hasher.update(sign_doc); - - let sign_digest: [u8; 32] = hasher - .finalize() - .to_vec() - .try_into() - .expect("hash size must be 32"); - - self.signer - .sign(self.pub_key.0.as_str(), sign_digest.into(), &self.pub_key.1) - }) - .await - .change_context(Error::TxBuilding)?; - - let tx = BroadcastTxRequest { - tx_bytes: tx.to_bytes().change_context(Error::TxBuilding)?, - mode: BroadcastMode::Sync as i32, - }; - - let response = self - .client - .broadcast_tx(tx) - .change_context(Error::Broadcast) - .await?; - let TxResponse { - txhash: tx_hash, .. - } = &response; - - info!(tx_hash, "broadcasted transaction"); - - self.confirm_tx(tx_hash).await?; - - info!(tx_hash, "confirmed transaction"); - - self.acc_sequence.replace( - acc_sequence - .checked_add(1) - .expect("account sequence must be less than u64::MAX"), - ); - Ok(response) - } - - async fn estimate_fee(&mut self, msgs: Vec) -> Result { - let (_, acc_sequence) = self.acc_number_and_sequence().await?; - - self.estimate_fee(msgs, acc_sequence).await - } -} - -impl BroadcastClient -where - T: clients::BroadcastClient, - Q: clients::AccountQueryClient, -{ - async fn acc_number_and_sequence(&mut self) -> Result<(u64, u64), Error> { - let account = accounts::account(&mut self.query_client, &self.address) - .await - .change_context_lazy(|| Error::QueryAccount { - address: self.address.clone(), - })?; - - let acc_sequence = self.acc_sequence.insert(cmp::max( - account.sequence, - self.acc_sequence.unwrap_or_default(), - )); - - Ok((account.account_number, *acc_sequence)) - } - - async fn estimate_fee( - &mut self, - msgs: Vec, - acc_sequence: u64, - ) -> Result { - let sim_tx = Tx::builder() - .msgs(msgs) - .pub_key(self.pub_key.1) - .acc_sequence(acc_sequence) - .build() - .with_dummy_sig() - .await - .change_context(Error::TxBuilding)? - .to_bytes() - .change_context(Error::TxBuilding)?; - - self.estimate_gas(sim_tx).await.map(|gas| { - let gas_adj = gas as f64 * self.config.gas_adjustment; - - Ok(Fee::from_amount_and_gas( - Coin { - amount: cast((gas_adj.mul(self.config.gas_price.amount)).ceil()) - .ok_or(Error::FeeEstimation)?, - denom: self.config.gas_price.denom.clone().into(), - }, - cast::(gas_adj).ok_or(Error::FeeEstimation)?, - )) - })? - } - - async fn estimate_gas(&mut self, tx_bytes: Vec) -> Result { - #[allow(deprecated)] - self.client - .simulate(SimulateRequest { tx: None, tx_bytes }) - .change_context(Error::GasEstimation) - .and_then(|response| async { - response - .gas_info - .map(|info| info.gas_used) - .ok_or(Error::GasEstimation.into()) - }) - .await - } - - async fn confirm_tx(&mut self, tx_hash: &str) -> Result<(), Error> { - let mut result: Result<(), Status> = Ok(()); - - for i in 0..self.config.tx_fetch_max_retries.saturating_add(1) { - if i > 0 { - thread::sleep(self.config.tx_fetch_interval) - } - - let response = self - .client - .get_tx(GetTxRequest { - hash: tx_hash.to_string(), - }) - .await; - - match evaluate_response(response) { - ConfirmationResult::Success => { - if let Err(report) = result { - debug!( - err = LoggableError::from(&report).as_value(), - "tx confirmed after {} retries", i - ) - } - - return Ok(()); - } - ConfirmationResult::Critical(err) => return Err(err.into()), - ConfirmationResult::Retriable(err) => { - if let Err(result) = result.as_mut() { - result.extend_one(err); - } else { - result = Err(err); - } - } - }; - } - - result.change_context(Error::TxConfirmation) - } -} - -fn evaluate_response(response: Result) -> ConfirmationResult { - match response { - Err(err) => ConfirmationResult::Retriable(err), - Ok(GetTxResponse { - tx_response: None, .. - }) => ConfirmationResult::Retriable(Report::new(Status::not_found("tx not found"))), - Ok(GetTxResponse { - tx_response: Some(response), - .. - }) => match response { - TxResponse { code: 0, .. } => ConfirmationResult::Success, - _ => ConfirmationResult::Critical(Error::Execution { response }), - }, - } -} - -enum ConfirmationResult { - Success, - Retriable(Report), - Critical(Error), -} - -#[cfg(test)] -mod tests { - use cosmos_sdk_proto::cosmos::auth::v1beta1::{BaseAccount, QueryAccountResponse}; - use cosmos_sdk_proto::cosmos::base::abci::v1beta1::{GasInfo, TxResponse}; - use cosmos_sdk_proto::cosmos::tx::v1beta1::{GetTxResponse, SimulateResponse}; - use cosmos_sdk_proto::traits::MessageExt; - use cosmos_sdk_proto::Any; - use cosmrs::{bank::MsgSend, tx::Msg, AccountId}; - use ecdsa::SigningKey; - use rand::rngs::OsRng; - use tokio::test; - use tonic::Status; - - use crate::broadcaster::clients::{MockAccountQueryClient, MockBroadcastClient}; - use crate::broadcaster::{BroadcastClient, Broadcaster, Config, Error}; - use crate::tofnd::grpc::{MockEcdsaClient, SharableEcdsaClient}; - use crate::types::{PublicKey, TMAddress}; - use crate::PREFIX; - - #[test] - async fn gas_estimation_call_failed() { - let key_id = "key_uid"; - let priv_key = SigningKey::random(&mut OsRng); - let pub_key: PublicKey = priv_key.verifying_key().into(); - let address: TMAddress = pub_key - .account_id(PREFIX) - .expect("failed to convert to account identifier") - .into(); - let account = BaseAccount { - address: address.to_string(), - pub_key: None, - account_number: 7, - sequence: 0, - }; - - let mut client = MockBroadcastClient::new(); - client - .expect_simulate() - .returning(|_| Err(Status::unavailable("unavailable service").into())); - - let signer = MockEcdsaClient::new(); - - let mut query_client = MockAccountQueryClient::new(); - query_client.expect_account().returning(move |_| { - Ok(QueryAccountResponse { - account: Some(account.to_any().unwrap()), - }) - }); - - let mut broadcaster = BroadcastClient::builder() - .client(client) - .signer(SharableEcdsaClient::new(signer)) - .query_client(query_client) - .address(address) - .pub_key((key_id.to_string(), pub_key)) - .config(Config::default()) - .build(); - let msgs = vec![dummy_msg()]; - - assert!(matches!( - broadcaster - .broadcast(msgs) - .await - .unwrap_err() - .current_context(), - Error::GasEstimation - )); - } - - #[test] - async fn gas_estimation_none_response() { - let key_id = "key_uid"; - let priv_key = SigningKey::random(&mut OsRng); - let pub_key: PublicKey = priv_key.verifying_key().into(); - let address: TMAddress = pub_key - .account_id(PREFIX) - .expect("failed to convert to account identifier") - .into(); - let account = BaseAccount { - address: address.to_string(), - pub_key: None, - account_number: 7, - sequence: 0, - }; - - let mut client = MockBroadcastClient::new(); - client.expect_simulate().returning(|_| { - Ok(SimulateResponse { - gas_info: None, - result: None, - }) - }); - - let signer = MockEcdsaClient::new(); - - let mut query_client = MockAccountQueryClient::new(); - query_client.expect_account().returning(move |_| { - Ok(QueryAccountResponse { - account: Some(account.to_any().unwrap()), - }) - }); - - let mut broadcaster = BroadcastClient::builder() - .client(client) - .signer(SharableEcdsaClient::new(signer)) - .query_client(query_client) - .address(address) - .pub_key((key_id.to_string(), pub_key)) - .config(Config::default()) - .build(); - let msgs = vec![dummy_msg()]; - - assert!(matches!( - broadcaster - .broadcast(msgs) - .await - .unwrap_err() - .current_context(), - Error::GasEstimation - )); - } - - #[test] - async fn broadcast_failed() { - let key_id = "key_uid"; - let priv_key = SigningKey::random(&mut OsRng); - let pub_key: PublicKey = priv_key.verifying_key().into(); - let address: TMAddress = pub_key - .account_id(PREFIX) - .expect("failed to convert to account identifier") - .into(); - let account = BaseAccount { - address: address.to_string(), - pub_key: None, - account_number: 7, - sequence: 0, - }; - - let mut client = MockBroadcastClient::new(); - client.expect_simulate().returning(|_| { - Ok(SimulateResponse { - gas_info: Some(GasInfo { - gas_wanted: 0, - gas_used: 0, - }), - result: None, - }) - }); - client - .expect_broadcast_tx() - .returning(|_| Err(Status::aborted("failed").into())); - - let mut signer = MockEcdsaClient::new(); - signer - .expect_sign() - .once() - .returning(move |actual_key_uid, data, actual_pub_key| { - assert_eq!(actual_key_uid, key_id); - assert_eq!(actual_pub_key, &pub_key); - - let (signature, _) = priv_key - .sign_prehash_recoverable(>::from(data).as_slice()) - .unwrap(); - - Ok(signature.to_vec()) - }); - - let mut query_client = MockAccountQueryClient::new(); - query_client.expect_account().returning(move |_| { - Ok(QueryAccountResponse { - account: Some(account.to_any().unwrap()), - }) - }); - - let mut broadcaster = BroadcastClient::builder() - .client(client) - .signer(SharableEcdsaClient::new(signer)) - .query_client(query_client) - .address(address) - .pub_key((key_id.to_string(), pub_key)) - .config(Config::default()) - .build(); - let msgs = vec![dummy_msg()]; - - assert!(matches!( - broadcaster - .broadcast(msgs) - .await - .unwrap_err() - .current_context(), - Error::Broadcast - )); - } - - #[test] - async fn tx_confirmation_failed() { - let key_id = "key_uid"; - let priv_key = SigningKey::random(&mut OsRng); - let pub_key: PublicKey = priv_key.verifying_key().into(); - let address: TMAddress = pub_key - .account_id(PREFIX) - .expect("failed to convert to account identifier") - .into(); - let account = BaseAccount { - address: address.to_string(), - pub_key: None, - account_number: 7, - sequence: 0, - }; - - let mut client = MockBroadcastClient::new(); - client.expect_simulate().returning(|_| { - Ok(SimulateResponse { - gas_info: Some(GasInfo { - gas_wanted: 0, - gas_used: 0, - }), - result: None, - }) - }); - client - .expect_broadcast_tx() - .returning(|_| Ok(TxResponse::default())); - client - .expect_get_tx() - .times((Config::default().tx_fetch_max_retries + 1) as usize) - .returning(|_| Err(Status::deadline_exceeded("time out").into())); - - let mut signer = MockEcdsaClient::new(); - signer - .expect_sign() - .once() - .returning(move |actual_key_uid, data, actual_pub_key| { - assert_eq!(actual_key_uid, key_id); - assert_eq!(actual_pub_key, &pub_key); - - let (signature, _) = priv_key - .sign_prehash_recoverable(>::from(data).as_slice()) - .unwrap(); - - Ok(signature.to_vec()) - }); - - let mut query_client = MockAccountQueryClient::new(); - query_client.expect_account().returning(move |_| { - Ok(QueryAccountResponse { - account: Some(account.to_any().unwrap()), - }) - }); - - let mut broadcaster = BroadcastClient::builder() - .client(client) - .signer(SharableEcdsaClient::new(signer)) - .query_client(query_client) - .address(address) - .pub_key((key_id.to_string(), pub_key)) - .config(Config::default()) - .build(); - let msgs = vec![dummy_msg()]; - - assert!(matches!( - broadcaster - .broadcast(msgs) - .await - .unwrap_err() - .current_context(), - Error::TxConfirmation - )); - } - - #[test] - async fn tx_execution_failed() { - let key_id = "key_uid"; - let priv_key = SigningKey::random(&mut OsRng); - let pub_key: PublicKey = priv_key.verifying_key().into(); - let address: TMAddress = pub_key - .account_id(PREFIX) - .expect("failed to convert to account identifier") - .into(); - let account = BaseAccount { - address: address.to_string(), - pub_key: None, - account_number: 7, - sequence: 0, - }; - - let mut client = MockBroadcastClient::new(); - client.expect_simulate().returning(|_| { - Ok(SimulateResponse { - gas_info: Some(GasInfo { - gas_wanted: 0, - gas_used: 0, - }), - result: None, - }) - }); - client - .expect_broadcast_tx() - .returning(|_| Ok(TxResponse::default())); - client.expect_get_tx().times(1).returning(|_| { - Ok(GetTxResponse { - tx_response: Some(TxResponse { - code: 32, - ..TxResponse::default() - }), - ..GetTxResponse::default() - }) - }); - - let mut signer = MockEcdsaClient::new(); - signer - .expect_sign() - .once() - .returning(move |actual_key_uid, data, actual_pub_key| { - assert_eq!(actual_key_uid, key_id); - assert_eq!(actual_pub_key, &pub_key); - - let (signature, _) = priv_key - .sign_prehash_recoverable(>::from(data).as_slice()) - .unwrap(); - - Ok(signature.to_vec()) - }); - - let mut query_client = MockAccountQueryClient::new(); - query_client.expect_account().returning(move |_| { - Ok(QueryAccountResponse { - account: Some(account.to_any().unwrap()), - }) - }); - - let mut broadcaster = BroadcastClient::builder() - .client(client) - .signer(SharableEcdsaClient::new(signer)) - .query_client(query_client) - .address(address) - .pub_key((key_id.to_string(), pub_key)) - .config(Config::default()) - .build(); - let msgs = vec![dummy_msg()]; - - assert!(matches!( - broadcaster - .broadcast(msgs) - .await - .unwrap_err() - .current_context(), - Error::Execution { - response: TxResponse { code: 32, .. } - } - )); - } - - #[test] - async fn broadcast_confirmed() { - let key_id = "key_uid"; - let priv_key = SigningKey::random(&mut OsRng); - let pub_key: PublicKey = priv_key.verifying_key().into(); - let address: TMAddress = pub_key - .account_id(PREFIX) - .expect("failed to convert to account identifier") - .into(); - let account = BaseAccount { - address: address.to_string(), - pub_key: None, - account_number: 7, - sequence: 0, - }; - - let mut client = MockBroadcastClient::new(); - client.expect_simulate().returning(|_| { - Ok(SimulateResponse { - gas_info: Some(GasInfo { - gas_wanted: 0, - gas_used: 0, - }), - result: None, - }) - }); - client - .expect_broadcast_tx() - .returning(|_| Ok(TxResponse::default())); - client.expect_get_tx().returning(|_| { - Ok(GetTxResponse { - tx_response: Some(TxResponse { - code: 0, - ..TxResponse::default() - }), - ..GetTxResponse::default() - }) - }); - - let mut signer = MockEcdsaClient::new(); - signer - .expect_sign() - .once() - .returning(move |actual_key_uid, data, actual_pub_key| { - assert_eq!(actual_key_uid, key_id); - assert_eq!(actual_pub_key, &pub_key); - - let (signature, _) = priv_key - .sign_prehash_recoverable(>::from(data).as_slice()) - .unwrap(); - - Ok(signature.to_vec()) - }); - - let mut query_client = MockAccountQueryClient::new(); - query_client.expect_account().returning(move |_| { - Ok(QueryAccountResponse { - account: Some(account.to_any().unwrap()), - }) - }); - - let mut broadcaster = BroadcastClient::builder() - .client(client) - .signer(SharableEcdsaClient::new(signer)) - .query_client(query_client) - .address(address) - .pub_key((key_id.to_string(), pub_key)) - .config(Config::default()) - .build(); - let msgs = vec![dummy_msg()]; - - assert_eq!(broadcaster.acc_sequence, None); - assert!(broadcaster.broadcast(msgs).await.is_ok()); - assert_eq!(broadcaster.acc_sequence, Some(1)); - } - - #[test] - async fn broadcast_confirmed_in_mem_acc_sequence_mismatch_with_on_chain() { - let key_id = "key_uid"; - let priv_key = SigningKey::random(&mut OsRng); - let pub_key: PublicKey = priv_key.verifying_key().into(); - let address: TMAddress = pub_key - .account_id(PREFIX) - .expect("failed to convert to account identifier") - .into(); - let mut account = BaseAccount { - address: address.to_string(), - pub_key: None, - account_number: 7, - sequence: 0, - }; - - let mut client = MockBroadcastClient::new(); - client.expect_simulate().returning(|_| { - Ok(SimulateResponse { - gas_info: Some(GasInfo { - gas_wanted: 0, - gas_used: 0, - }), - result: None, - }) - }); - client - .expect_broadcast_tx() - .returning(|_| Ok(TxResponse::default())); - client.expect_get_tx().returning(|_| { - Ok(GetTxResponse { - tx_response: Some(TxResponse { - code: 0, - ..TxResponse::default() - }), - ..GetTxResponse::default() - }) - }); - - let mut signer = MockEcdsaClient::new(); - signer - .expect_sign() - .times(3) - .returning(move |actual_key_uid, data, actual_pub_key| { - assert_eq!(actual_key_uid, key_id); - assert_eq!(actual_pub_key, &pub_key); - - let (signature, _) = priv_key - .sign_prehash_recoverable(>::from(data).as_slice()) - .unwrap(); - - Ok(signature.to_vec()) - }); - - let mut query_client = MockAccountQueryClient::new(); - let mut call_count = 0; - query_client.expect_account().returning(move |_| { - call_count += 1; - - match call_count { - 1 => { - account.sequence = 0; - } - 2 => { - account.sequence = 10; - } - _ => { - account.sequence = 0; - } - } - - Ok(QueryAccountResponse { - account: Some(account.to_any().unwrap()), - }) - }); - - let mut broadcaster = BroadcastClient::builder() - .client(client) - .signer(SharableEcdsaClient::new(signer)) - .query_client(query_client) - .address(address) - .pub_key((key_id.to_string(), pub_key)) - .config(Config::default()) - .build(); - - assert_eq!(broadcaster.acc_sequence, None); - assert!(broadcaster.broadcast(vec![dummy_msg()]).await.is_ok()); - assert_eq!(broadcaster.acc_sequence, Some(1)); - assert!(broadcaster.broadcast(vec![dummy_msg()]).await.is_ok()); - assert_eq!(broadcaster.acc_sequence, Some(11)); - assert!(broadcaster.broadcast(vec![dummy_msg()]).await.is_ok()); - assert_eq!(broadcaster.acc_sequence, Some(12)); - } - - fn dummy_msg() -> Any { - MsgSend { - from_address: AccountId::new("", &[1, 2, 3]).unwrap(), - to_address: AccountId::new("", &[4, 5, 6]).unwrap(), - amount: vec![], - } - .to_any() - .unwrap() - } -} diff --git a/ampd/src/broadcaster/accounts.rs b/ampd/src/broadcaster/accounts.rs deleted file mode 100644 index 93b3e4734..000000000 --- a/ampd/src/broadcaster/accounts.rs +++ /dev/null @@ -1,158 +0,0 @@ -use cosmos_sdk_proto::cosmos::auth::v1beta1::{BaseAccount, QueryAccountRequest}; -use cosmos_sdk_proto::traits::Message; -use error_stack::{Result, ResultExt}; -use thiserror::Error; - -use crate::broadcaster::clients::AccountQueryClient; -use crate::types::TMAddress; - -#[derive(Error, Debug)] -pub enum Error { - #[error("failed to retrieve the account information for address {address}")] - ResponseFailed { address: TMAddress }, - #[error("address {address} is unknown")] - AccountNotFound { address: TMAddress }, - #[error("received response could not be decoded")] - MalformedResponse, -} - -pub async fn account(client: &mut T, address: &TMAddress) -> Result -where - T: AccountQueryClient, -{ - let response = client - .account(QueryAccountRequest { - address: address.to_string(), - }) - .await - .map_err(|report| match report.current_context().code() { - tonic::Code::NotFound => { - report.attach_printable("to proceed, please ensure the account is funded") - } - _ => report, - }) - .change_context_lazy(|| Error::ResponseFailed { - address: address.clone(), - })?; - - let account = response - .account - .ok_or_else(|| { - Error::AccountNotFound { - address: address.clone(), - } - .into() - }) - .and_then(|account| { - BaseAccount::decode(&account.value[..]) - .change_context(Error::MalformedResponse) - .attach_printable_lazy(|| format!("{{ value = {:?} }}", account.value)) - })?; - - Ok(account) -} - -#[cfg(test)] -mod tests { - use cosmos_sdk_proto::cosmos::auth::v1beta1::BaseAccount; - use cosmos_sdk_proto::cosmos::auth::v1beta1::QueryAccountResponse; - use cosmos_sdk_proto::traits::MessageExt; - use cosmrs::Any; - use ecdsa::SigningKey; - use rand::rngs::OsRng; - use tokio::test; - use tonic::Status; - - use crate::broadcaster::accounts::account; - use crate::broadcaster::accounts::Error::*; - use crate::broadcaster::clients::MockAccountQueryClient; - use crate::types::PublicKey; - use crate::types::TMAddress; - - #[test] - async fn response_failed() { - let mut client = MockAccountQueryClient::new(); - client - .expect_account() - .returning(|_| Err(Status::aborted("aborted").into())); - - let address = rand_tm_address(); - - assert!(matches!( - account(&mut client, &address) - .await - .unwrap_err() - .current_context(), - ResponseFailed { address: _ } - )); - } - - #[test] - async fn account_not_found() { - let mut client = MockAccountQueryClient::new(); - client - .expect_account() - .returning(|_| Ok(QueryAccountResponse { account: None })); - - let address = rand_tm_address(); - - assert!(matches!( - account(&mut client, &address) - .await - .unwrap_err() - .current_context(), - AccountNotFound { address: _ } - )); - } - - #[test] - async fn malformed_response() { - let mut client = MockAccountQueryClient::new(); - client.expect_account().returning(|_| { - Ok(QueryAccountResponse { - account: Some(Any { - type_url: "wrong_type".to_string(), - value: vec![1, 2, 3, 4, 5], - }), - }) - }); - - let address = rand_tm_address(); - - assert!(matches!( - account(&mut client, &address) - .await - .unwrap_err() - .current_context(), - MalformedResponse - )); - } - - #[test] - async fn get_existing_account() { - let address = rand_tm_address(); - let acc = BaseAccount { - address: address.to_string(), - pub_key: None, - account_number: 7, - sequence: 20, - }; - let any = acc.clone().to_any().unwrap(); - - let mut client = MockAccountQueryClient::new(); - client.expect_account().returning(move |_| { - Ok(QueryAccountResponse { - account: Some(any.to_owned()), - }) - }); - - assert_eq!(account(&mut client, &address).await.unwrap(), acc); - } - - fn rand_tm_address() -> TMAddress { - PublicKey::from(SigningKey::random(&mut OsRng).verifying_key()) - .account_id("axelar") - .unwrap() - .into() - } -} diff --git a/ampd/src/broadcaster/clients.rs b/ampd/src/broadcaster/clients.rs deleted file mode 100644 index a453dd2c9..000000000 --- a/ampd/src/broadcaster/clients.rs +++ /dev/null @@ -1,72 +0,0 @@ -use async_trait::async_trait; -use cosmos_sdk_proto::cosmos::auth::v1beta1::query_client::QueryClient; -use cosmos_sdk_proto::cosmos::auth::v1beta1::{QueryAccountRequest, QueryAccountResponse}; -use cosmos_sdk_proto::cosmos::base::abci::v1beta1::TxResponse; -use cosmos_sdk_proto::cosmos::tx::v1beta1::service_client::ServiceClient; -use cosmos_sdk_proto::cosmos::tx::v1beta1::{ - BroadcastTxRequest, GetTxRequest, GetTxResponse, SimulateRequest, SimulateResponse, -}; -use error_stack::{Report, Result}; -use mockall::automock; -use tonic::transport::Channel; - -use tonic::{Response, Status}; - -#[automock] -#[async_trait] -pub trait BroadcastClient { - async fn broadcast_tx(&mut self, request: BroadcastTxRequest) -> Result; - async fn simulate(&mut self, request: SimulateRequest) -> Result; - async fn get_tx(&mut self, request: GetTxRequest) -> Result; -} - -#[async_trait] -impl BroadcastClient for ServiceClient { - async fn broadcast_tx(&mut self, request: BroadcastTxRequest) -> Result { - self.broadcast_tx(request) - .await - .and_then(|response| { - response - .into_inner() - .tx_response - .ok_or_else(|| Status::not_found("tx not found")) - }) - .map_err(Report::from) - } - - async fn simulate(&mut self, request: SimulateRequest) -> Result { - self.simulate(request) - .await - .map(Response::into_inner) - .map_err(Report::from) - } - - async fn get_tx(&mut self, request: GetTxRequest) -> Result { - self.get_tx(request) - .await - .map(Response::into_inner) - .map_err(Report::from) - } -} - -#[automock] -#[async_trait] -pub trait AccountQueryClient { - async fn account( - &mut self, - request: QueryAccountRequest, - ) -> Result; -} - -#[async_trait] -impl AccountQueryClient for QueryClient { - async fn account( - &mut self, - request: QueryAccountRequest, - ) -> Result { - self.account(request) - .await - .map(Response::into_inner) - .map_err(Report::from) - } -} diff --git a/ampd/src/broadcaster/confirm_tx.rs b/ampd/src/broadcaster/confirm_tx.rs new file mode 100644 index 000000000..a0d0c8fdc --- /dev/null +++ b/ampd/src/broadcaster/confirm_tx.rs @@ -0,0 +1,355 @@ +use std::sync::Arc; + +use axelar_wasm_std::FnExt; +use cosmrs::proto::cosmos::tx::v1beta1::{GetTxRequest, GetTxResponse}; +use error_stack::{bail, Report, Result}; +use futures::{StreamExt, TryFutureExt}; +use thiserror::Error; +use tokio::sync::{mpsc, Mutex}; +use tokio_stream::wrappers::ReceiverStream; +use tonic::Status; +use tracing::error; + +use super::cosmos; +use crate::asyncutil::future::{with_retry, RetryPolicy}; + +#[derive(Debug, PartialEq)] +pub enum TxStatus { + Success, + Failure, +} + +impl From for TxStatus { + fn from(code: u32) -> Self { + match code { + 0 => Self::Success, + _ => Self::Failure, + } + } +} + +#[derive(Debug, PartialEq)] +pub struct TxResponse { + pub status: TxStatus, + pub response: cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse, +} + +impl From for TxResponse { + fn from(response: cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse) -> Self { + Self { + status: response.code.into(), + response, + } + } +} + +#[derive(Error, Debug)] +pub enum Error { + #[error("failed confirming tx due to tx not found: {tx_hash}")] + Confirmation { tx_hash: String }, + #[error("failed confirming tx due to grpc error {status}: {tx_hash}")] + Grpc { status: Status, tx_hash: String }, + #[error("failed sending tx response")] + SendTxRes(#[from] Box>), +} + +pub struct TxConfirmer +where + T: cosmos::BroadcastClient, +{ + client: T, + retry_policy: RetryPolicy, + tx_hash_receiver: mpsc::Receiver, + tx_res_sender: mpsc::Sender, +} + +impl TxConfirmer +where + T: cosmos::BroadcastClient, +{ + pub fn new( + client: T, + retry_policy: RetryPolicy, + tx_hash_receiver: mpsc::Receiver, + tx_res_sender: mpsc::Sender, + ) -> Self { + Self { + client, + retry_policy, + tx_hash_receiver, + tx_res_sender, + } + } + + pub async fn run(self) -> Result<(), Error> { + let Self { + client, + retry_policy, + tx_hash_receiver, + tx_res_sender, + } = self; + let limit = tx_hash_receiver.capacity(); + let client = Arc::new(Mutex::new(client)); + + let mut tx_hash_stream = ReceiverStream::new(tx_hash_receiver) + .map(|tx_hash| { + // multiple instances of confirm_tx can be spawned due to buffer_unordered, + // so we need to clone the client to avoid a deadlock + confirm_tx_with_retry(client.clone(), tx_hash, retry_policy) + .and_then(|tx| async { send_response(&tx_res_sender, tx).await }) + }) + .buffer_unordered(limit); + + while let Some(res) = tx_hash_stream.next().await { + res?; + } + + Ok(()) + } +} + +async fn confirm_tx_with_retry( + client: Arc>, + tx_hash: String, + retry_policy: RetryPolicy, +) -> Result { + with_retry(|| confirm_tx(client.clone(), tx_hash.clone()), retry_policy).await +} + +// do to limitations of lambdas and lifetime issues this needs to be a separate function +async fn confirm_tx( + client: Arc>, + tx_hash: String, +) -> Result { + let req = GetTxRequest { + hash: tx_hash.clone(), + }; + + client + .lock() + .await + .tx(req) + .await + .then(evaluate_tx_response(tx_hash)) +} + +fn evaluate_tx_response( + tx_hash: String, +) -> impl Fn(core::result::Result) -> Result { + move |response| match response { + Err(status) => bail!(Error::Grpc { + status, + tx_hash: tx_hash.clone() + }), + Ok(GetTxResponse { + tx_response: None, .. + }) => bail!(Error::Confirmation { + tx_hash: tx_hash.clone() + }), + Ok(GetTxResponse { + tx_response: Some(response), + .. + }) => Ok(response.into()), + } +} + +async fn send_response( + tx_res_sender: &mpsc::Sender, + tx: TxResponse, +) -> Result<(), Error> { + tx_res_sender + .send(tx) + .await + .map_err(Box::new) + .map_err(Into::into) + .map_err(Report::new) +} + +#[cfg(test)] +mod test { + use std::time::Duration; + + use cosmrs::proto::cosmos::tx::v1beta1::GetTxRequest; + use mockall::predicate; + use tokio::sync::mpsc; + use tokio::test; + + use super::{Error, TxConfirmer, TxResponse, TxStatus}; + use crate::asyncutil::future::RetryPolicy; + use crate::broadcaster::cosmos::MockBroadcastClient; + + #[test] + async fn should_confirm_successful_tx_and_send_it_back() { + let tx_hash = "tx_hash".to_string(); + let tx_response = cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse { + code: 0, + txhash: tx_hash.clone(), + ..Default::default() + }; + let tx_res = cosmrs::proto::cosmos::tx::v1beta1::GetTxResponse { + tx_response: Some(tx_response.clone()), + ..Default::default() + }; + + let mut client = MockBroadcastClient::new(); + client + .expect_tx() + .with(predicate::eq(GetTxRequest { + hash: tx_hash.clone(), + })) + .return_once(|_| Ok(tx_res)); + + let sleep = Duration::from_secs(5); + let max_attempts = 3; + let (tx_confirmer_sender, tx_confirmer_receiver) = mpsc::channel(100); + let (tx_res_sender, mut tx_res_receiver) = mpsc::channel(100); + + let tx_confirmer = TxConfirmer::new( + client, + RetryPolicy::RepeatConstant { + sleep, + max_attempts, + }, + tx_confirmer_receiver, + tx_res_sender, + ); + let handle = tokio::spawn(tx_confirmer.run()); + + tx_confirmer_sender.send(tx_hash).await.unwrap(); + assert_eq!( + tx_res_receiver.recv().await.unwrap(), + TxResponse { + status: TxStatus::Success, + response: tx_response + } + ); + drop(tx_confirmer_sender); + assert!(handle.await.unwrap().is_ok()); + } + + #[test] + async fn should_confirm_failed_tx_and_send_it_back() { + let tx_hash = "tx_hash".to_string(); + let tx_response = cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse { + code: 1, + txhash: tx_hash.clone(), + ..Default::default() + }; + let tx_res = cosmrs::proto::cosmos::tx::v1beta1::GetTxResponse { + tx_response: Some(tx_response.clone()), + ..Default::default() + }; + + let mut client = MockBroadcastClient::new(); + client + .expect_tx() + .with(predicate::eq(GetTxRequest { + hash: tx_hash.clone(), + })) + .return_once(|_| Ok(tx_res)); + + let sleep = Duration::from_secs(5); + let max_attempts = 3; + let (tx_confirmer_sender, tx_confirmer_receiver) = mpsc::channel(100); + let (tx_res_sender, mut tx_res_receiver) = mpsc::channel(100); + + let tx_confirmer = TxConfirmer::new( + client, + RetryPolicy::RepeatConstant { + sleep, + max_attempts, + }, + tx_confirmer_receiver, + tx_res_sender, + ); + let handle = tokio::spawn(tx_confirmer.run()); + + tx_confirmer_sender.send(tx_hash).await.unwrap(); + assert_eq!( + tx_res_receiver.recv().await.unwrap(), + TxResponse { + status: TxStatus::Failure, + response: tx_response + } + ); + drop(tx_confirmer_sender); + assert!(handle.await.unwrap().is_ok()); + } + + #[test] + async fn should_retry_when_tx_is_not_found() { + let tx_hash = "tx_hash".to_string(); + + let mut client = MockBroadcastClient::new(); + client + .expect_tx() + .with(predicate::eq(GetTxRequest { + hash: tx_hash.clone(), + })) + .times(3) + .returning(|_| Ok(cosmrs::proto::cosmos::tx::v1beta1::GetTxResponse::default())); + + let sleep = Duration::from_millis(100); + let max_attempts = 3; + let (tx_confirmer_sender, tx_confirmer_receiver) = mpsc::channel(100); + let (tx_res_sender, _tx_res_receiver) = mpsc::channel(100); + + let tx_confirmer = TxConfirmer::new( + client, + RetryPolicy::RepeatConstant { + sleep, + max_attempts, + }, + tx_confirmer_receiver, + tx_res_sender, + ); + let handle = tokio::spawn(tx_confirmer.run()); + + tx_confirmer_sender.send(tx_hash.clone()).await.unwrap(); + assert!(matches!( + handle.await.unwrap().unwrap_err().current_context(), + Error::Confirmation { tx_hash: actual } if *actual == tx_hash + )); + } + + #[test] + async fn should_retry_when_grpc_error() { + let tx_hash = "tx_hash".to_string(); + + let mut client = MockBroadcastClient::new(); + client + .expect_tx() + .with(predicate::eq(GetTxRequest { + hash: tx_hash.clone(), + })) + .times(3) + .returning(|_| { + Err(tonic::Status::new( + tonic::Code::Internal, + "internal server error", + )) + }); + + let sleep = Duration::from_millis(100); + let max_attempts = 3; + let (tx_confirmer_sender, tx_confirmer_receiver) = mpsc::channel(100); + let (tx_res_sender, _tx_res_receiver) = mpsc::channel(100); + + let tx_confirmer = TxConfirmer::new( + client, + RetryPolicy::RepeatConstant { + sleep, + max_attempts, + }, + tx_confirmer_receiver, + tx_res_sender, + ); + let handle = tokio::spawn(tx_confirmer.run()); + + tx_confirmer_sender.send(tx_hash.clone()).await.unwrap(); + assert!(matches!( + handle.await.unwrap().unwrap_err().current_context(), + Error::Grpc { tx_hash: actual, status } if *actual == tx_hash && status.code() == tonic::Code::Internal + )); + } +} diff --git a/ampd/src/broadcaster/cosmos.rs b/ampd/src/broadcaster/cosmos.rs new file mode 100644 index 000000000..5f0dabeb9 --- /dev/null +++ b/ampd/src/broadcaster/cosmos.rs @@ -0,0 +1,86 @@ +//! Abstractions that decouple the broadcaster from the explicit clients of the cosmrs crate. +//! +//! In this module, new traits are defined and implemented for several cosmrs clients, +//! so the broadcaster only needs to depend on the traits that it owns itself. This makes it less +//! vulnerable to unexpected changes in the external crate and helps with unit testing, +//! because the client traits also provide auto-mocks. + +use async_trait::async_trait; +use cosmrs::proto::cosmos::auth::v1beta1::query_client::QueryClient as AuthQueryClient; +use cosmrs::proto::cosmos::auth::v1beta1::{QueryAccountRequest, QueryAccountResponse}; +use cosmrs::proto::cosmos::bank::v1beta1::query_client::QueryClient as BankQueryClient; +use cosmrs::proto::cosmos::bank::v1beta1::{QueryBalanceRequest, QueryBalanceResponse}; +use cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse; +use cosmrs::proto::cosmos::tx::v1beta1::service_client::ServiceClient; +use cosmrs::proto::cosmos::tx::v1beta1::{ + BroadcastTxRequest, GetTxRequest, GetTxResponse, SimulateRequest, SimulateResponse, +}; +use mockall::automock; +use tonic::transport::Channel; +use tonic::{Response, Status}; + +#[automock] +#[async_trait] +pub trait BroadcastClient { + async fn broadcast_tx(&mut self, request: BroadcastTxRequest) -> Result; + async fn simulate(&mut self, request: SimulateRequest) -> Result; + async fn tx(&mut self, request: GetTxRequest) -> Result; +} + +#[async_trait] +impl BroadcastClient for ServiceClient { + async fn broadcast_tx(&mut self, request: BroadcastTxRequest) -> Result { + self.broadcast_tx(request).await.and_then(|response| { + response + .into_inner() + .tx_response + .ok_or_else(|| Status::not_found("tx not found")) + }) + } + + async fn simulate(&mut self, request: SimulateRequest) -> Result { + self.simulate(request).await.map(Response::into_inner) + } + + async fn tx(&mut self, request: GetTxRequest) -> Result { + self.get_tx(request).await.map(Response::into_inner) + } +} + +#[automock] +#[async_trait] +pub trait AccountQueryClient { + async fn account( + &mut self, + address: QueryAccountRequest, + ) -> Result; +} + +#[async_trait] +impl AccountQueryClient for AuthQueryClient { + async fn account( + &mut self, + request: QueryAccountRequest, + ) -> Result { + self.account(request).await.map(Response::into_inner) + } +} + +#[automock] +#[async_trait] +pub trait BalanceQueryClient { + async fn balance( + &mut self, + request: QueryBalanceRequest, + ) -> Result; +} + +#[async_trait] +impl BalanceQueryClient for BankQueryClient { + async fn balance( + &mut self, + request: QueryBalanceRequest, + ) -> Result { + self.balance(request).await.map(Response::into_inner) + } +} diff --git a/ampd/src/broadcaster/dec_coin.rs b/ampd/src/broadcaster/dec_coin.rs index 664bb7f6a..a3d08a11d 100644 --- a/ampd/src/broadcaster/dec_coin.rs +++ b/ampd/src/broadcaster/dec_coin.rs @@ -64,6 +64,15 @@ impl From for proto::cosmos::base::v1beta1::DecCoin { } } +impl From<&DecCoin> for proto::cosmos::base::v1beta1::DecCoin { + fn from(coin: &DecCoin) -> proto::cosmos::base::v1beta1::DecCoin { + proto::cosmos::base::v1beta1::DecCoin { + denom: coin.denom.to_string(), + amount: coin.amount.to_string(), + } + } +} + impl TryFrom for DecCoin { type Error = Report; @@ -92,15 +101,6 @@ impl TryFrom<&str> for DecCoin { } } -impl From<&DecCoin> for proto::cosmos::base::v1beta1::DecCoin { - fn from(coin: &DecCoin) -> proto::cosmos::base::v1beta1::DecCoin { - proto::cosmos::base::v1beta1::DecCoin { - denom: coin.denom.to_string(), - amount: coin.amount.to_string(), - } - } -} - impl Display for DecCoin { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { // See: https://github.com/cosmos/cosmos-sdk/blob/c4864e9f85011b3e971885ea995a0021c01a885d/types/dec_coin.go#L134 @@ -186,10 +186,12 @@ impl Display for Denom { #[cfg(test)] mod tests { - use super::DecCoin; - use cosmrs::proto; use std::convert::TryFrom; + use cosmrs::proto; + + use super::DecCoin; + #[test] fn correct_parse() { assert!(DecCoin::new(1000.00, "uaxl").is_ok()) diff --git a/ampd/src/broadcaster/mod.rs b/ampd/src/broadcaster/mod.rs new file mode 100644 index 000000000..2cd0b0bf1 --- /dev/null +++ b/ampd/src/broadcaster/mod.rs @@ -0,0 +1,770 @@ +use std::cmp; +use std::convert::TryInto; +use std::ops::Mul; +use std::time::Duration; + +use async_trait::async_trait; +use axelar_wasm_std::FnExt; +use cosmrs::proto::cosmos::auth::v1beta1::{ + BaseAccount, QueryAccountRequest, QueryAccountResponse, +}; +use cosmrs::proto::cosmos::bank::v1beta1::QueryBalanceRequest; +use cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse; +use cosmrs::proto::cosmos::tx::v1beta1::{BroadcastMode, BroadcastTxRequest, SimulateRequest}; +use cosmrs::proto::traits::MessageExt; +use cosmrs::tendermint::chain::Id; +use cosmrs::tx::Fee; +use cosmrs::{Amount, Coin, Denom, Gas}; +use dec_coin::DecCoin; +use error_stack::{ensure, report, FutureExt, Result, ResultExt}; +use futures::TryFutureExt; +use k256::sha2::{Digest, Sha256}; +use mockall::automock; +use num_traits::{cast, Zero}; +use prost::Message; +use prost_types::Any; +use report::ResultCompatExt; +use serde::{Deserialize, Serialize}; +use thiserror::Error; +use tonic::{Code, Status}; +use tracing::info; +use tx::Tx; +use typed_builder::TypedBuilder; + +use crate::tofnd; +use crate::tofnd::grpc::Multisig; +use crate::types::{PublicKey, TMAddress}; + +pub mod confirm_tx; +mod cosmos; +mod dec_coin; +mod tx; + +#[derive(Error, Debug)] +pub enum Error { + #[error("failed building tx")] + TxBuilding, + #[error("failed to estimate gas")] + GasEstimation, + #[error("failed to estimate fee")] + FeeEstimation, + #[error("broadcast failed")] + Broadcast, + #[error("failed to query balance for address '{address}' and denomination '{denom}'")] + QueryBalance { address: TMAddress, denom: Denom }, + #[error("failed to query account for address '{address}'")] + QueryAccount { address: TMAddress }, + #[error("address '{address}' controls no tokens of denomination '{denom}' that are required to pay broadcast fees")] + NoTokensOfFeeDenom { address: TMAddress, denom: Denom }, + #[error("failed to encode broadcaster address from public key")] + AddressEncoding, + #[error("received response for query '{query}' could not be decoded")] + MalformedResponse { query: String }, + #[error("address {address} is unknown, please make sure it is funded")] + AccountNotFound { address: TMAddress }, +} + +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] +pub struct Config { + pub chain_id: Id, + #[serde(with = "humantime_serde")] + pub tx_fetch_interval: Duration, + pub tx_fetch_max_retries: u32, + pub gas_adjustment: f64, + pub gas_price: DecCoin, + pub batch_gas_limit: Gas, + pub queue_cap: usize, + #[serde(with = "humantime_serde")] + pub broadcast_interval: Duration, +} + +impl Default for Config { + fn default() -> Self { + Self { + chain_id: "axelar-dojo-1".parse().unwrap(), + tx_fetch_interval: Duration::from_millis(500), + tx_fetch_max_retries: 10, + gas_adjustment: 1.0, + gas_price: DecCoin::new(0.00005, "uaxl").unwrap(), + batch_gas_limit: 1000000, + queue_cap: 1000, + broadcast_interval: Duration::from_secs(5), + } + } +} + +#[automock] +#[async_trait] +pub trait Broadcaster { + fn sender_address(&self) -> TMAddress; + async fn broadcast(&mut self, msgs: Vec) -> Result; + async fn estimate_fee(&mut self, msgs: Vec) -> Result; +} + +#[derive(TypedBuilder)] +pub struct UnvalidatedBasicBroadcaster +where + T: cosmos::BroadcastClient + Send, + S: Multisig + Send + Sync, + A: cosmos::AccountQueryClient + Send, + B: cosmos::BalanceQueryClient, +{ + client: T, + signer: S, + auth_query_client: A, + bank_query_client: B, + address_prefix: String, + #[builder(default, setter(skip))] + acc_sequence: Option, + pub_key: (String, PublicKey), + config: Config, +} + +impl UnvalidatedBasicBroadcaster +where + T: cosmos::BroadcastClient + Send, + S: Multisig + Send + Sync, + A: cosmos::AccountQueryClient + Send, + B: cosmos::BalanceQueryClient, +{ + pub async fn validate_fee_denomination(mut self) -> Result, Error> { + let denom: Denom = self.config.gas_price.denom.clone().into(); + let address: TMAddress = self.derive_address()?; + + ensure!( + self.balance(address.clone(), denom.clone()) + .await? + .then(extract_non_zero_amount) + .is_some(), + Error::NoTokensOfFeeDenom { denom, address } + ); + + Ok(BasicBroadcaster { + client: self.client, + signer: self.signer, + auth_query_client: self.auth_query_client, + address: address.clone(), + acc_sequence: self.acc_sequence, + pub_key: self.pub_key, + config: self.config, + }) + } + + fn derive_address(&mut self) -> Result { + Ok(self + .pub_key + .1 + .account_id(&self.address_prefix) + .change_context(Error::AddressEncoding)? + .into()) + } + + async fn balance(&mut self, address: TMAddress, denom: Denom) -> Result { + let coin = self + .bank_query_client + .balance(QueryBalanceRequest { + address: address.to_string(), + denom: denom.to_string(), + }) + .await + .and_then(|response| { + response + .balance + .ok_or(Status::not_found("balance not found")) + }) + .change_context(Error::QueryBalance { address, denom })?; + + ResultCompatExt::change_context( + coin.try_into(), + Error::MalformedResponse { + query: "balance".to_string(), + }, + ) + } +} + +fn extract_non_zero_amount(coin: Coin) -> Option { + Some(coin.amount).filter(|amount| !amount.is_zero()) +} + +#[derive(Debug)] +pub struct BasicBroadcaster +where + T: cosmos::BroadcastClient + Send, + S: Multisig + Send + Sync, + Q: cosmos::AccountQueryClient + Send, +{ + client: T, + signer: S, + auth_query_client: Q, + address: TMAddress, + acc_sequence: Option, + pub_key: (String, PublicKey), + config: Config, +} + +#[async_trait] +impl Broadcaster for BasicBroadcaster +where + T: cosmos::BroadcastClient + Send, + S: Multisig + Send + Sync, + Q: cosmos::AccountQueryClient + Send, +{ + fn sender_address(&self) -> TMAddress { + self.address.clone() + } + + async fn broadcast(&mut self, msgs: Vec) -> Result { + let (acc_number, acc_sequence) = self.acc_number_and_sequence().await?; + let tx = Tx::builder() + .msgs(msgs.clone()) + .fee(self.estimate_fee(msgs, acc_sequence).await?) + .pub_key(self.pub_key.1) + .acc_sequence(acc_sequence) + .build() + .sign_with(&self.config.chain_id, acc_number, |sign_doc| { + let mut hasher = Sha256::new(); + hasher.update(sign_doc); + + let sign_digest: [u8; 32] = hasher + .finalize() + .to_vec() + .try_into() + .expect("hash size must be 32"); + + self.signer.sign( + self.pub_key.0.as_str(), + sign_digest.into(), + &self.pub_key.1, + tofnd::Algorithm::Ecdsa, + ) + }) + .await + .change_context(Error::TxBuilding)?; + + let tx = BroadcastTxRequest { + tx_bytes: tx.to_bytes().change_context(Error::TxBuilding)?, + mode: BroadcastMode::Sync as i32, + }; + + let response = self + .client + .broadcast_tx(tx) + .change_context(Error::Broadcast) + .await?; + let TxResponse { + txhash: tx_hash, .. + } = &response; + + info!(tx_hash, "broadcasted transaction"); + + self.acc_sequence.replace( + acc_sequence + .checked_add(1) + .expect("account sequence must be less than u64::MAX"), + ); + Ok(response) + } + + async fn estimate_fee(&mut self, msgs: Vec) -> Result { + let (_, acc_sequence) = self.acc_number_and_sequence().await?; + + self.estimate_fee(msgs, acc_sequence).await + } +} + +impl BasicBroadcaster +where + T: cosmos::BroadcastClient + Send, + S: Multisig + Send + Sync, + Q: cosmos::AccountQueryClient + Send, +{ + async fn acc_number_and_sequence(&mut self) -> Result<(u64, u64), Error> { + let request = QueryAccountRequest { + address: self.address.to_string(), + }; + + let response = self + .auth_query_client + .account(request) + .await + .then(remap_account_not_found_error) + .change_context(Error::QueryAccount { + address: self.address.clone(), + })?; + + let account = response.account.map_or( + Err(report!(Error::AccountNotFound { + address: self.address.clone() + })), + decode_base_account, + )?; + + let acc_sequence = self.acc_sequence.insert(cmp::max( + account.sequence, + self.acc_sequence.unwrap_or_default(), + )); + + Ok((account.account_number, *acc_sequence)) + } + + async fn estimate_fee(&mut self, msgs: Vec, acc_sequence: u64) -> Result { + let sim_tx = Tx::builder() + .msgs(msgs) + .pub_key(self.pub_key.1) + .acc_sequence(acc_sequence) + .build() + .with_dummy_sig() + .await + .change_context(Error::TxBuilding)? + .to_bytes() + .change_context(Error::TxBuilding)?; + + self.estimate_gas(sim_tx).await.map(|gas| { + let gas_adj = gas as f64 * self.config.gas_adjustment; + + Ok(Fee::from_amount_and_gas( + Coin { + amount: cast(gas_adj.mul(self.config.gas_price.amount).ceil()) + .ok_or(Error::FeeEstimation)?, + denom: self.config.gas_price.denom.clone().into(), + }, + cast::(gas_adj).ok_or(Error::FeeEstimation)?, + )) + })? + } + + async fn estimate_gas(&mut self, tx_bytes: Vec) -> Result { + #[allow(deprecated)] + self.client + .simulate(SimulateRequest { tx: None, tx_bytes }) + .change_context(Error::GasEstimation) + .and_then(|response| async { + response + .gas_info + .map(|info| info.gas_used) + .ok_or(Error::GasEstimation.into()) + }) + .await + } +} + +fn decode_base_account(account: Any) -> Result { + BaseAccount::decode(&account.value[..]) + .change_context(Error::MalformedResponse { + query: "account".to_string(), + }) + .attach_printable_lazy(|| format!("{{ value = {:?} }}", account.value)) +} + +fn remap_account_not_found_error( + response: core::result::Result, +) -> core::result::Result { + if matches!(response.clone(), Err(status) if status.code() == Code::NotFound) { + Ok(QueryAccountResponse { account: None }) + } else { + response + } +} + +#[cfg(test)] +mod tests { + use cosmrs::bank::MsgSend; + use cosmrs::crypto::PublicKey; + use cosmrs::proto::cosmos::auth::v1beta1::{BaseAccount, QueryAccountResponse}; + use cosmrs::proto::cosmos::bank::v1beta1::QueryBalanceResponse; + use cosmrs::proto::cosmos::base::abci::v1beta1::{GasInfo, TxResponse}; + use cosmrs::proto::cosmos::tx::v1beta1::{GetTxResponse, SimulateResponse}; + use cosmrs::proto::traits::MessageExt; + use cosmrs::proto::Any; + use cosmrs::tx::Msg; + use cosmrs::{AccountId, Coin, Denom}; + use ecdsa::SigningKey; + use k256::Secp256k1; + use rand::rngs::OsRng; + use tokio::test; + use tonic::Status; + + use crate::broadcaster::cosmos::{ + MockAccountQueryClient, MockBalanceQueryClient, MockBroadcastClient, + }; + use crate::broadcaster::{ + BasicBroadcaster, Broadcaster, Config, Error, UnvalidatedBasicBroadcaster, + }; + use crate::tofnd::grpc::MockMultisig; + use crate::types::TMAddress; + use crate::PREFIX; + + #[test] + async fn broadcaster_has_incorrect_fee_denomination_return_error() { + let known_denom = "some/other/denom".parse().unwrap(); + + let broadcaster = + init_unvalidated_broadcaster(Some(init_mock_balance_client(known_denom)), None, None); + + let report = broadcaster.validate_fee_denomination().await.unwrap_err(); + assert!(matches!( + report.current_context(), + Error::NoTokensOfFeeDenom { .. } + )); + } + + #[test] + async fn broadcaster_has_correct_fee_denomination_return_validated_broadcaster() { + let broadcaster = init_unvalidated_broadcaster(None, None, None); + + let result = broadcaster.validate_fee_denomination().await; + assert!(result.is_ok()); + } + + #[test] + async fn gas_estimation_call_failed() { + let mut client = MockBroadcastClient::new(); + client + .expect_simulate() + .returning(|_| Err(Status::unavailable("unavailable service"))); + + let mut broadcaster = init_validated_broadcaster(None, None, Some(client)).await; + + let msgs = vec![dummy_msg()]; + + assert!(matches!( + broadcaster + .broadcast(msgs) + .await + .unwrap_err() + .current_context(), + Error::GasEstimation + )); + } + + #[test] + async fn gas_estimation_none_response() { + let mut client = MockBroadcastClient::new(); + client.expect_simulate().returning(|_| { + Ok(SimulateResponse { + gas_info: None, + result: None, + }) + }); + + let mut broadcaster = init_validated_broadcaster(None, None, Some(client)).await; + let msgs = vec![dummy_msg()]; + + assert!(matches!( + broadcaster + .broadcast(msgs) + .await + .unwrap_err() + .current_context(), + Error::GasEstimation + )); + } + + #[test] + async fn broadcast_failed() { + let mut client = MockBroadcastClient::new(); + client.expect_simulate().returning(|_| { + Ok(SimulateResponse { + gas_info: Some(GasInfo { + gas_wanted: 0, + gas_used: 0, + }), + result: None, + }) + }); + client + .expect_broadcast_tx() + .returning(|_| Err(Status::aborted("failed"))); + + let mut broadcaster = init_validated_broadcaster(None, None, Some(client)).await; + let msgs = vec![dummy_msg()]; + + assert!(matches!( + broadcaster + .broadcast(msgs) + .await + .unwrap_err() + .current_context(), + Error::Broadcast + )); + } + + #[test] + async fn broadcast_confirmed() { + let mut broadcaster = init_validated_broadcaster(None, None, None).await; + let msgs = vec![dummy_msg()]; + + assert_eq!(broadcaster.acc_sequence, None); + assert!(broadcaster.broadcast(msgs).await.is_ok()); + assert_eq!(broadcaster.acc_sequence, Some(1)); + } + + #[test] + async fn broadcast_confirmed_in_mem_acc_sequence_mismatch_with_on_chain() { + let mut auth_query_client = MockAccountQueryClient::new(); + let mut call_count = 0; + auth_query_client + .expect_account() + .returning(move |request| { + let mut account = BaseAccount { + address: request.address, + pub_key: None, + account_number: 7, + sequence: 0, + }; + + call_count += 1; + + account.sequence = match call_count { + 1 => 0, + 2 => 10, + _ => 0, + }; + + Ok(QueryAccountResponse { + account: Some(account.to_any().unwrap()), + }) + }); + + let mut broadcaster = init_validated_broadcaster(None, Some(auth_query_client), None).await; + + assert_eq!(broadcaster.acc_sequence, None); + assert!(broadcaster.broadcast(vec![dummy_msg()]).await.is_ok()); + assert_eq!(broadcaster.acc_sequence, Some(1)); + assert!(broadcaster.broadcast(vec![dummy_msg()]).await.is_ok()); + assert_eq!(broadcaster.acc_sequence, Some(11)); + assert!(broadcaster.broadcast(vec![dummy_msg()]).await.is_ok()); + assert_eq!(broadcaster.acc_sequence, Some(12)); + } + + #[test] + async fn account_query_failed_return_error() { + let mut client = MockAccountQueryClient::new(); + client + .expect_account() + .returning(|_| Err(Status::aborted("aborted"))); + + let mut broadcaster = init_validated_broadcaster(None, Some(client), None).await; + + for report in [ + broadcaster.broadcast(vec![dummy_msg()]).await.unwrap_err(), + Broadcaster::estimate_fee(&mut broadcaster, vec![dummy_msg()]) + .await + .unwrap_err(), + ] { + assert!(matches!( + report.current_context(), + Error::QueryAccount { .. } + )); + } + } + + #[test] + async fn account_not_found_returns_error() { + let mut client = MockAccountQueryClient::new(); + client + .expect_account() + .returning(|_| Ok(QueryAccountResponse { account: None })); + + let mut broadcaster = init_validated_broadcaster(None, Some(client), None).await; + + for report in [ + broadcaster.broadcast(vec![dummy_msg()]).await.unwrap_err(), + Broadcaster::estimate_fee(&mut broadcaster, vec![dummy_msg()]) + .await + .unwrap_err(), + ] { + assert!(matches!( + report.current_context(), + Error::AccountNotFound { .. } + )); + } + } + + #[test] + async fn malformed_account_query_response_return_error() { + let mut client = MockAccountQueryClient::new(); + client.expect_account().returning(|_| { + Ok(QueryAccountResponse { + account: Some(Any { + type_url: "wrong_type".to_string(), + value: vec![1, 2, 3, 4, 5], + }), + }) + }); + + let mut broadcaster = init_validated_broadcaster(None, Some(client), None).await; + + for report in [ + broadcaster.broadcast(vec![dummy_msg()]).await.unwrap_err(), + Broadcaster::estimate_fee(&mut broadcaster, vec![dummy_msg()]) + .await + .unwrap_err(), + ] { + assert!(matches!( + report.current_context(), + Error::MalformedResponse { .. } + )); + } + } + + fn init_unvalidated_broadcaster( + balance_client_override: Option, + auth_client_override: Option, + broadcast_client_override: Option, + ) -> UnvalidatedBasicBroadcaster< + MockBroadcastClient, + MockMultisig, + MockAccountQueryClient, + MockBalanceQueryClient, + > { + let key_id = "key_uid".to_string(); + let priv_key = SigningKey::random(&mut OsRng); + let pub_key: PublicKey = priv_key.verifying_key().into(); + let known_denom: Denom = Config::default().gas_price.denom.clone().into(); + + UnvalidatedBasicBroadcaster::builder() + .client(broadcast_client_override.unwrap_or_else(init_mock_broadcaster_client)) + .signer(init_mock_signer(key_id.clone(), priv_key)) + .auth_query_client( + auth_client_override.unwrap_or_else(|| init_mock_account_client(pub_key)), + ) + .bank_query_client( + balance_client_override.unwrap_or(init_mock_balance_client(known_denom)), + ) + .address_prefix(PREFIX.to_string()) + .pub_key((key_id, pub_key)) + .config(Config::default()) + .build() + } + + async fn init_validated_broadcaster( + balance_client_override: Option, + auth_client_override: Option, + broadcast_client_override: Option, + ) -> BasicBroadcaster { + init_unvalidated_broadcaster( + balance_client_override, + auth_client_override, + broadcast_client_override, + ) + .validate_fee_denomination() + .await + .unwrap() + } + + fn init_mock_broadcaster_client() -> MockBroadcastClient { + let mut client = MockBroadcastClient::new(); + client.expect_simulate().returning(|_| { + Ok(SimulateResponse { + gas_info: Some(GasInfo { + gas_wanted: 1000, + gas_used: 500, + }), + result: None, + }) + }); + client + .expect_broadcast_tx() + .returning(|_| Ok(TxResponse::default())); + client.expect_tx().returning(|_| { + Ok(GetTxResponse { + tx_response: Some(TxResponse { + code: 0, + ..TxResponse::default() + }), + ..GetTxResponse::default() + }) + }); + + client + } + + // returns a non-zero balance if the denom in the request is known, a zero balance otherwise + fn init_mock_balance_client(known_denom: Denom) -> MockBalanceQueryClient { + let mut bank_query_client = MockBalanceQueryClient::new(); + bank_query_client + .expect_balance() + .returning(move |request| { + if request.denom.eq(known_denom.as_ref()) { + Ok(QueryBalanceResponse { + balance: Some( + Coin { + amount: 1, + denom: known_denom.clone(), + } + .into(), + ), + }) + } else { + Ok(QueryBalanceResponse { + balance: Some( + Coin { + amount: 0, + denom: request.denom.parse().unwrap(), + } + .into(), + ), + }) + } + }); + bank_query_client + } + + // returns an account for the address corresponding to the given public key if that address is queried + fn init_mock_account_client(pub_key: PublicKey) -> MockAccountQueryClient { + let address: TMAddress = pub_key.account_id(PREFIX).unwrap().into(); + let account = BaseAccount { + address: address.to_string(), + pub_key: None, + account_number: 7, + sequence: 0, + }; + + let mut auth_query_client = MockAccountQueryClient::new(); + auth_query_client + .expect_account() + .returning(move |request| { + if request.address == address.to_string() { + Ok(QueryAccountResponse { + account: Some(account.to_any().unwrap()), + }) + } else { + Ok(QueryAccountResponse { account: None }) + } + }); + + auth_query_client + } + + // signs a digest if the public key matches the given private key + fn init_mock_signer(key_id: String, priv_key: SigningKey) -> MockMultisig { + let pub_key: PublicKey = priv_key.verifying_key().into(); + + let mut signer = MockMultisig::default(); + signer + .expect_sign() + .returning(move |actual_key_uid, data, actual_pub_key, _| { + assert_eq!(actual_key_uid, &key_id); + assert_eq!(actual_pub_key, &pub_key); + + let (signature, _) = priv_key + .sign_prehash_recoverable(>::from(data).as_slice()) + .unwrap(); + + Ok(signature.to_vec()) + }); + signer + } + + fn dummy_msg() -> Any { + MsgSend { + from_address: AccountId::new("", &[1, 2, 3]).unwrap(), + to_address: AccountId::new("", &[4, 5, 6]).unwrap(), + amount: vec![], + } + .to_any() + .unwrap() + } +} diff --git a/ampd/src/broadcaster/tx.rs b/ampd/src/broadcaster/tx.rs index 077a27330..bcea228e8 100644 --- a/ampd/src/broadcaster/tx.rs +++ b/ampd/src/broadcaster/tx.rs @@ -1,12 +1,10 @@ use core::fmt::Debug; use std::future::Future; +use cosmrs::proto::cosmos::tx::v1beta1::TxRaw; use cosmrs::tendermint::chain::Id; -use cosmrs::{ - proto::cosmos::tx::v1beta1::TxRaw, - tx::{BodyBuilder, Fee, SignDoc, SignerInfo}, - Any, Coin, -}; +use cosmrs::tx::{BodyBuilder, Fee, SignDoc, SignerInfo}; +use cosmrs::{Any, Coin}; use error_stack::{Context, Result, ResultExt}; use report::ResultCompatExt; use thiserror::Error; @@ -98,24 +96,21 @@ where #[cfg(test)] mod tests { - use cosmos_sdk_proto::Any; - use cosmrs::{ - bank::MsgSend, - bip32::secp256k1::elliptic_curve::rand_core::OsRng, - crypto::secp256k1::SigningKey, - proto::cosmos::tx::v1beta1::TxRaw, - tendermint::chain::Id, - tx::{BodyBuilder, Fee, Msg, SignDoc, SignerInfo}, - AccountId, Coin, - }; + use cosmrs::bank::MsgSend; + use cosmrs::bip32::secp256k1::elliptic_curve::rand_core::OsRng; + use cosmrs::crypto::secp256k1::SigningKey; + use cosmrs::proto::cosmos::tx::v1beta1::TxRaw; + use cosmrs::proto::Any; + use cosmrs::tendermint::chain::Id; + use cosmrs::tx::{BodyBuilder, Fee, Msg, SignDoc, SignerInfo}; + use cosmrs::{AccountId, Coin}; use error_stack::Result; use k256::ecdsa; use k256::sha2::{Digest, Sha256}; use tokio::test; - use crate::types::PublicKey; - use super::{Error, Tx, DUMMY_CHAIN_ID}; + use crate::types::PublicKey; #[test] async fn sign_with_should_produce_the_correct_tx() { diff --git a/ampd/src/commands/bond_verifier.rs b/ampd/src/commands/bond_verifier.rs index 3ff02521a..7703df219 100644 --- a/ampd/src/commands/bond_verifier.rs +++ b/ampd/src/commands/bond_verifier.rs @@ -1,5 +1,3 @@ -use std::path::Path; - use axelar_wasm_std::nonempty; use cosmrs::cosmwasm::MsgExecuteContract; use cosmrs::tx::Msg; @@ -20,10 +18,10 @@ pub struct Args { pub denom: String, } -pub async fn run(config: Config, state_path: &Path, args: Args) -> Result, Error> { +pub async fn run(config: Config, args: Args) -> Result, Error> { let coin = Coin::new(args.amount, args.denom.as_str()).change_context(Error::InvalidInput)?; - let pub_key = verifier_pub_key(state_path, config.tofnd_config.clone()).await?; + let pub_key = verifier_pub_key(config.tofnd_config.clone()).await?; let msg = serde_json::to_vec(&ExecuteMsg::BondVerifier { service_name: args.service_name.into(), diff --git a/ampd/src/commands/claim_stake.rs b/ampd/src/commands/claim_stake.rs new file mode 100644 index 000000000..918c1e90a --- /dev/null +++ b/ampd/src/commands/claim_stake.rs @@ -0,0 +1,41 @@ +use axelar_wasm_std::nonempty; +use cosmrs::cosmwasm::MsgExecuteContract; +use cosmrs::tx::Msg; +use error_stack::Result; +use report::ResultCompatExt; +use service_registry::msg::ExecuteMsg; +use valuable::Valuable; + +use crate::commands::{broadcast_tx, verifier_pub_key}; +use crate::config::Config; +use crate::{Error, PREFIX}; + +#[derive(clap::Args, Debug, Valuable)] +pub struct Args { + pub service_name: nonempty::String, +} + +pub async fn run(config: Config, args: Args) -> Result, Error> { + let pub_key = verifier_pub_key(config.tofnd_config.clone()).await?; + + let msg = serde_json::to_vec(&ExecuteMsg::ClaimStake { + service_name: args.service_name.into(), + }) + .expect("claim stake msg should be serializable"); + + let tx = MsgExecuteContract { + sender: pub_key.account_id(PREFIX).change_context(Error::Tofnd)?, + contract: config.service_registry.cosmwasm_contract.as_ref().clone(), + msg, + funds: vec![], + } + .into_any() + .expect("failed to serialize proto message"); + + let tx_hash = broadcast_tx(config, tx, pub_key).await?.txhash; + + Ok(Some(format!( + "successfully broadcast claim stake transaction, tx hash: {}", + tx_hash + ))) +} diff --git a/ampd/src/commands/daemon.rs b/ampd/src/commands/daemon.rs index 176022a24..0cfc1da85 100644 --- a/ampd/src/commands/daemon.rs +++ b/ampd/src/commands/daemon.rs @@ -1,30 +1,8 @@ -use std::path::Path; - -use error_stack::{Report, ResultExt}; -use tracing::info; +use error_stack::Report; use crate::config::Config; -use crate::state::{flush, load}; use crate::Error; -pub async fn run(config: Config, state_path: &Path) -> Result, Report> { - let state = load(state_path).change_context(Error::LoadConfig)?; - let (state, execution_result) = crate::run(config, state).await; - - info!("persisting state"); - let state_flush_result = flush(&state, state_path).change_context(Error::ReturnState); - - match (execution_result, state_flush_result) { - // both execution and persisting state failed: return the merged error - (Err(mut report), Err(state_err)) => { - report.extend_one(state_err); - Err(report) - } - - // any single path failed: report the error - (Err(report), Ok(())) | (Ok(()), Err(report)) => Err(report), - - // no errors in either execution or persisting state - (Ok(()), Ok(())) => Ok(None), - } +pub async fn run(config: Config) -> Result, Report> { + crate::run(config).await.map(|_| None) } diff --git a/ampd/src/commands/deregister_chain_support.rs b/ampd/src/commands/deregister_chain_support.rs index 7311689d7..c212125f3 100644 --- a/ampd/src/commands/deregister_chain_support.rs +++ b/ampd/src/commands/deregister_chain_support.rs @@ -1,5 +1,3 @@ -use std::path::Path; - use axelar_wasm_std::nonempty; use cosmrs::cosmwasm::MsgExecuteContract; use cosmrs::tx::Msg; @@ -19,8 +17,8 @@ pub struct Args { pub chains: Vec, } -pub async fn run(config: Config, state_path: &Path, args: Args) -> Result, Error> { - let pub_key = verifier_pub_key(state_path, config.tofnd_config.clone()).await?; +pub async fn run(config: Config, args: Args) -> Result, Error> { + let pub_key = verifier_pub_key(config.tofnd_config.clone()).await?; let msg = serde_json::to_vec(&ExecuteMsg::DeregisterChainSupport { service_name: args.service_name.into(), diff --git a/ampd/src/commands/mod.rs b/ampd/src/commands/mod.rs index 10b600ade..257946ded 100644 --- a/ampd/src/commands/mod.rs +++ b/ampd/src/commands/mod.rs @@ -1,30 +1,31 @@ -use std::path::Path; - use clap::Subcommand; -use cosmos_sdk_proto::cosmos::base::abci::v1beta1::TxResponse; -use cosmos_sdk_proto::cosmos::{ - auth::v1beta1::query_client::QueryClient, tx::v1beta1::service_client::ServiceClient, -}; -use cosmos_sdk_proto::Any; +use cosmrs::proto::cosmos::auth::v1beta1::query_client::QueryClient as AuthQueryClient; +use cosmrs::proto::cosmos::bank::v1beta1::query_client::QueryClient as BankQueryClient; +use cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse; +use cosmrs::proto::cosmos::tx::v1beta1::service_client::ServiceClient; +use cosmrs::proto::Any; use cosmrs::AccountId; -use error_stack::Result; -use error_stack::ResultExt; +use error_stack::{report, FutureExt, Result, ResultExt}; +use futures::TryFutureExt; use serde::{Deserialize, Serialize}; +use tokio::sync::mpsc::{Receiver, Sender}; use valuable::Valuable; +use crate::asyncutil::future::RetryPolicy; use crate::broadcaster::Broadcaster; -use crate::config::Config as AmpdConfig; -use crate::state; -use crate::tofnd::grpc::{MultisigClient, SharableEcdsaClient}; +use crate::config::{Config as AmpdConfig, Config}; +use crate::tofnd::grpc::{Multisig, MultisigClient}; use crate::types::{PublicKey, TMAddress}; -use crate::{broadcaster, Error}; -use crate::{tofnd, PREFIX}; +use crate::{broadcaster, tofnd, Error, PREFIX}; pub mod bond_verifier; +pub mod claim_stake; pub mod daemon; pub mod deregister_chain_support; pub mod register_chain_support; pub mod register_public_key; +pub mod send_tokens; +pub mod unbond_verifier; pub mod verifier_address; #[derive(Debug, Subcommand, Valuable)] @@ -33,14 +34,20 @@ pub enum SubCommand { Daemon, /// Bond the verifier to the service registry contract BondVerifier(bond_verifier::Args), + /// Unbond the verifier from the service registry contract + UnbondVerifier(unbond_verifier::Args), + /// Claim unbonded stake from the service registry contract + ClaimStake(claim_stake::Args), /// Register chain support to the service registry contract RegisterChainSupport(register_chain_support::Args), /// Deregister chain support to the service registry contract DeregisterChainSupport(deregister_chain_support::Args), /// Register public key to the multisig contract - RegisterPublicKey, + RegisterPublicKey(register_public_key::Args), /// Query the verifier address VerifierAddress, + /// Send tokens from the verifier account to a specified address + SendTokens(send_tokens::Args), } #[derive(Debug, Deserialize, Serialize, PartialEq)] @@ -56,20 +63,14 @@ impl Default for ServiceRegistryConfig { } } -async fn verifier_pub_key(state_path: &Path, config: tofnd::Config) -> Result { - let state = state::load(state_path).change_context(Error::LoadConfig)?; - - match state.pub_key { - Some(pub_key) => Ok(pub_key), - None => SharableEcdsaClient::new( - MultisigClient::connect(config.party_uid, config.url) - .await - .change_context(Error::Connection)?, - ) - .keygen(&config.key_uid) +async fn verifier_pub_key(config: tofnd::Config) -> Result { + MultisigClient::new(config.party_uid, config.url.clone()) .await - .change_context(Error::Tofnd), - } + .change_context(Error::Connection) + .attach_printable(config.url.clone())? + .keygen(&config.key_uid, tofnd::Algorithm::Ecdsa) + .await + .change_context(Error::Tofnd) } async fn broadcast_tx( @@ -77,38 +78,87 @@ async fn broadcast_tx( tx: Any, pub_key: PublicKey, ) -> Result { + let (confirmation_sender, mut confirmation_receiver) = tokio::sync::mpsc::channel(1); + let (hash_to_confirm_sender, hash_to_confirm_receiver) = tokio::sync::mpsc::channel(1); + + let mut broadcaster = instantiate_broadcaster( + config, + pub_key, + hash_to_confirm_receiver, + confirmation_sender, + ) + .await?; + + broadcaster + .broadcast(vec![tx]) + .change_context(Error::Broadcaster) + .and_then(|response| { + hash_to_confirm_sender + .send(response.txhash) + .change_context(Error::Broadcaster) + }) + .await?; + + confirmation_receiver + .recv() + .await + .ok_or(report!(Error::TxConfirmation)) + .map(|tx| tx.response) +} + +async fn instantiate_broadcaster( + config: Config, + pub_key: PublicKey, + tx_hashes_to_confirm: Receiver, + confirmed_txs: Sender, +) -> Result { let AmpdConfig { tm_grpc, broadcast, tofnd_config, .. } = config; - let service_client = ServiceClient::connect(tm_grpc.to_string()) .await - .change_context(Error::Connection)?; - let query_client = QueryClient::connect(tm_grpc.to_string()) + .change_context(Error::Connection) + .attach_printable(tm_grpc.clone())?; + let auth_query_client = AuthQueryClient::connect(tm_grpc.to_string()) + .await + .change_context(Error::Connection) + .attach_printable(tm_grpc.clone())?; + let bank_query_client = BankQueryClient::connect(tm_grpc.to_string()) .await - .change_context(Error::Connection)?; - let ecdsa_client = SharableEcdsaClient::new( - MultisigClient::connect(tofnd_config.party_uid, tofnd_config.url) - .await - .change_context(Error::Connection)?, - ); - let address = pub_key - .account_id(PREFIX) - .expect("failed to convert to account identifier") - .into(); + .change_context(Error::Connection) + .attach_printable(tm_grpc)?; + let multisig_client = MultisigClient::new(tofnd_config.party_uid, tofnd_config.url.clone()) + .await + .change_context(Error::Connection) + .attach_printable(tofnd_config.url)?; + + broadcaster::confirm_tx::TxConfirmer::new( + service_client.clone(), + RetryPolicy::RepeatConstant { + sleep: broadcast.tx_fetch_interval, + max_attempts: broadcast.tx_fetch_max_retries.saturating_add(1).into(), + }, + tx_hashes_to_confirm, + confirmed_txs, + ) + .run() + .await + .change_context(Error::TxConfirmation)?; - broadcaster::BroadcastClient::builder() + let basic_broadcaster = broadcaster::UnvalidatedBasicBroadcaster::builder() .client(service_client) - .signer(ecdsa_client) - .query_client(query_client) + .signer(multisig_client) + .auth_query_client(auth_query_client) + .bank_query_client(bank_query_client) .pub_key((tofnd_config.key_uid, pub_key)) .config(broadcast) - .address(address) + .address_prefix(PREFIX.to_string()) .build() - .broadcast(vec![tx]) + .validate_fee_denomination() .await - .change_context(Error::Broadcaster) + .change_context(Error::Broadcaster)?; + Ok(basic_broadcaster) } diff --git a/ampd/src/commands/register_chain_support.rs b/ampd/src/commands/register_chain_support.rs index 7e527c845..68c3d8070 100644 --- a/ampd/src/commands/register_chain_support.rs +++ b/ampd/src/commands/register_chain_support.rs @@ -1,5 +1,3 @@ -use std::path::Path; - use axelar_wasm_std::nonempty; use cosmrs::cosmwasm::MsgExecuteContract; use cosmrs::tx::Msg; @@ -19,8 +17,8 @@ pub struct Args { pub chains: Vec, } -pub async fn run(config: Config, state_path: &Path, args: Args) -> Result, Error> { - let pub_key = verifier_pub_key(state_path, config.tofnd_config.clone()).await?; +pub async fn run(config: Config, args: Args) -> Result, Error> { + let pub_key = verifier_pub_key(config.tofnd_config.clone()).await?; let msg = serde_json::to_vec(&ExecuteMsg::RegisterChainSupport { service_name: args.service_name.into(), diff --git a/ampd/src/commands/register_public_key.rs b/ampd/src/commands/register_public_key.rs index e0d6c22fe..b68ee6edc 100644 --- a/ampd/src/commands/register_public_key.rs +++ b/ampd/src/commands/register_public_key.rs @@ -1,39 +1,67 @@ -use std::convert::TryFrom; -use std::convert::TryInto; -use std::path::Path; +use std::convert::{TryFrom, TryInto}; -use cosmrs::{cosmwasm::MsgExecuteContract, tx::Msg}; +use cosmrs::cosmwasm::MsgExecuteContract; +use cosmrs::tx::Msg; use error_stack::{Result, ResultExt}; -use multisig::{ - key::{KeyType, PublicKey}, - msg::ExecuteMsg, -}; +use multisig::key::PublicKey; +use multisig::msg::ExecuteMsg; use report::ResultCompatExt; use sha3::{Digest, Keccak256}; use tracing::info; +use valuable::Valuable; use crate::commands::{broadcast_tx, verifier_pub_key}; use crate::config::Config; -use crate::tofnd::grpc::{MultisigClient, SharableEcdsaClient}; +use crate::tofnd::grpc::{Multisig, MultisigClient}; +use crate::tofnd::{self}; use crate::types::TMAddress; use crate::{handlers, Error, PREFIX}; -pub async fn run(config: Config, state_path: &Path) -> Result, Error> { - let pub_key = verifier_pub_key(state_path, config.tofnd_config.clone()).await?; +#[derive(clap::ValueEnum, Clone, Debug, Valuable, Copy)] +enum KeyType { + Ecdsa, + Ed25519, +} + +impl From for tofnd::Algorithm { + fn from(val: KeyType) -> Self { + match val { + KeyType::Ecdsa => tofnd::Algorithm::Ecdsa, + KeyType::Ed25519 => tofnd::Algorithm::Ed25519, + } + } +} + +impl From for multisig::key::KeyType { + fn from(val: KeyType) -> Self { + match val { + KeyType::Ecdsa => multisig::key::KeyType::Ecdsa, + KeyType::Ed25519 => multisig::key::KeyType::Ed25519, + } + } +} + +#[derive(clap::Args, Debug, Valuable)] +pub struct Args { + key_type: KeyType, +} - let multisig_address = get_multisig_address(&config)?; +pub async fn run(config: Config, args: Args) -> Result, Error> { + let pub_key = verifier_pub_key(config.tofnd_config.clone()).await?; + + let multisig_address = multisig_address(&config)?; let tofnd_config = config.tofnd_config.clone(); - let ecdsa_client = SharableEcdsaClient::new( - MultisigClient::connect(tofnd_config.party_uid, tofnd_config.url) - .await - .change_context(Error::Connection)?, - ); - let multisig_key = ecdsa_client - .keygen(&multisig_address.to_string()) + let multisig_client = MultisigClient::new(tofnd_config.party_uid, tofnd_config.url.clone()) + .await + .change_context(Error::Connection) + .attach_printable(tofnd_config.url)?; + let multisig_key = multisig_client + .keygen(&multisig_address.to_string(), args.key_type.into()) .await .change_context(Error::Tofnd)?; + info!(key_id = multisig_address.to_string(), "keygen successful"); let sender = pub_key.account_id(PREFIX).change_context(Error::Tofnd)?; @@ -43,18 +71,19 @@ pub async fn run(config: Config, state_path: &Path) -> Result, Er .try_into() .expect("wrong length"); - let signed_sender_address = ecdsa_client + let signed_sender_address = multisig_client .sign( &multisig_address.to_string(), address_hash.into(), &multisig_key, + args.key_type.into(), ) .await .change_context(Error::Tofnd)? .into(); let msg = serde_json::to_vec(&ExecuteMsg::RegisterPublicKey { - public_key: PublicKey::try_from((KeyType::Ecdsa, multisig_key.to_bytes().into())) + public_key: PublicKey::try_from((args.key_type.into(), multisig_key.to_bytes().into())) .change_context(Error::Tofnd)?, signed_sender_address, }) @@ -77,7 +106,7 @@ pub async fn run(config: Config, state_path: &Path) -> Result, Er ))) } -fn get_multisig_address(config: &Config) -> Result { +fn multisig_address(config: &Config) -> Result { config .handlers .iter() diff --git a/ampd/src/commands/send_tokens.rs b/ampd/src/commands/send_tokens.rs new file mode 100644 index 000000000..c89fa6ddb --- /dev/null +++ b/ampd/src/commands/send_tokens.rs @@ -0,0 +1,41 @@ +use axelar_wasm_std::nonempty; +use cosmrs::bank::MsgSend; +use cosmrs::tx::Msg; +use cosmrs::{AccountId, Coin}; +use error_stack::Result; +use report::ResultCompatExt; +use valuable::Valuable; + +use crate::commands::{broadcast_tx, verifier_pub_key}; +use crate::config::Config; +use crate::{Error, PREFIX}; + +#[derive(clap::Args, Debug, Valuable)] +pub struct Args { + pub to_address: nonempty::String, + pub amount: u128, + pub denom: nonempty::String, +} + +pub async fn run(config: Config, args: Args) -> Result, Error> { + let coin = Coin::new(args.amount, args.denom.as_str()).change_context(Error::InvalidInput)?; + let pub_key = verifier_pub_key(config.tofnd_config.clone()).await?; + + let tx = MsgSend { + to_address: args + .to_address + .parse::() + .change_context(Error::InvalidInput)?, + from_address: pub_key.account_id(PREFIX).change_context(Error::Tofnd)?, + amount: vec![coin], + } + .into_any() + .expect("failed to serialize proto message"); + + let tx_hash = broadcast_tx(config, tx, pub_key).await?.txhash; + + Ok(Some(format!( + "successfully broadcast send transaction, tx hash: {}", + tx_hash + ))) +} diff --git a/ampd/src/commands/unbond_verifier.rs b/ampd/src/commands/unbond_verifier.rs new file mode 100644 index 000000000..f17f4919f --- /dev/null +++ b/ampd/src/commands/unbond_verifier.rs @@ -0,0 +1,41 @@ +use axelar_wasm_std::nonempty; +use cosmrs::cosmwasm::MsgExecuteContract; +use cosmrs::tx::Msg; +use error_stack::Result; +use report::ResultCompatExt; +use service_registry::msg::ExecuteMsg; +use valuable::Valuable; + +use crate::commands::{broadcast_tx, verifier_pub_key}; +use crate::config::Config; +use crate::{Error, PREFIX}; + +#[derive(clap::Args, Debug, Valuable)] +pub struct Args { + pub service_name: nonempty::String, +} + +pub async fn run(config: Config, args: Args) -> Result, Error> { + let pub_key = verifier_pub_key(config.tofnd_config.clone()).await?; + + let msg = serde_json::to_vec(&ExecuteMsg::UnbondVerifier { + service_name: args.service_name.into(), + }) + .expect("unbond verifier msg should be serializable"); + + let tx = MsgExecuteContract { + sender: pub_key.account_id(PREFIX).change_context(Error::Tofnd)?, + contract: config.service_registry.cosmwasm_contract.as_ref().clone(), + msg, + funds: vec![], + } + .into_any() + .expect("failed to serialize proto message"); + + let tx_hash = broadcast_tx(config, tx, pub_key).await?.txhash; + + Ok(Some(format!( + "successfully broadcast unbond verifier transaction, tx hash: {}", + tx_hash + ))) +} diff --git a/ampd/src/commands/verifier_address.rs b/ampd/src/commands/verifier_address.rs index 3378744fa..d3b9f70f8 100644 --- a/ampd/src/commands/verifier_address.rs +++ b/ampd/src/commands/verifier_address.rs @@ -1,16 +1,13 @@ -use std::path::Path; - use axelar_wasm_std::FnExt; use error_stack::Result; use report::ResultCompatExt; use crate::commands::verifier_pub_key; use crate::tofnd::Config as TofndConfig; -use crate::Error; -use crate::PREFIX; +use crate::{Error, PREFIX}; -pub async fn run(config: TofndConfig, state_path: &Path) -> Result, Error> { - verifier_pub_key(state_path, config) +pub async fn run(config: TofndConfig) -> Result, Error> { + verifier_pub_key(config) .await .and_then(|pub_key| pub_key.account_id(PREFIX).change_context(Error::Tofnd))? .then(|account_id| Ok(Some(format!("verifier address: {}", account_id)))) diff --git a/ampd/src/config.rs b/ampd/src/config.rs index e97311dad..0a1609945 100644 --- a/ampd/src/config.rs +++ b/ampd/src/config.rs @@ -1,13 +1,13 @@ use std::net::{Ipv4Addr, SocketAddrV4}; -use std::time::Duration; use serde::{Deserialize, Serialize}; -use crate::broadcaster; use crate::commands::ServiceRegistryConfig; -use crate::handlers::{self, config::deserialize_handler_configs}; +use crate::handlers::config::deserialize_handler_configs; +use crate::handlers::{self}; use crate::tofnd::Config as TofndConfig; use crate::url::Url; +use crate::{broadcaster, event_processor}; #[derive(Deserialize, Serialize, Debug, PartialEq)] #[serde(default)] @@ -15,9 +15,7 @@ pub struct Config { pub health_check_bind_addr: SocketAddrV4, pub tm_jsonrpc: Url, pub tm_grpc: Url, - pub event_buffer_cap: usize, - #[serde(with = "humantime_serde")] - pub event_stream_timeout: Duration, + pub event_processor: event_processor::Config, pub broadcast: broadcaster::Config, #[serde(deserialize_with = "deserialize_handler_configs")] pub handlers: Vec, @@ -33,8 +31,7 @@ impl Default for Config { broadcast: broadcaster::Config::default(), handlers: vec![], tofnd_config: TofndConfig::default(), - event_buffer_cap: 100000, - event_stream_timeout: Duration::from_secs(15), + event_processor: event_processor::Config::default(), service_registry: ServiceRegistryConfig::default(), health_check_bind_addr: SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 3000), } @@ -51,17 +48,14 @@ mod tests { use std::time::Duration; use cosmrs::AccountId; - use router_api::ChainName; + use super::Config; use crate::evm::finalizer::Finalization; - use crate::handlers::config::Chain; - use crate::handlers::config::Config as HandlerConfig; + use crate::handlers::config::{Chain, Config as HandlerConfig}; use crate::types::TMAddress; use crate::url::Url; - use super::Config; - const PREFIX: &str = "axelar"; #[test] @@ -116,6 +110,16 @@ mod tests { [handlers.rpc_timeout] secs = 3 nanos = 0 + + [[handlers]] + type = 'MvxMsgVerifier' + cosmwasm_contract = '{}' + proxy_url = 'http://localhost:7545' + + [[handlers]] + type = 'MvxVerifierSetVerifier' + cosmwasm_contract = '{}' + proxy_url = 'http://localhost:7545' ", TMAddress::random(PREFIX), TMAddress::random(PREFIX), @@ -123,10 +127,12 @@ mod tests { TMAddress::random(PREFIX), TMAddress::random(PREFIX), TMAddress::random(PREFIX), + TMAddress::random(PREFIX), + TMAddress::random(PREFIX), ); let cfg: Config = toml::from_str(config_str.as_str()).unwrap(); - assert_eq!(cfg.handlers.len(), 6); + assert_eq!(cfg.handlers.len(), 8); } #[test] @@ -306,6 +312,18 @@ mod tests { rpc_url: Url::from_str("http://127.0.0.1").unwrap(), rpc_timeout: Some(Duration::from_secs(3)), }, + HandlerConfig::MvxMsgVerifier { + cosmwasm_contract: TMAddress::from( + AccountId::new("axelar", &[0u8; 32]).unwrap(), + ), + proxy_url: Url::from_str("http://127.0.0.1").unwrap(), + }, + HandlerConfig::MvxVerifierSetVerifier { + cosmwasm_contract: TMAddress::from( + AccountId::new("axelar", &[0u8; 32]).unwrap(), + ), + proxy_url: Url::from_str("http://127.0.0.1").unwrap(), + }, ], ..Config::default() } diff --git a/ampd/src/error.rs b/ampd/src/error.rs deleted file mode 100644 index 61562ca35..000000000 --- a/ampd/src/error.rs +++ /dev/null @@ -1,24 +0,0 @@ -use thiserror::Error; -use tracing::error; - -#[derive(Error, Debug)] -pub enum Error { - #[error("failed to load config, falling back on default")] - LoadConfig, - #[error("{0} is not a valid location to persist state")] - StateLocation(String), - #[error("event sub failed")] - EventSub, - #[error("event processor failed")] - EventProcessor, - #[error("broadcaster failed")] - Broadcaster, - #[error("state updater failed")] - StateUpdater, - #[error("tofnd failed")] - Tofnd, - #[error("connection failed")] - Connection, - #[error("task execution failed")] - Task, -} diff --git a/ampd/src/event_processor.rs b/ampd/src/event_processor.rs index 14fe2606e..c0d3e3437 100644 --- a/ampd/src/event_processor.rs +++ b/ampd/src/event_processor.rs @@ -7,16 +7,16 @@ use error_stack::{Context, Result, ResultExt}; use events::Event; use futures::StreamExt; use report::LoggableError; +use serde::{Deserialize, Serialize}; use thiserror::Error; use tokio::time::timeout; use tokio_stream::Stream; use tokio_util::sync::CancellationToken; -use tracing::warn; +use tracing::{info, warn}; use valuable::Valuable; use crate::asyncutil::future::{self, RetryPolicy}; use crate::asyncutil::task::TaskError; -use crate::handlers::chain; use crate::queue::queued_broadcaster::BroadcasterClient; #[async_trait] @@ -24,14 +24,6 @@ pub trait EventHandler { type Err: Context; async fn handle(&self, event: &Event) -> Result, Self::Err>; - - fn chain(self, handler: H) -> chain::Handler - where - Self: Sized, - H: EventHandler, - { - chain::Handler::new(self, handler) - } } #[derive(Error, Debug)] @@ -44,14 +36,36 @@ pub enum Error { Tasks(#[from] TaskError), } +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] +pub struct Config { + #[serde(with = "humantime_serde")] + pub retry_delay: Duration, + pub retry_max_attempts: u64, + #[serde(with = "humantime_serde")] + pub stream_timeout: Duration, + pub stream_buffer_size: usize, +} + +impl Default for Config { + fn default() -> Self { + Self { + retry_delay: Duration::from_secs(1), + retry_max_attempts: 3, + stream_timeout: Duration::from_secs(15), + stream_buffer_size: 100000, + } + } +} + /// Let the `handler` consume events from the `event_stream`. The token is checked for cancellation /// at the end of each consumed block or when the `event_stream` times out. If the token is cancelled or the /// `event_stream` is closed, the function returns pub async fn consume_events( + handler_label: String, handler: H, broadcaster: B, event_stream: S, - stream_timeout: Duration, + event_processor_config: Config, token: CancellationToken, ) -> Result<(), Error> where @@ -62,12 +76,30 @@ where { let mut event_stream = Box::pin(event_stream); loop { - let stream_status = retrieve_next_event(&mut event_stream, stream_timeout) - .await - .change_context(Error::EventStream)?; + let stream_status = + retrieve_next_event(&mut event_stream, event_processor_config.stream_timeout) + .await + .change_context(Error::EventStream)?; if let StreamStatus::Active(event) = &stream_status { - handle_event(&handler, &broadcaster, event).await?; + handle_event( + &handler, + &broadcaster, + event, + RetryPolicy::RepeatConstant { + sleep: event_processor_config.retry_delay, + max_attempts: event_processor_config.retry_max_attempts, + }, + ) + .await?; + } + + if let StreamStatus::Active(Event::BlockEnd(height)) = &stream_status { + info!( + handler = handler_label, + height = height.value(), + "handler finished processing block" + ); } if should_task_stop(stream_status, &token) { @@ -76,22 +108,18 @@ where } } -async fn handle_event(handler: &H, broadcaster: &B, event: &Event) -> Result<(), Error> +async fn handle_event( + handler: &H, + broadcaster: &B, + event: &Event, + retry_policy: RetryPolicy, +) -> Result<(), Error> where H: EventHandler, B: BroadcasterClient, { // if handlers run into errors we log them and then move on to the next event - match future::with_retry( - || handler.handle(event), - // TODO: make timeout and max_attempts configurable - RetryPolicy::RepeatConstant { - sleep: Duration::from_secs(1), - max_attempts: 3, - }, - ) - .await - { + match future::with_retry(|| handler.handle(event), retry_policy).await { Ok(msgs) => { for msg in msgs { broadcaster @@ -147,6 +175,8 @@ enum StreamStatus { #[cfg(test)] mod tests { + use std::time::Duration; + use async_trait::async_trait; use cosmrs::bank::MsgSend; use cosmrs::tx::Msg; @@ -155,15 +185,24 @@ mod tests { use events::Event; use futures::stream; use mockall::mock; - use std::time::Duration; use tokio::time::timeout; use tokio_util::sync::CancellationToken; use crate::event_processor; - use crate::{ - event_processor::{consume_events, Error, EventHandler}, - queue::queued_broadcaster::MockBroadcasterClient, - }; + use crate::event_processor::{consume_events, Config, Error, EventHandler}; + use crate::queue::queued_broadcaster::MockBroadcasterClient; + + pub fn setup_event_config( + retry_delay_value: Duration, + stream_timeout_value: Duration, + ) -> Config { + Config { + retry_delay: retry_delay_value, + retry_max_attempts: 3, + stream_timeout: stream_timeout_value, + stream_buffer_size: 100000, + } + } #[tokio::test] async fn stop_when_stream_closes() { @@ -180,14 +219,16 @@ mod tests { .returning(|_| Ok(vec![])); let broadcaster = MockBroadcasterClient::new(); + let event_config = setup_event_config(Duration::from_secs(1), Duration::from_secs(1000)); let result_with_timeout = timeout( Duration::from_secs(1), consume_events( + "handler".to_string(), handler, broadcaster, stream::iter(events), - Duration::from_secs(1000), + event_config, CancellationToken::new(), ), ) @@ -208,14 +249,16 @@ mod tests { handler.expect_handle().times(1).returning(|_| Ok(vec![])); let broadcaster = MockBroadcasterClient::new(); + let event_config = setup_event_config(Duration::from_secs(1), Duration::from_secs(1000)); let result_with_timeout = timeout( Duration::from_secs(1), consume_events( + "handler".to_string(), handler, broadcaster, stream::iter(events), - Duration::from_secs(1000), + event_config, CancellationToken::new(), ), ) @@ -237,14 +280,16 @@ mod tests { .returning(|_| Err(report!(EventHandlerError::Failed))); let broadcaster = MockBroadcasterClient::new(); + let event_config = setup_event_config(Duration::from_secs(1), Duration::from_secs(1000)); let result_with_timeout = timeout( Duration::from_secs(3), consume_events( + "handler".to_string(), handler, broadcaster, stream::iter(events), - Duration::from_secs(1000), + event_config, CancellationToken::new(), ), ) @@ -265,6 +310,7 @@ mod tests { .once() .returning(|_| Ok(vec![dummy_msg(), dummy_msg()])); + let event_config = setup_event_config(Duration::from_secs(1), Duration::from_secs(1000)); let mut broadcaster = MockBroadcasterClient::new(); broadcaster .expect_broadcast() @@ -274,10 +320,11 @@ mod tests { let result_with_timeout = timeout( Duration::from_secs(3), consume_events( + "handler".to_string(), handler, broadcaster, stream::iter(events), - Duration::from_secs(1000), + event_config, CancellationToken::new(), ), ) @@ -301,6 +348,7 @@ mod tests { handler.expect_handle().times(4).returning(|_| Ok(vec![])); let broadcaster = MockBroadcasterClient::new(); + let event_config = setup_event_config(Duration::from_secs(1), Duration::from_secs(1000)); let token = CancellationToken::new(); token.cancel(); @@ -308,10 +356,11 @@ mod tests { let result_with_timeout = timeout( Duration::from_secs(1), consume_events( + "handler".to_string(), handler, broadcaster, stream::iter(events), - Duration::from_secs(1000), + event_config, token, ), ) @@ -326,6 +375,7 @@ mod tests { let handler = MockEventHandler::new(); let broadcaster = MockBroadcasterClient::new(); + let event_config = setup_event_config(Duration::from_secs(1), Duration::from_secs(0)); let token = CancellationToken::new(); token.cancel(); @@ -333,10 +383,11 @@ mod tests { let result_with_timeout = timeout( Duration::from_secs(1), consume_events( + "handler".to_string(), handler, broadcaster, stream::pending::>(), // never returns any items so it can time out - Duration::from_secs(0), + event_config, token, ), ) diff --git a/ampd/src/event_sub.rs b/ampd/src/event_sub.rs index d3551d097..512b0ee4c 100644 --- a/ampd/src/event_sub.rs +++ b/ampd/src/event_sub.rs @@ -1,24 +1,20 @@ use std::iter; use std::time::Duration; -use error_stack::ResultExt; -use error_stack::{FutureExt, Report, Result}; +use error_stack::{FutureExt, Report, Result, ResultExt}; +use events::Event; use futures::TryStreamExt; use mockall::automock; use tendermint::block; use thiserror::Error; -use tokio::select; use tokio::sync::broadcast::{self, Sender}; -use tokio::time; +use tokio::{select, time}; use tokio_stream::wrappers::errors::BroadcastStreamRecvError; use tokio_stream::wrappers::BroadcastStream; -use tokio_stream::{Stream, StreamExt}; +use tokio_stream::Stream; use tokio_util::sync::CancellationToken; use tracing::info; -use events::Event; - -use crate::asyncutil::future::{self, RetryPolicy}; use crate::tm_client::TmClient; #[automock] @@ -40,7 +36,6 @@ impl EventSub for EventSubscriber { pub struct EventPublisher { tm_client: T, - start_from: Option, poll_interval: Duration, tx: Sender, } @@ -50,7 +45,6 @@ impl EventPublisher { let (tx, _) = broadcast::channel::(capacity); let publisher = EventPublisher { tm_client: client, - start_from: None, poll_interval: Duration::new(5, 0), tx: tx.clone(), }; @@ -59,22 +53,8 @@ impl EventPublisher { (publisher, subscriber) } - pub fn start_from(mut self, height: block::Height) -> Self { - self.start_from = Some(height); - self - } - - #[allow(dead_code)] - pub fn poll_interval(mut self, poll_interval: Duration) -> Self { - self.poll_interval = poll_interval; - self - } - pub async fn run(mut self, token: CancellationToken) -> Result<(), EventSubError> { - let mut curr_block_height = match self.start_from { - Some(start_from) => start_from, - None => self.latest_block_height().await?, - }; + let mut curr_block_height = self.latest_block_height().await?; let mut interval = time::interval(self.poll_interval); loop { @@ -144,20 +124,13 @@ impl EventPublisher { } async fn events(&self, block_height: block::Height) -> Result, EventSubError> { - let block_results = future::with_retry( - || { - self.tm_client.block_results(block_height).change_context( - EventSubError::EventQuery { - block: block_height, - }, - ) - }, - RetryPolicy::RepeatConstant { - sleep: Duration::from_secs(1), - max_attempts: 3, - }, - ) - .await?; + let block_results = self + .tm_client + .block_results(block_height) + .change_context(EventSubError::EventQuery { + block: block_height, + }) + .await?; let begin_block_events = block_results.begin_block_events.into_iter().flatten(); let tx_events = block_results @@ -175,13 +148,6 @@ impl EventPublisher { } } -pub fn skip_to_block( - stream: impl Stream>, - height: block::Height, -) -> impl Stream> { - stream.skip_while(move |event| !matches!(event, Ok(Event::BlockBegin(h)) if *h >= height)) -} - #[derive(Error, Debug)] pub enum EventSubError { #[error("querying events for block {block} failed")] @@ -203,109 +169,16 @@ mod tests { use mockall::predicate::eq; use rand::Rng; use random_string::generate; - use tendermint::block; use tendermint::{abci, AppHash}; use tokio::sync::{mpsc, oneshot}; use tokio::test; - use tokio_stream::wrappers::ReceiverStream; use tokio_util::sync::CancellationToken; - use crate::event_sub::{skip_to_block, Event, EventPublisher, EventSub}; + use crate::event_sub::{Event, EventPublisher, EventSub}; use crate::tm_client; #[test] - async fn skip_to_block_should_work() { - let (tx, rx) = mpsc::channel(100); - let skip_to: block::Height = 5u32.into(); - - for i in 1u32..10 { - tx.send(Event::BlockBegin(i.into())).await.unwrap(); - tx.send(Event::BlockEnd(i.into())).await.unwrap(); - } - - let mut stream = skip_to_block::<()>(ReceiverStream::new(rx).map(Ok), skip_to); - - assert_eq!( - stream.next().await.unwrap().unwrap(), - Event::BlockBegin(skip_to) - ); - assert_eq!( - stream.next().await.unwrap().unwrap(), - Event::BlockEnd(skip_to) - ); - assert_eq!( - stream.next().await.unwrap().unwrap(), - Event::BlockBegin(skip_to.increment()) - ); - assert_eq!( - stream.next().await.unwrap().unwrap(), - Event::BlockEnd(skip_to.increment()) - ); - } - - #[test] - async fn start_from_should_work() { - let block_count = 10; - let block: tendermint::Block = - serde_json::from_str(include_str!("tests/axelar_block.json")).unwrap(); - let from_height = (block.header.height.value() - block_count + 1) - .try_into() - .unwrap(); - let to_height = block.header.height; - - let mut mock_client = tm_client::MockTmClient::new(); - mock_client.expect_latest_block().once().returning(move || { - Ok(tm_client::BlockResponse { - block_id: Default::default(), - block: block.clone(), - }) - }); - mock_client - .expect_block_results() - .times(block_count as usize) - .returning(|height| { - Ok(tm_client::BlockResultsResponse { - height, - begin_block_events: None, - end_block_events: None, - consensus_param_updates: None, - txs_results: None, - validator_updates: vec![], - app_hash: AppHash::default(), - finalize_block_events: vec![], - }) - }); - - let token = CancellationToken::new(); - let (event_publisher, event_subcriber) = - EventPublisher::new(mock_client, 2 * block_count as usize); - let event_publisher = event_publisher.start_from(from_height); - let mut stream = event_subcriber.subscribe(); - - let child_token = token.child_token(); - let handle = tokio::spawn(async move { event_publisher.run(child_token).await }); - - for height in from_height.value()..to_height.value() { - let event = stream.next().await; - assert_eq!( - event.unwrap().unwrap(), - Event::BlockBegin(height.try_into().unwrap()) - ); - - let event = stream.next().await; - assert_eq!( - event.unwrap().unwrap(), - Event::BlockEnd(height.try_into().unwrap()) - ); - } - - token.cancel(); - - assert!(handle.await.is_ok()); - } - - #[test] - async fn should_start_from_latest_when_none_is_given() { + async fn should_start_from_the_latest_block() { let block: tendermint::Block = serde_json::from_str(include_str!("tests/axelar_block.json")).unwrap(); let height = block.header.height; @@ -356,9 +229,6 @@ mod tests { let latest_block: tendermint::Block = serde_json::from_str(include_str!("tests/axelar_block.json")).unwrap(); let latest_block_height = latest_block.header.height; - let start_from_block_height = (latest_block.header.height.value() - 100) - .try_into() - .unwrap(); let (sub_tx, mut sub_rx) = oneshot::channel::<()>(); let (pub_tx, mut pub_rx) = mpsc::channel::(100); @@ -397,10 +267,8 @@ mod tests { }); let token = CancellationToken::new(); - let (event_publisher, event_subcriber) = EventPublisher::new(mock_client, 100); - let event_publisher = event_publisher - .start_from(start_from_block_height) - .poll_interval(Duration::from_millis(500)); + let (mut event_publisher, event_subcriber) = EventPublisher::new(mock_client, 100); + event_publisher.poll_interval = Duration::from_millis(500); let handle = tokio::spawn(event_publisher.run(token.child_token())); while let Some(call_count) = pub_rx.recv().await { @@ -472,18 +340,27 @@ mod tests { let mut latest_block_call_count = 0; mock_client .expect_latest_block() - .times(block_count) - .returning(move || { - let mut block = block.clone(); - block.header.height = (block_height.value() + latest_block_call_count) - .try_into() - .unwrap(); - - latest_block_call_count += 1; - Ok(tm_client::BlockResponse { - block_id: Default::default(), - block, - }) + .times(block_count + 1) + .returning(move || match latest_block_call_count { + 0 => { + latest_block_call_count += 1; + Ok(tm_client::BlockResponse { + block_id: Default::default(), + block: block.clone(), + }) + } + _ => { + let mut block = block.clone(); + block.header.height = (block_height.value() + latest_block_call_count - 1) + .try_into() + .unwrap(); + + latest_block_call_count += 1; + Ok(tm_client::BlockResponse { + block_id: Default::default(), + block, + }) + } }); mock_client .expect_block_results() @@ -496,11 +373,9 @@ mod tests { }); let token = CancellationToken::new(); - let (event_publisher, event_subcriber) = + let (mut event_publisher, event_subcriber) = EventPublisher::new(mock_client, block_count * event_count_per_block); - let event_publisher = event_publisher - .start_from(block_height) - .poll_interval(Duration::new(0, 1e7 as u32)); + event_publisher.poll_interval = Duration::from_millis(10); let mut stream = event_subcriber.subscribe(); let child_token = token.child_token(); diff --git a/ampd/src/evm/finalizer.rs b/ampd/src/evm/finalizer.rs index 9e0551a2f..f47cdbf47 100644 --- a/ampd/src/evm/finalizer.rs +++ b/ampd/src/evm/finalizer.rs @@ -1,6 +1,6 @@ use async_trait::async_trait; use error_stack::{self, Report, ResultExt}; -use ethers::types::U64; +use ethers_core::types::U64; use mockall::automock; use serde::{Deserialize, Serialize}; @@ -116,13 +116,12 @@ where #[cfg(test)] mod tests { + use ethers_core::abi::Hash; + use ethers_core::types::{Block, U64}; + use tokio::test; + use crate::evm::finalizer::{pick, ConfirmationHeightFinalizer, Finalization, Finalizer}; use crate::evm::json_rpc::MockEthereumClient; - use ethers::{ - abi::Hash, - types::{Block, U64}, - }; - use tokio::test; #[test] async fn latest_finalized_block_height_should_work() { diff --git a/ampd/src/evm/json_rpc.rs b/ampd/src/evm/json_rpc.rs index 845c0c824..983ef7eb0 100644 --- a/ampd/src/evm/json_rpc.rs +++ b/ampd/src/evm/json_rpc.rs @@ -1,7 +1,7 @@ use async_trait::async_trait; -use ethers::providers::{JsonRpcClient, ProviderError}; -use ethers::types::{Block, BlockNumber, TransactionReceipt, H256, U64}; -use ethers::utils::serialize; +use ethers_core::types::{Block, BlockNumber, TransactionReceipt, H256, U64}; +use ethers_core::utils::serialize; +use ethers_providers::{JsonRpcClient, ProviderError}; use mockall::automock; use crate::json_rpc::Client; @@ -17,10 +17,6 @@ pub trait EthereumClient { async fn transaction_receipt(&self, hash: H256) -> Result>; } -#[async_trait] -pub trait MoonbeamClient: EthereumClient { - async fn finalized_block_hash(&self) -> Result; -} #[async_trait] impl

EthereumClient for Client

where @@ -42,13 +38,3 @@ where self.request("eth_getTransactionReceipt", [hash]).await } } - -#[async_trait] -impl

MoonbeamClient for Client

-where - P: JsonRpcClient + Send + Sync + 'static, -{ - async fn finalized_block_hash(&self) -> Result { - self.request("chain_getFinalizedHead", ()).await - } -} diff --git a/ampd/src/evm/verifier.rs b/ampd/src/evm/verifier.rs index ee79606d6..b29c702f0 100644 --- a/ampd/src/evm/verifier.rs +++ b/ampd/src/evm/verifier.rs @@ -1,9 +1,8 @@ -use ethers::contract::EthLogDecode; -use ethers::types::{Log, TransactionReceipt, H256}; -use num_traits::cast; - use axelar_wasm_std::voting::Vote; +use ethers_contract::EthLogDecode; +use ethers_core::types::{Log, TransactionReceipt, H256}; use evm_gateway::{IAxelarAmplifierGatewayEvents, WeightedSigners}; +use num_traits::cast; use crate::handlers::evm_verify_msg::Message; use crate::handlers::evm_verify_verifier_set::VerifierSetConfirmation; @@ -51,7 +50,7 @@ fn has_failed(tx_receipt: &TransactionReceipt) -> bool { tx_receipt.status == Some(0u64.into()) } -fn get_event<'a>( +fn event<'a>( gateway_address: &EVMAddress, tx_receipt: &'a TransactionReceipt, log_index: u32, @@ -84,7 +83,7 @@ where return Vote::FailedOnChain; } - match get_event(gateway_address, tx_receipt, expected_event_index) { + match event(gateway_address, tx_receipt, expected_event_index) { Some(event) if tx_receipt.transaction_hash == expected_transaction_hash && to_verify == event => { @@ -120,30 +119,23 @@ pub fn verify_verifier_set( mod tests { use axelar_wasm_std::voting::Vote; use cosmwasm_std::Uint128; - use ethers::{ - abi::{encode, Token}, - contract::EthEvent, - types::{Log, TransactionReceipt, H256}, - }; - use evm_gateway::{ - i_axelar_amplifier_gateway::{ContractCallFilter, SignersRotatedFilter}, - WeightedSigners, - }; - use multisig::{ - key::KeyType, - test::common::{build_verifier_set, ecdsa_test_data}, - }; + use ethers_contract::EthEvent; + use ethers_core::abi::{encode, Token}; + use ethers_core::types::{Log, TransactionReceipt, H256}; + use evm_gateway::i_axelar_amplifier_gateway::{ContractCallFilter, SignersRotatedFilter}; + use evm_gateway::WeightedSigners; + use multisig::key::KeyType; + use multisig::test::common::{build_verifier_set, ecdsa_test_data}; use super::{verify_message, verify_verifier_set}; - use crate::{ - handlers::{evm_verify_msg::Message, evm_verify_verifier_set::VerifierSetConfirmation}, - types::{EVMAddress, Hash}, - }; + use crate::handlers::evm_verify_msg::Message; + use crate::handlers::evm_verify_verifier_set::VerifierSetConfirmation; + use crate::types::{EVMAddress, Hash}; #[test] fn should_not_verify_verifier_set_if_tx_id_does_not_match() { let (gateway_address, tx_receipt, mut verifier_set) = - get_matching_verifier_set_and_tx_receipt(); + matching_verifier_set_and_tx_receipt(); verifier_set.tx_id = Hash::random(); assert_eq!( @@ -155,7 +147,7 @@ mod tests { #[test] fn should_not_verify_verifier_set_if_tx_failed() { let (gateway_address, mut tx_receipt, verifier_set) = - get_matching_verifier_set_and_tx_receipt(); + matching_verifier_set_and_tx_receipt(); tx_receipt.status = Some(0u64.into()); assert_eq!( @@ -166,7 +158,7 @@ mod tests { #[test] fn should_not_verify_verifier_set_if_gateway_address_does_not_match() { - let (_, tx_receipt, verifier_set) = get_matching_verifier_set_and_tx_receipt(); + let (_, tx_receipt, verifier_set) = matching_verifier_set_and_tx_receipt(); let gateway_address = EVMAddress::random(); assert_eq!( @@ -178,7 +170,7 @@ mod tests { #[test] fn should_not_verify_verifier_set_if_log_index_does_not_match() { let (gateway_address, tx_receipt, mut verifier_set) = - get_matching_verifier_set_and_tx_receipt(); + matching_verifier_set_and_tx_receipt(); verifier_set.event_index = 0; assert_eq!( @@ -200,7 +192,7 @@ mod tests { #[test] fn should_not_verify_verifier_set_if_verifier_set_does_not_match() { let (gateway_address, tx_receipt, mut verifier_set) = - get_matching_verifier_set_and_tx_receipt(); + matching_verifier_set_and_tx_receipt(); verifier_set.verifier_set.threshold = Uint128::from(50u64); assert_eq!( @@ -211,8 +203,7 @@ mod tests { #[test] fn should_verify_verifier_set_if_correct() { - let (gateway_address, tx_receipt, verifier_set) = - get_matching_verifier_set_and_tx_receipt(); + let (gateway_address, tx_receipt, verifier_set) = matching_verifier_set_and_tx_receipt(); assert_eq!( verify_verifier_set(&gateway_address, &tx_receipt, &verifier_set), @@ -222,7 +213,7 @@ mod tests { #[test] fn should_not_verify_msg_if_tx_id_does_not_match() { - let (gateway_address, tx_receipt, mut msg) = get_matching_msg_and_tx_receipt(); + let (gateway_address, tx_receipt, mut msg) = matching_msg_and_tx_receipt(); msg.tx_id = Hash::random(); assert_eq!( @@ -233,7 +224,7 @@ mod tests { #[test] fn should_not_verify_msg_if_tx_failed() { - let (gateway_address, mut tx_receipt, msg) = get_matching_msg_and_tx_receipt(); + let (gateway_address, mut tx_receipt, msg) = matching_msg_and_tx_receipt(); tx_receipt.status = Some(0u64.into()); assert_eq!( @@ -244,7 +235,7 @@ mod tests { #[test] fn should_not_verify_msg_if_gateway_address_does_not_match() { - let (_, tx_receipt, msg) = get_matching_msg_and_tx_receipt(); + let (_, tx_receipt, msg) = matching_msg_and_tx_receipt(); let gateway_address = EVMAddress::random(); assert_eq!( @@ -255,7 +246,7 @@ mod tests { #[test] fn should_not_verify_msg_if_log_index_does_not_match() { - let (gateway_address, tx_receipt, mut msg) = get_matching_msg_and_tx_receipt(); + let (gateway_address, tx_receipt, mut msg) = matching_msg_and_tx_receipt(); msg.event_index = 0; assert_eq!( @@ -276,7 +267,7 @@ mod tests { #[test] fn should_not_verify_msg_if_msg_does_not_match() { - let (gateway_address, tx_receipt, mut msg) = get_matching_msg_and_tx_receipt(); + let (gateway_address, tx_receipt, mut msg) = matching_msg_and_tx_receipt(); msg.source_address = EVMAddress::random(); assert_eq!( @@ -287,7 +278,7 @@ mod tests { #[test] fn should_verify_msg_if_correct() { - let (gateway_address, tx_receipt, msg) = get_matching_msg_and_tx_receipt(); + let (gateway_address, tx_receipt, msg) = matching_msg_and_tx_receipt(); assert_eq!( verify_message(&gateway_address, &tx_receipt, &msg), @@ -295,7 +286,7 @@ mod tests { ); } - fn get_matching_verifier_set_and_tx_receipt( + fn matching_verifier_set_and_tx_receipt( ) -> (EVMAddress, TransactionReceipt, VerifierSetConfirmation) { let tx_id = Hash::random(); let log_index = 1; @@ -334,7 +325,7 @@ mod tests { (gateway_address, tx_receipt, verifier_set) } - fn get_matching_msg_and_tx_receipt() -> (EVMAddress, TransactionReceipt, Message) { + fn matching_msg_and_tx_receipt() -> (EVMAddress, TransactionReceipt, Message) { let tx_id = Hash::random(); let log_index = 1; let gateway_address = EVMAddress::random(); diff --git a/ampd/src/grpc/client.rs b/ampd/src/grpc/client.rs new file mode 100644 index 000000000..44822fbc9 --- /dev/null +++ b/ampd/src/grpc/client.rs @@ -0,0 +1,276 @@ +use error_stack::{Report, Result}; +use tonic::{codegen, transport}; + +use super::proto::ampd_client::AmpdClient; +use super::proto::crypto_client::CryptoClient; + +pub struct Client { + pub ampd: AmpdClient, + pub crypto: CryptoClient, +} + +pub async fn new(dst: D) -> Result +where + D: TryInto, + D::Error: Into, +{ + let conn = transport::Endpoint::new(dst) + .map_err(Report::new)? + .connect() + .await + .map_err(Report::new)?; + + let ampd = AmpdClient::new(conn.clone()); + let crypto = CryptoClient::new(conn); + + Ok(Client { ampd, crypto }) +} + +#[cfg(test)] +mod tests { + use std::time::Duration; + + use cosmrs::bank::MsgSend; + use cosmrs::tx::Msg; + use cosmrs::{AccountId, Any}; + use error_stack::Report; + use events::Event; + use futures::StreamExt; + use k256::ecdsa::SigningKey; + use k256::sha2::{Digest, Sha256}; + use mockall::predicate; + use rand::rngs::OsRng; + use tokio::net::TcpListener; + use tokio::sync::{mpsc, oneshot}; + use tokio::{test, time}; + use tokio_stream::wrappers::errors::BroadcastStreamRecvError; + use tokio_stream::wrappers::{ReceiverStream, TcpListenerStream}; + use tonic::Code; + use url::Url; + + use crate::event_sub::MockEventSub; + use crate::grpc; + use crate::proto::{ + Algorithm, BroadcastRequest, BroadcastResponse, KeyRequest, KeyResponse, SignRequest, + SignResponse, SubscribeRequest, + }; + use crate::queue::queued_broadcaster::MockBroadcasterClient; + use crate::tofnd::grpc::MockMultisig; + use crate::tofnd::{self}; + use crate::types::PublicKey; + + async fn start_server( + event_sub: MockEventSub, + broadcaster: MockBroadcasterClient, + multisig_client: MockMultisig, + ) -> (Url, oneshot::Sender<()>) { + let (tx, rx) = oneshot::channel::<()>(); + let server = grpc::server::new(event_sub, broadcaster, multisig_client); + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + + tokio::spawn( + server.serve_with_incoming_shutdown(TcpListenerStream::new(listener), async { + drop(rx.await) + }), + ); + time::sleep(Duration::from_millis(100)).await; + + (format!("http://{addr}").parse().unwrap(), tx) + } + + #[test] + async fn key_should_work() { + let key_id = "key_id"; + let key: PublicKey = SigningKey::random(&mut OsRng).verifying_key().into(); + let algorithm = Algorithm::Ed25519; + + let mut multisig_client = MockMultisig::new(); + multisig_client + .expect_keygen() + .with( + predicate::eq(key_id), + predicate::eq(tofnd::Algorithm::from(algorithm)), + ) + .return_once(move |_, _| Ok(key)); + + let (url, tx) = start_server( + MockEventSub::new(), + MockBroadcasterClient::new(), + multisig_client, + ) + .await; + assert_eq!( + grpc::client::new(url.to_string()) + .await + .unwrap() + .crypto + .key(KeyRequest { + key_id: key_id.to_string(), + algorithm: algorithm.into(), + }) + .await + .unwrap() + .into_inner(), + KeyResponse { + pub_key: key.to_bytes() + } + ); + + tx.send(()).unwrap(); + } + + #[test] + async fn sign_should_work() { + let key_id = "key_id"; + let algorithm = Algorithm::Ed25519; + let key: PublicKey = SigningKey::random(&mut OsRng).verifying_key().into(); + let msg = b"message"; + let mut hasher = Sha256::new(); + hasher.update(msg); + let sign_digest: [u8; 32] = hasher.finalize().to_vec().try_into().unwrap(); + + let mut multisig_client = MockMultisig::new(); + multisig_client + .expect_keygen() + .with( + predicate::eq(key_id), + predicate::eq(tofnd::Algorithm::from(algorithm)), + ) + .return_once(move |_, _| Ok(key)); + multisig_client + .expect_sign() + .with( + predicate::eq(key_id), + predicate::eq(tofnd::MessageDigest::from(sign_digest)), + predicate::function(move |actual: &PublicKey| actual == &key), + predicate::eq(tofnd::Algorithm::from(algorithm)), + ) + .return_once(|_, _, _, _| Ok(vec![1; 64])); + + let (url, tx) = start_server( + MockEventSub::new(), + MockBroadcasterClient::new(), + multisig_client, + ) + .await; + assert_eq!( + grpc::client::new(url.to_string()) + .await + .unwrap() + .crypto + .sign(SignRequest { + key_id: key_id.to_string(), + msg: msg.to_vec(), + algorithm: algorithm.into(), + }) + .await + .unwrap() + .into_inner(), + SignResponse { + signature: vec![1; 64] + } + ); + + tx.send(()).unwrap(); + } + + #[test] + async fn broadcast_should_work() { + let msg = dummy_msg(); + + let mut broadcaster = MockBroadcasterClient::new(); + broadcaster + .expect_broadcast() + .with(predicate::eq(msg.clone())) + .return_once(|_| Ok(())); + + let (url, tx) = start_server(MockEventSub::new(), broadcaster, MockMultisig::new()).await; + assert_eq!( + grpc::client::new(url.to_string()) + .await + .unwrap() + .ampd + .broadcast(BroadcastRequest { msg: Some(msg) }) + .await + .unwrap() + .into_inner(), + BroadcastResponse {} + ); + + tx.send(()).unwrap(); + } + + #[test] + async fn subscribe_should_work() { + let mut event_sub = MockEventSub::new(); + let (event_tx, event_rx) = mpsc::channel(1); + event_sub + .expect_subscribe() + .return_once(|| Box::pin(ReceiverStream::new(event_rx))); + + let (url, tx) = + start_server(event_sub, MockBroadcasterClient::new(), MockMultisig::new()).await; + let mut stream = grpc::client::new(url.to_string()) + .await + .unwrap() + .ampd + .subscribe(SubscribeRequest { + event_filters: vec![], + include_block_begin_end: true, + }) + .await + .unwrap() + .into_inner(); + + let event = Event::BlockBegin(1u32.into()); + event_tx.send(Ok(event.clone())).await.unwrap(); + assert_eq!( + stream.next().await.unwrap().unwrap().event, + Some(event.into()) + ); + + let event = Event::Abci { + event_type: "some_event".into(), + attributes: serde_json::from_str("{\"key_1\":\"value_1\",\"key_2\":\"value_2\"}") + .unwrap(), + }; + event_tx.send(Ok(event.clone())).await.unwrap(); + assert_eq!( + stream.next().await.unwrap().unwrap().event, + Some(event.into()) + ); + + let event = Event::BlockEnd(1u32.into()); + event_tx.send(Ok(event.clone())).await.unwrap(); + assert_eq!( + stream.next().await.unwrap().unwrap().event, + Some(event.into()) + ); + + event_tx + .send(Err(Report::new(BroadcastStreamRecvError::Lagged(10)))) + .await + .unwrap(); + assert_eq!( + stream.next().await.unwrap().unwrap_err().code(), + Code::Internal + ); + + let event = Event::BlockBegin(2u32.into()); + assert!(event_tx.send(Ok(event.clone())).await.is_err()); + + drop(stream); + tx.send(()).unwrap(); + } + + fn dummy_msg() -> Any { + MsgSend { + from_address: AccountId::new("", &[1, 2, 3]).unwrap(), + to_address: AccountId::new("", &[4, 5, 6]).unwrap(), + amount: vec![], + } + .to_any() + .unwrap() + } +} diff --git a/ampd/src/grpc/mod.rs b/ampd/src/grpc/mod.rs index 1912a314a..26bd4efa2 100644 --- a/ampd/src/grpc/mod.rs +++ b/ampd/src/grpc/mod.rs @@ -1,15 +1,6 @@ -use tonic::transport::{server::Router, Server}; - -use crate::{event_sub::EventSubscriber, queue::queued_broadcaster::QueuedBroadcasterClient}; - -mod ampd; - -#[allow(dead_code)] -pub fn new_server( - event_subscriber: EventSubscriber, - broadcaster: QueuedBroadcasterClient, -) -> Router { - Server::builder().add_service(ampd::proto::ampd_server::AmpdServer::new( - ampd::Server::new(event_subscriber, broadcaster), - )) +pub mod proto { + tonic::include_proto!("ampd"); } + +pub mod client; +pub mod server; diff --git a/ampd/src/grpc/ampd.rs b/ampd/src/grpc/server/ampd.rs similarity index 96% rename from ampd/src/grpc/ampd.rs rename to ampd/src/grpc/server/ampd.rs index 8123e1d28..5cbe6a6e3 100644 --- a/ampd/src/grpc/ampd.rs +++ b/ampd/src/grpc/server/ampd.rs @@ -1,15 +1,14 @@ -use std::{future, pin::Pin}; +use std::future; +use std::pin::Pin; use async_trait::async_trait; use events::Event; use futures::{Stream, StreamExt, TryStreamExt}; use tonic::{Request, Response, Status}; -use crate::{event_sub::EventSub, queue::queued_broadcaster::BroadcasterClient}; - -pub mod proto { - tonic::include_proto!("ampd"); -} +use super::proto; +use crate::event_sub::EventSub; +use crate::queue::queued_broadcaster::BroadcasterClient; impl From for proto::subscribe_response::Event { fn from(event: Event) -> Self { @@ -115,7 +114,6 @@ where let req = req.into_inner(); let msg = req.msg.ok_or(Status::invalid_argument("missing msg"))?; - // TODO: validate msg by estimating its gas cost before broadcasting? self.broadcaster .broadcast(msg) .await @@ -130,8 +128,9 @@ mod tests { use std::collections::HashMap; use std::time::Duration; - use cosmos_sdk_proto::Any; - use cosmrs::{bank::MsgSend, tx::Msg, AccountId}; + use cosmrs::bank::MsgSend; + use cosmrs::tx::Msg; + use cosmrs::{AccountId, Any}; use error_stack::Report; use events::Event; use serde_json::Map; @@ -142,11 +141,12 @@ mod tests { use tokio_stream::StreamExt; use tonic::Code; - use crate::queue::queued_broadcaster; - use crate::{event_sub::MockEventSub, queue::queued_broadcaster::MockBroadcasterClient}; - - use super::proto::{self, ampd_server::Ampd}; + use super::proto::ampd_server::Ampd; + use super::proto::{self}; use super::Server; + use crate::event_sub::MockEventSub; + use crate::queue::queued_broadcaster; + use crate::queue::queued_broadcaster::MockBroadcasterClient; #[test] async fn subscribe_should_return_stream_of_error_when_event_subscriber_fails() { diff --git a/ampd/src/grpc/server/crypto.rs b/ampd/src/grpc/server/crypto.rs new file mode 100644 index 000000000..48e912616 --- /dev/null +++ b/ampd/src/grpc/server/crypto.rs @@ -0,0 +1,204 @@ +use async_trait::async_trait; +use k256::sha2::{Digest, Sha256}; +use tonic::{Request, Response, Status}; + +use super::proto; +use crate::tofnd::grpc::Multisig; +use crate::tofnd::{self}; +use crate::types::PublicKey; + +impl From for tofnd::Algorithm { + fn from(algorithm: proto::Algorithm) -> Self { + match algorithm { + proto::Algorithm::Ed25519 => Self::Ed25519, + proto::Algorithm::Ecdsa => Self::Ecdsa, + } + } +} + +pub struct Server { + multisig_client: M, +} + +impl Server +where + M: Multisig, +{ + pub fn new(multisig_client: M) -> Self { + Self { multisig_client } + } + + async fn key(&self, key_id: &str, algorithm: proto::Algorithm) -> Result { + self.multisig_client + .keygen(key_id, algorithm.into()) + .await + .map_err(|err| Status::internal(err.to_string())) + } +} + +#[async_trait] +impl proto::crypto_server::Crypto for Server +where + M: Multisig + Send + Sync + 'static, +{ + async fn sign( + &self, + req: Request, + ) -> Result, Status> { + let req = req.into_inner(); + + let mut hasher = Sha256::new(); + hasher.update(req.msg); + let sign_digest: [u8; 32] = hasher + .finalize() + .to_vec() + .try_into() + .expect("hash size must be 32"); + + let algorithm = proto::Algorithm::from_i32(req.algorithm) + .ok_or(Status::invalid_argument("invalid algorithm"))?; + let key = self.key(&req.key_id, algorithm).await?; + let signature = self + .multisig_client + .sign(&req.key_id, sign_digest.into(), &key, algorithm.into()) + .await + .map_err(|err| Status::internal(err.to_string()))?; + + Ok(Response::new(proto::SignResponse { signature })) + } + + async fn key( + &self, + req: Request, + ) -> Result, Status> { + let req = req.into_inner(); + + let algorithm = proto::Algorithm::from_i32(req.algorithm) + .ok_or(Status::invalid_argument("invalid algorithm"))?; + let key = self.key(&req.key_id, algorithm).await?; + + Ok(Response::new(proto::KeyResponse { + pub_key: key.to_bytes(), + })) + } +} + +#[cfg(test)] +mod tests { + use ecdsa::SigningKey; + use k256::sha2::{Digest, Sha256}; + use mockall::predicate; + use rand::rngs::OsRng; + use tokio::test; + use tonic::Code; + + use super::proto::{self}; + use super::Server; + use crate::proto::crypto_server; + use crate::proto::crypto_server::Crypto; + use crate::tofnd; + use crate::tofnd::grpc::MockMultisig; + use crate::types::PublicKey; + + #[test] + async fn sign_should_return_correct_signature() { + let key_id = "key_id"; + let algorithm = proto::Algorithm::Ecdsa; + let key: PublicKey = SigningKey::random(&mut OsRng).verifying_key().into(); + let msg = b"message"; + let mut hasher = Sha256::new(); + hasher.update(msg); + let sign_digest: [u8; 32] = hasher.finalize().to_vec().try_into().unwrap(); + + let mut multisig_client = MockMultisig::default(); + multisig_client + .expect_keygen() + .with( + predicate::eq(key_id), + predicate::eq(tofnd::Algorithm::from(algorithm)), + ) + .return_once(move |_, _| Ok(key)); + multisig_client + .expect_sign() + .with( + predicate::eq(key_id), + predicate::eq(tofnd::MessageDigest::from(sign_digest)), + predicate::function(move |actual: &PublicKey| actual == &key), + predicate::eq(tofnd::Algorithm::from(algorithm)), + ) + .return_once(|_, _, _, _| Ok(vec![1; 64])); + let server = Server::new(multisig_client); + + let req = tonic::Request::new(proto::SignRequest { + key_id: key_id.to_string(), + msg: msg.to_vec(), + algorithm: algorithm.into(), + }); + let res = server.sign(req).await.unwrap().into_inner(); + + assert_eq!(res.signature, vec![1; 64]); + } + + #[test] + async fn sign_should_return_error_when_algorithm_is_invalid() { + let key_id = "key_id"; + let algorithm = 2; + let msg = b"message"; + + let multisig_client = MockMultisig::default(); + let server = Server::new(multisig_client); + + let req = tonic::Request::new(proto::SignRequest { + key_id: key_id.to_string(), + msg: msg.to_vec(), + algorithm, + }); + let res = server.sign(req).await.unwrap_err(); + + assert_eq!(res.code(), Code::InvalidArgument); + } + + #[test] + async fn key_should_return_correct_key() { + let key_id = "key_id"; + let algorithm = proto::Algorithm::Ecdsa; + let key: PublicKey = SigningKey::random(&mut OsRng).verifying_key().into(); + + let mut multisig_client = MockMultisig::default(); + multisig_client + .expect_keygen() + .with( + predicate::eq(key_id), + predicate::eq(tofnd::Algorithm::from(algorithm)), + ) + .return_once(move |_, _| Ok(key)); + let server = Server::new(multisig_client); + + let req = tonic::Request::new(proto::KeyRequest { + key_id: key_id.to_string(), + algorithm: algorithm.into(), + }); + let res = crypto_server::Crypto::key(&server, req) + .await + .unwrap() + .into_inner(); + + assert_eq!(res.pub_key, key.to_bytes()); + } + + #[test] + async fn key_should_return_error_when_algorithm_is_invalid() { + let key_id = "key_id"; + + let multisig_client = MockMultisig::default(); + let server = Server::new(multisig_client); + + let req = tonic::Request::new(proto::KeyRequest { + key_id: key_id.to_string(), + algorithm: 2, + }); + let res = crypto_server::Crypto::key(&server, req).await.unwrap_err(); + + assert_eq!(res.code(), Code::InvalidArgument); + } +} diff --git a/ampd/src/grpc/server/mod.rs b/ampd/src/grpc/server/mod.rs new file mode 100644 index 000000000..bb7483c8a --- /dev/null +++ b/ampd/src/grpc/server/mod.rs @@ -0,0 +1,27 @@ +use tonic::transport::server::Router; +use tonic::transport::Server; + +use super::proto; +use crate::event_sub::EventSub; +use crate::queue::queued_broadcaster::BroadcasterClient; +use crate::tofnd::grpc::Multisig; + +mod ampd; +mod crypto; + +#[allow(dead_code)] +pub fn new(event_subscriber: S, broadcaster: B, multisig_client: M) -> Router +where + S: EventSub + Send + Sync + 'static, + B: BroadcasterClient + Send + Sync + 'static, + M: Multisig + Send + Sync + 'static, +{ + Server::builder() + .add_service(proto::ampd_server::AmpdServer::new(ampd::Server::new( + event_subscriber, + broadcaster, + ))) + .add_service(proto::crypto_server::CryptoServer::new( + crypto::Server::new(multisig_client), + )) +} diff --git a/ampd/src/handlers/chain.rs b/ampd/src/handlers/chain.rs deleted file mode 100644 index dd90698ce..000000000 --- a/ampd/src/handlers/chain.rs +++ /dev/null @@ -1,173 +0,0 @@ -use crate::event_processor::EventHandler; -use async_trait::async_trait; -use cosmrs::Any; -use error_stack::{Result, ResultExt}; -use events::Event; -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum Error { - #[error("one of the chained handlers failed handling event")] - ChainError, -} - -pub struct Handler -where - H1: EventHandler, - H2: EventHandler, -{ - handler_1: H1, - handler_2: H2, -} - -impl Handler -where - H1: EventHandler, - H2: EventHandler, -{ - pub fn new(handler_1: H1, handler_2: H2) -> Self { - Self { - handler_1, - handler_2, - } - } -} - -#[async_trait] -impl EventHandler for Handler -where - H1: EventHandler + Send + Sync, - H2: EventHandler + Send + Sync, -{ - type Err = Error; - - async fn handle(&self, event: &Event) -> Result, Error> { - let msgs_1 = self - .handler_1 - .handle(event) - .await - .change_context(Error::ChainError)?; - let msgs_2 = self - .handler_2 - .handle(event) - .await - .change_context(Error::ChainError)?; - - Ok(msgs_1.into_iter().chain(msgs_2).collect()) - } -} - -#[cfg(test)] -mod tests { - use async_trait::async_trait; - use cosmrs::tx::Msg; - use cosmrs::{bank::MsgSend, AccountId, Any}; - use error_stack::Result; - use events::Event; - use mockall::{mock, predicate}; - use tendermint::block; - use thiserror::Error; - - use crate::event_processor::{self, EventHandler}; - - #[tokio::test] - async fn should_chain_handlers() { - let height: block::Height = (10_u32).into(); - let msg_1 = dummy_msg(AccountId::new("", &[1, 2, 3]).unwrap()); - let msg_2 = dummy_msg(AccountId::new("", &[2, 3, 4]).unwrap()); - let msg_3 = dummy_msg(AccountId::new("", &[3, 4, 5]).unwrap()); - - let mut handler_1 = MockEventHandler::new(); - let msgs_1 = vec![msg_1.clone()]; - handler_1 - .expect_handle() - .once() - .with(predicate::eq(Event::BlockEnd(height))) - .returning(move |_| Ok(msgs_1.clone())); - let mut handler_2 = MockEventHandler::new(); - let msgs_2 = vec![msg_2.clone(), msg_3.clone()]; - handler_2 - .expect_handle() - .once() - .with(predicate::eq(Event::BlockEnd(height))) - .returning(move |_| Ok(msgs_2.clone())); - - assert_eq!( - handler_1 - .chain(handler_2) - .handle(&Event::BlockEnd(height)) - .await - .unwrap(), - vec![msg_1, msg_2, msg_3] - ); - } - - #[tokio::test] - async fn should_fail_if_the_first_handler_fails() { - let height: block::Height = (10_u32).into(); - - let mut handler_1 = MockEventHandler::new(); - handler_1 - .expect_handle() - .once() - .with(predicate::eq(Event::BlockEnd(height))) - .returning(|_| Err(EventHandlerError::Unknown.into())); - - assert!(handler_1 - .chain(MockEventHandler::new()) - .handle(&Event::BlockEnd(height)) - .await - .is_err()); - } - - #[tokio::test] - async fn should_fail_if_the_second_handler_fails() { - let height: block::Height = (10_u32).into(); - - let mut handler_1 = MockEventHandler::new(); - handler_1 - .expect_handle() - .once() - .with(predicate::eq(Event::BlockEnd(height))) - .returning(|_| Ok(vec![])); - let mut handler_2 = MockEventHandler::new(); - handler_2 - .expect_handle() - .once() - .with(predicate::eq(Event::BlockEnd(height))) - .returning(|_| Err(EventHandlerError::Unknown.into())); - - assert!(handler_1 - .chain(handler_2) - .handle(&Event::BlockEnd(height)) - .await - .is_err()); - } - - #[derive(Error, Debug)] - pub enum EventHandlerError { - #[error("unknown")] - Unknown, - } - - mock! { - EventHandler{} - - #[async_trait] - impl event_processor::EventHandler for EventHandler { - type Err = EventHandlerError; - - async fn handle(&self, event: &Event) -> Result, EventHandlerError>; - } - } - - fn dummy_msg(from_address: AccountId) -> Any { - MsgSend { - from_address, - to_address: AccountId::new("", &[4, 5, 6]).unwrap(), - amount: vec![], - } - .to_any() - .unwrap() - } -} diff --git a/ampd/src/handlers/config.rs b/ampd/src/handlers/config.rs index 40bd94765..6befec34e 100644 --- a/ampd/src/handlers/config.rs +++ b/ampd/src/handlers/config.rs @@ -1,6 +1,7 @@ use std::time::Duration; use itertools::Itertools; +use router_api::ChainName; use serde::de::{self, Deserializer}; use serde::{Deserialize, Serialize}; use serde_with::with_prefix; @@ -8,7 +9,6 @@ use serde_with::with_prefix; use crate::evm::finalizer::Finalization; use crate::types::TMAddress; use crate::url::Url; -use router_api::ChainName; #[derive(Debug, Deserialize, Serialize, PartialEq)] pub struct Chain { @@ -47,6 +47,14 @@ pub enum Config { rpc_url: Url, rpc_timeout: Option, }, + MvxMsgVerifier { + cosmwasm_contract: TMAddress, + proxy_url: Url, + }, + MvxVerifierSetVerifier { + cosmwasm_contract: TMAddress, + proxy_url: Url, + }, } fn validate_multisig_signer_config<'de, D>(configs: &[Config]) -> Result<(), D::Error> @@ -142,6 +150,38 @@ where } } +fn validate_mvx_msg_verifier_config<'de, D>(configs: &[Config]) -> Result<(), D::Error> +where + D: Deserializer<'de>, +{ + match configs + .iter() + .filter(|config| matches!(config, Config::MvxMsgVerifier { .. })) + .count() + { + count if count > 1 => Err(de::Error::custom( + "only one Mvx msg verifier config is allowed", + )), + _ => Ok(()), + } +} + +fn validate_mvx_worker_set_verifier_config<'de, D>(configs: &[Config]) -> Result<(), D::Error> +where + D: Deserializer<'de>, +{ + match configs + .iter() + .filter(|config| matches!(config, Config::MvxVerifierSetVerifier { .. })) + .count() + { + count if count > 1 => Err(de::Error::custom( + "only one Mvx worker set verifier config is allowed", + )), + _ => Ok(()), + } +} + pub fn deserialize_handler_configs<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, @@ -153,6 +193,8 @@ where validate_multisig_signer_config::(&configs)?; validate_sui_msg_verifier_config::(&configs)?; validate_sui_verifier_set_verifier_config::(&configs)?; + validate_mvx_msg_verifier_config::(&configs)?; + validate_mvx_worker_set_verifier_config::(&configs)?; Ok(configs) } @@ -160,7 +202,8 @@ where #[cfg(test)] mod tests { - use crate::{evm::finalizer::Finalization, handlers::config::Chain}; + use crate::evm::finalizer::Finalization; + use crate::handlers::config::Chain; #[test] fn finalizer_should_default_to_ethereum() { diff --git a/ampd/src/handlers/end_block.rs b/ampd/src/handlers/end_block.rs deleted file mode 100644 index 9b0c88234..000000000 --- a/ampd/src/handlers/end_block.rs +++ /dev/null @@ -1,79 +0,0 @@ -use crate::event_processor::EventHandler; -use async_trait::async_trait; -use cosmrs::Any; -use error_stack::{Result, ResultExt}; -use events::Event; -use tendermint::block; -use thiserror::Error; -use tokio::sync::mpsc::{self, Receiver}; - -#[derive(Error, Debug)] -pub enum Error { - #[error("failed sending label and block height")] - SendError, -} - -struct Handler { - tx: mpsc::Sender, -} - -impl Handler { - fn new() -> (Self, Receiver) { - let (tx, rx) = mpsc::channel(10000); - (Self { tx }, rx) - } -} - -pub fn with_block_height_notifier( - handler: impl EventHandler + Send + Sync, -) -> (impl EventHandler, Receiver) { - let (end_block_handler, rx) = Handler::new(); - (handler.chain(end_block_handler), rx) -} - -#[async_trait] -impl EventHandler for Handler { - type Err = Error; - - async fn handle(&self, event: &Event) -> Result, Error> { - if let Event::BlockEnd(height) = event { - self.tx - .send(*height) - .await - .change_context(Error::SendError)?; - } - - Ok(vec![]) - } -} - -#[cfg(test)] -mod tests { - use crate::event_processor::EventHandler; - use crate::handlers::end_block::Handler; - use events::Event; - use tendermint::block; - - #[tokio::test] - async fn handle_should_stream_blocks() { - let count = 10; - let (handler, mut rx) = Handler::new(); - let mut height = block::Height::default(); - - for _ in 0..count { - assert_eq!( - handler.handle(&Event::BlockEnd(height)).await.unwrap(), - vec![] - ); - height = height.increment(); - } - - let mut height = block::Height::default(); - for _ in 0..count { - let actual_height = rx.recv().await.unwrap(); - assert_eq!(actual_height, height); - - height = height.increment(); - } - } -} diff --git a/ampd/src/handlers/errors.rs b/ampd/src/handlers/errors.rs index 876912a3b..bd04d31ef 100644 --- a/ampd/src/handlers/errors.rs +++ b/ampd/src/handlers/errors.rs @@ -10,4 +10,6 @@ pub enum Error { Sign, #[error("failed to get transaction receipts")] TxReceipts, + #[error("unsupported key type {0}")] + KeyType(String), } diff --git a/ampd/src/handlers/evm_verify_msg.rs b/ampd/src/handlers/evm_verify_msg.rs index 6825ef71d..6fe8ca131 100644 --- a/ampd/src/handlers/evm_verify_msg.rs +++ b/ampd/src/handlers/evm_verify_msg.rs @@ -2,21 +2,21 @@ use std::collections::{HashMap, HashSet}; use std::convert::TryInto; use async_trait::async_trait; +use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; +use axelar_wasm_std::voting::{PollId, Vote}; use cosmrs::cosmwasm::MsgExecuteContract; -use cosmrs::{tx::Msg, Any}; +use cosmrs::tx::Msg; +use cosmrs::Any; use error_stack::ResultExt; -use ethers::types::{TransactionReceipt, U64}; +use ethers_core::types::{TransactionReceipt, U64}; +use events::Error::EventTypeMismatch; +use events_derive::try_from; use futures::future::join_all; +use router_api::ChainName; use serde::Deserialize; use tokio::sync::watch::Receiver; use tracing::{info, info_span}; use valuable::Valuable; - -use axelar_wasm_std::msg_id::tx_hash_event_index::HexTxHashAndEventIndex; -use axelar_wasm_std::voting::{PollId, Vote}; -use events::Error::EventTypeMismatch; -use events_derive::try_from; -use router_api::ChainName; use voting_verifier::msg::ExecuteMsg; use crate::event_processor::EventHandler; @@ -35,7 +35,7 @@ pub struct Message { pub tx_id: Hash, pub event_index: u32, pub destination_address: String, - pub destination_chain: router_api::ChainName, + pub destination_chain: ChainName, pub source_address: EVMAddress, pub payload_hash: Hash, } @@ -44,7 +44,7 @@ pub struct Message { #[try_from("wasm-messages_poll_started")] struct PollStartedEvent { poll_id: PollId, - source_chain: router_api::ChainName, + source_chain: ChainName, source_gateway_address: EVMAddress, confirmation_height: u64, expires_at: u64, @@ -230,25 +230,23 @@ mod tests { use base64::Engine; use cosmwasm_std; use error_stack::{Report, Result}; - use ethers::providers::ProviderError; - use tendermint::abci; - use tokio::sync::watch; - use tokio::test as async_test; - + use ethers_providers::ProviderError; use events::Error::{DeserializationFailed, EventTypeMismatch}; use events::Event; use router_api::ChainName; + use tendermint::abci; + use tokio::sync::watch; + use tokio::test as async_test; use voting_verifier::events::{PollMetadata, PollStarted, TxEventConfirmation}; + use super::PollStartedEvent; use crate::event_processor::EventHandler; use crate::evm::finalizer::Finalization; use crate::evm::json_rpc::MockEthereumClient; use crate::types::{EVMAddress, Hash, TMAddress}; use crate::PREFIX; - use super::PollStartedEvent; - - fn get_poll_started_event(participants: Vec, expires_at: u64) -> PollStarted { + fn poll_started_event(participants: Vec, expires_at: u64) -> PollStarted { PollStarted::Messages { metadata: PollMetadata { poll_id: "100".parse().unwrap(), @@ -295,8 +293,8 @@ mod tests { #[test] fn should_not_deserialize_incorrect_event() { // incorrect event type - let mut event: Event = get_event( - get_poll_started_event(participants(5, None), 100), + let mut event: Event = to_event( + poll_started_event(participants(5, None), 100), &TMAddress::random(PREFIX), ); match event { @@ -315,8 +313,8 @@ mod tests { )); // invalid field - let mut event: Event = get_event( - get_poll_started_event(participants(5, None), 100), + let mut event: Event = to_event( + poll_started_event(participants(5, None), 100), &TMAddress::random(PREFIX), ); match event { @@ -338,8 +336,8 @@ mod tests { #[test] fn should_deserialize_correct_event() { - let event: Event = get_event( - get_poll_started_event(participants(5, None), 100), + let event: Event = to_event( + poll_started_event(participants(5, None), 100), &TMAddress::random(PREFIX), ); let event: Result = event.try_into(); @@ -359,8 +357,8 @@ mod tests { let voting_verifier_contract = TMAddress::random(PREFIX); let verifier = TMAddress::random(PREFIX); let expiration = 100u64; - let event: Event = get_event( - get_poll_started_event(participants(5, Some(verifier.clone())), expiration), + let event: Event = to_event( + poll_started_event(participants(5, Some(verifier.clone())), expiration), &voting_verifier_contract, ); @@ -384,7 +382,7 @@ mod tests { assert_eq!(handler.handle(&event).await.unwrap(), vec![]); } - fn get_event(event: impl Into, contract_address: &TMAddress) -> Event { + fn to_event(event: impl Into, contract_address: &TMAddress) -> Event { let mut event: cosmwasm_std::Event = event.into(); event.ty = format!("wasm-{}", event.ty); diff --git a/ampd/src/handlers/evm_verify_verifier_set.rs b/ampd/src/handlers/evm_verify_verifier_set.rs index 577a50bc5..6097f85b9 100644 --- a/ampd/src/handlers/evm_verify_verifier_set.rs +++ b/ampd/src/handlers/evm_verify_verifier_set.rs @@ -1,29 +1,28 @@ use std::convert::TryInto; use async_trait::async_trait; +use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; +use axelar_wasm_std::voting::{PollId, Vote}; use cosmrs::cosmwasm::MsgExecuteContract; -use cosmrs::{tx::Msg, Any}; +use cosmrs::tx::Msg; +use cosmrs::Any; use error_stack::ResultExt; -use ethers::types::{TransactionReceipt, U64}; +use ethers_core::types::{TransactionReceipt, U64}; +use events::Error::EventTypeMismatch; +use events_derive::try_from; use multisig::verifier_set::VerifierSet; +use router_api::ChainName; use serde::Deserialize; use tokio::sync::watch::Receiver; use tracing::{info, info_span}; use valuable::Valuable; - -use axelar_wasm_std::{ - msg_id::tx_hash_event_index::HexTxHashAndEventIndex, - voting::{PollId, Vote}, -}; -use events::Error::EventTypeMismatch; -use events_derive::try_from; -use router_api::ChainName; use voting_verifier::msg::ExecuteMsg; use crate::event_processor::EventHandler; +use crate::evm::finalizer; use crate::evm::finalizer::Finalization; +use crate::evm::json_rpc::EthereumClient; use crate::evm::verifier::verify_verifier_set; -use crate::evm::{finalizer, json_rpc::EthereumClient}; use crate::handlers::errors::Error; use crate::types::{EVMAddress, Hash, TMAddress}; @@ -41,7 +40,7 @@ pub struct VerifierSetConfirmation { struct PollStartedEvent { verifier_set: VerifierSetConfirmation, poll_id: PollId, - source_chain: router_api::ChainName, + source_chain: ChainName, source_gateway_address: EVMAddress, expires_at: u64, confirmation_height: u64, @@ -199,35 +198,32 @@ where #[cfg(test)] mod tests { - use std::{convert::TryInto, str::FromStr}; + use std::convert::TryInto; + use std::str::FromStr; use base64::engine::general_purpose::STANDARD; use base64::Engine; use error_stack::{Report, Result}; - use ethers::providers::ProviderError; - - use tendermint::abci; - use tokio::{sync::watch, test as async_test}; - + use ethers_providers::ProviderError; use events::Event; - use multisig::{ - key::KeyType, - test::common::{build_verifier_set, ecdsa_test_data}, - }; + use multisig::key::KeyType; + use multisig::test::common::{build_verifier_set, ecdsa_test_data}; use router_api::ChainName; + use tendermint::abci; + use tokio::sync::watch; + use tokio::test as async_test; use voting_verifier::events::{PollMetadata, PollStarted, VerifierSetConfirmation}; - use crate::{ - event_processor::EventHandler, - evm::{finalizer::Finalization, json_rpc::MockEthereumClient}, - handlers::evm_verify_verifier_set::PollStartedEvent, - types::{Hash, TMAddress}, - PREFIX, - }; + use crate::event_processor::EventHandler; + use crate::evm::finalizer::Finalization; + use crate::evm::json_rpc::MockEthereumClient; + use crate::handlers::evm_verify_verifier_set::PollStartedEvent; + use crate::types::{Hash, TMAddress}; + use crate::PREFIX; #[test] fn should_deserialize_correct_event() { - let event: Event = get_event( + let event: Event = to_event( poll_started_event(participants(5, None), 100), &TMAddress::random(PREFIX), ); @@ -249,7 +245,7 @@ mod tests { let voting_verifier = TMAddress::random(PREFIX); let verifier = TMAddress::random(PREFIX); let expiration = 100u64; - let event: Event = get_event( + let event: Event = to_event( poll_started_event(participants(5, Some(verifier.clone())), expiration), &voting_verifier, ); @@ -297,7 +293,7 @@ mod tests { } } - fn get_event(event: impl Into, contract_address: &TMAddress) -> Event { + fn to_event(event: impl Into, contract_address: &TMAddress) -> Event { let mut event: cosmwasm_std::Event = event.into(); event.ty = format!("wasm-{}", event.ty); diff --git a/ampd/src/handlers/mod.rs b/ampd/src/handlers/mod.rs index 2711b7f74..1a01f197b 100644 --- a/ampd/src/handlers/mod.rs +++ b/ampd/src/handlers/mod.rs @@ -1,10 +1,10 @@ -pub mod chain; pub mod config; -pub mod end_block; mod errors; pub mod evm_verify_msg; pub mod evm_verify_verifier_set; pub mod multisig; +pub mod mvx_verify_msg; +pub mod mvx_verify_verifier_set; pub mod sui_verify_msg; pub mod sui_verify_verifier_set; @@ -20,7 +20,10 @@ mod tests { use crate::types::TMAddress; /// Convert a CosmWasm event into an ABCI event - pub fn get_event(event: impl Into, contract_address: &TMAddress) -> Event { + pub fn into_structured_event( + event: impl Into, + contract_address: &TMAddress, + ) -> Event { let mut event: cosmwasm_std::Event = event.into(); event.ty = format!("wasm-{}", event.ty); diff --git a/ampd/src/handlers/multisig.rs b/ampd/src/handlers/multisig.rs index 290a99602..12e0ac448 100644 --- a/ampd/src/handlers/multisig.rs +++ b/ampd/src/handlers/multisig.rs @@ -3,27 +3,25 @@ use std::convert::TryInto; use async_trait::async_trait; use cosmrs::cosmwasm::MsgExecuteContract; -use cosmrs::{tx::Msg, Any}; +use cosmrs::tx::Msg; +use cosmrs::Any; use cosmwasm_std::{HexBinary, Uint64}; use ecdsa::VerifyingKey; -use error_stack::ResultExt; +use error_stack::{Report, ResultExt}; +use events_derive; +use events_derive::try_from; use hex::encode; +use multisig::msg::ExecuteMsg; use serde::de::Error as DeserializeError; use serde::{Deserialize, Deserializer}; use tokio::sync::watch::Receiver; use tracing::info; -use events::Error::EventTypeMismatch; -use events_derive; -use events_derive::try_from; -use multisig::msg::ExecuteMsg; - use crate::event_processor::EventHandler; use crate::handlers::errors::Error::{self, DeserializeEvent}; -use crate::tofnd::grpc::SharableEcdsaClient; -use crate::tofnd::MessageDigest; -use crate::types::PublicKey; -use crate::types::TMAddress; +use crate::tofnd::grpc::Multisig; +use crate::tofnd::{self, MessageDigest}; +use crate::types::{PublicKey, TMAddress}; #[derive(Debug, Deserialize)] #[try_from("wasm-signing_started")] @@ -66,18 +64,21 @@ where .collect() } -pub struct Handler { +pub struct Handler { verifier: TMAddress, multisig: TMAddress, - signer: SharableEcdsaClient, + signer: S, latest_block_height: Receiver, } -impl Handler { +impl Handler +where + S: Multisig, +{ pub fn new( verifier: TMAddress, multisig: TMAddress, - signer: SharableEcdsaClient, + signer: S, latest_block_height: Receiver, ) -> Self { Self { @@ -107,7 +108,10 @@ impl Handler { } #[async_trait] -impl EventHandler for Handler { +impl EventHandler for Handler +where + S: Multisig + Sync, +{ type Err = Error; async fn handle(&self, event: &events::Event) -> error_stack::Result, Error> { @@ -121,7 +125,12 @@ impl EventHandler for Handler { msg, expires_at, } = match event.try_into() as error_stack::Result<_, _> { - Err(report) if matches!(report.current_context(), EventTypeMismatch(_)) => { + Err(report) + if matches!( + report.current_context(), + events::Error::EventTypeMismatch(_) + ) => + { return Ok(vec![]); } result => result.change_context(DeserializeEvent)?, @@ -144,9 +153,20 @@ impl EventHandler for Handler { match pub_keys.get(&self.verifier) { Some(pub_key) => { + let key_type = match pub_key.type_url() { + PublicKey::ED25519_TYPE_URL => tofnd::Algorithm::Ed25519, + PublicKey::SECP256K1_TYPE_URL => tofnd::Algorithm::Ecdsa, + unspported => return Err(Report::from(Error::KeyType(unspported.to_string()))), + }; + let signature = self .signer - .sign(self.multisig.to_string().as_str(), msg.clone(), pub_key) + .sign( + self.multisig.to_string().as_str(), + msg.clone(), + pub_key, + key_type, + ) .await .change_context(Error::Sign)?; @@ -173,11 +193,13 @@ mod test { use base64::engine::general_purpose::STANDARD; use base64::Engine; - use cosmos_sdk_proto::cosmos::base::abci::v1beta1::TxResponse; + use cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse; use cosmrs::AccountId; use cosmwasm_std::{HexBinary, Uint64}; use ecdsa::SigningKey; use error_stack::{Report, Result}; + use multisig::events::Event; + use multisig::types::MsgToSign; use rand::distributions::Alphanumeric; use rand::rngs::OsRng; use rand::Rng; @@ -185,16 +207,10 @@ mod test { use tendermint::abci; use tokio::sync::watch; - use multisig::events::Event::SigningStarted; - use multisig::key::PublicKey; - use multisig::types::MsgToSign; - - use crate::broadcaster::MockBroadcaster; - use crate::tofnd; - use crate::tofnd::grpc::{MockEcdsaClient, SharableEcdsaClient}; - use crate::types; - use super::*; + use crate::broadcaster::MockBroadcaster; + use crate::tofnd::grpc::MockMultisig; + use crate::{tofnd, types}; const MULTISIG_ADDRESS: &str = "axelarvaloper1zh9wrak6ke4n6fclj5e8yk397czv430ygs5jz7"; @@ -219,7 +235,7 @@ mod test { fn rand_chain_name() -> ChainName { rand::thread_rng() .sample_iter(&Alphanumeric) - .take(32) + .take(10) .map(char::from) .collect::() .try_into() @@ -229,9 +245,9 @@ mod test { fn signing_started_event() -> events::Event { let pub_keys = (0..10) .map(|_| (rand_account().to_string(), rand_public_key())) - .collect::>(); + .collect::>(); - let poll_started = SigningStarted { + let poll_started = Event::SigningStarted { session_id: Uint64::one(), verifier_set_id: "verifier_set_id".to_string(), pub_keys, @@ -260,9 +276,9 @@ mod test { fn signing_started_event_with_missing_fields(contract_address: &str) -> events::Event { let pub_keys = (0..10) .map(|_| (rand_account().to_string(), rand_public_key())) - .collect::>(); + .collect::>(); - let poll_started = SigningStarted { + let poll_started = Event::SigningStarted { session_id: Uint64::one(), verifier_set_id: "verifier_set_id".to_string(), pub_keys, @@ -288,12 +304,12 @@ mod test { .unwrap() } - fn get_handler( + fn handler( verifier: TMAddress, multisig: TMAddress, - signer: SharableEcdsaClient, + signer: MockMultisig, latest_block_height: u64, - ) -> Handler { + ) -> Handler { let mut broadcaster = MockBroadcaster::new(); broadcaster .expect_broadcast() @@ -329,10 +345,10 @@ mod test { let mut event = signing_started_event(); let invalid_pub_key: [u8; 32] = rand::random(); - let mut map: HashMap = HashMap::new(); + let mut map: HashMap = HashMap::new(); map.insert( rand_account().to_string(), - PublicKey::Ecdsa(HexBinary::from(invalid_pub_key.as_slice())), + multisig::key::PublicKey::Ecdsa(HexBinary::from(invalid_pub_key.as_slice())), ); match event { events::Event::Abci { @@ -361,12 +377,12 @@ mod test { #[tokio::test] async fn should_not_handle_event_with_missing_fields_if_multisig_address_does_not_match() { - let client = MockEcdsaClient::new(); + let client = MockMultisig::default(); - let handler = get_handler( + let handler = handler( rand_account(), TMAddress::from(MULTISIG_ADDRESS.parse::().unwrap()), - SharableEcdsaClient::new(client), + client, 100u64, ); @@ -383,12 +399,12 @@ mod test { #[tokio::test] async fn should_error_on_event_with_missing_fields_if_multisig_address_does_match() { - let client = MockEcdsaClient::new(); + let client = MockMultisig::default(); - let handler = get_handler( + let handler = handler( rand_account(), TMAddress::from(MULTISIG_ADDRESS.parse::().unwrap()), - SharableEcdsaClient::new(client), + client, 100u64, ); @@ -400,14 +416,9 @@ mod test { #[tokio::test] async fn should_not_handle_event_if_multisig_address_does_not_match() { - let client = MockEcdsaClient::new(); + let client = MockMultisig::default(); - let handler = get_handler( - rand_account(), - rand_account(), - SharableEcdsaClient::new(client), - 100u64, - ); + let handler = handler(rand_account(), rand_account(), client, 100u64); assert_eq!( handler.handle(&signing_started_event()).await.unwrap(), @@ -417,15 +428,15 @@ mod test { #[tokio::test] async fn should_not_handle_event_if_verifier_is_not_a_participant() { - let mut client = MockEcdsaClient::new(); + let mut client = MockMultisig::default(); client .expect_sign() - .returning(move |_, _, _| Err(Report::from(tofnd::error::Error::SignFailed))); + .returning(move |_, _, _, _| Err(Report::from(tofnd::error::Error::SignFailed))); - let handler = get_handler( + let handler = handler( rand_account(), TMAddress::from(MULTISIG_ADDRESS.parse::().unwrap()), - SharableEcdsaClient::new(client), + client, 100u64, ); @@ -437,18 +448,18 @@ mod test { #[tokio::test] async fn should_not_handle_event_if_sign_failed() { - let mut client = MockEcdsaClient::new(); + let mut client = MockMultisig::default(); client .expect_sign() - .returning(move |_, _, _| Err(Report::from(tofnd::error::Error::SignFailed))); + .returning(move |_, _, _, _| Err(Report::from(tofnd::error::Error::SignFailed))); let event = signing_started_event(); let signing_started: SigningStartedEvent = ((&event).try_into() as Result<_, _>).unwrap(); let verifier = signing_started.pub_keys.keys().next().unwrap().clone(); - let handler = get_handler( + let handler = handler( verifier, TMAddress::from(MULTISIG_ADDRESS.parse::().unwrap()), - SharableEcdsaClient::new(client), + client, 99u64, ); @@ -460,18 +471,18 @@ mod test { #[tokio::test] async fn should_not_handle_event_if_session_expired() { - let mut client = MockEcdsaClient::new(); + let mut client = MockMultisig::default(); client .expect_sign() - .returning(move |_, _, _| Err(Report::from(tofnd::error::Error::SignFailed))); + .returning(move |_, _, _, _| Err(Report::from(tofnd::error::Error::SignFailed))); let event = signing_started_event(); let signing_started: SigningStartedEvent = ((&event).try_into() as Result<_, _>).unwrap(); let verifier = signing_started.pub_keys.keys().next().unwrap().clone(); - let handler = get_handler( + let handler = handler( verifier, TMAddress::from(MULTISIG_ADDRESS.parse::().unwrap()), - SharableEcdsaClient::new(client), + client, 101u64, ); diff --git a/ampd/src/handlers/mvx_verify_msg.rs b/ampd/src/handlers/mvx_verify_msg.rs new file mode 100644 index 000000000..a836ef40e --- /dev/null +++ b/ampd/src/handlers/mvx_verify_msg.rs @@ -0,0 +1,340 @@ +use std::collections::HashSet; +use std::convert::TryInto; + +use async_trait::async_trait; +use axelar_wasm_std::voting::{PollId, Vote}; +use cosmrs::cosmwasm::MsgExecuteContract; +use cosmrs::tx::Msg; +use cosmrs::Any; +use error_stack::ResultExt; +use events::Error::EventTypeMismatch; +use events::Event; +use events_derive::try_from; +use multiversx_sdk::data::address::Address; +use serde::Deserialize; +use tokio::sync::watch::Receiver; +use tracing::info; +use voting_verifier::msg::ExecuteMsg; + +use crate::event_processor::EventHandler; +use crate::handlers::errors::Error; +use crate::mvx::proxy::MvxProxy; +use crate::mvx::verifier::verify_message; +use crate::types::{Hash, TMAddress}; + +type Result = error_stack::Result; + +#[derive(Deserialize, Debug)] +pub struct Message { + pub tx_id: Hash, + pub event_index: u32, + pub destination_address: String, + pub destination_chain: router_api::ChainName, + pub source_address: Address, + pub payload_hash: Hash, +} + +#[derive(Deserialize, Debug)] +#[try_from("wasm-messages_poll_started")] +struct PollStartedEvent { + poll_id: PollId, + source_gateway_address: Address, + messages: Vec, + participants: Vec, + expires_at: u64, +} + +pub struct Handler

+where + P: MvxProxy + Send + Sync, +{ + verifier: TMAddress, + voting_verifier_contract: TMAddress, + blockchain: P, + latest_block_height: Receiver, +} + +impl

Handler

+where + P: MvxProxy + Send + Sync, +{ + pub fn new( + verifier: TMAddress, + voting_verifier_contract: TMAddress, + blockchain: P, + latest_block_height: Receiver, + ) -> Self { + Self { + verifier, + voting_verifier_contract, + blockchain, + latest_block_height, + } + } + + fn vote_msg(&self, poll_id: PollId, votes: Vec) -> MsgExecuteContract { + MsgExecuteContract { + sender: self.verifier.as_ref().clone(), + contract: self.voting_verifier_contract.as_ref().clone(), + msg: serde_json::to_vec(&ExecuteMsg::Vote { poll_id, votes }) + .expect("vote msg should serialize"), + funds: vec![], + } + } +} + +#[async_trait] +impl

EventHandler for Handler

+where + P: MvxProxy + Send + Sync, +{ + type Err = Error; + + async fn handle(&self, event: &Event) -> Result> { + if !event.is_from_contract(self.voting_verifier_contract.as_ref()) { + return Ok(vec![]); + } + + let PollStartedEvent { + poll_id, + source_gateway_address, + messages, + participants, + expires_at, + .. + } = match event.try_into() as error_stack::Result<_, _> { + Err(report) if matches!(report.current_context(), EventTypeMismatch(_)) => { + return Ok(vec![]); + } + event => event.change_context(Error::DeserializeEvent)?, + }; + + if !participants.contains(&self.verifier) { + return Ok(vec![]); + } + + let latest_block_height = *self.latest_block_height.borrow(); + if latest_block_height >= expires_at { + info!(poll_id = poll_id.to_string(), "skipping expired poll"); + + return Ok(vec![]); + } + + let tx_hashes: HashSet<_> = messages.iter().map(|message| message.tx_id).collect(); + let transactions_info = self + .blockchain + .transactions_info_with_results(tx_hashes) + .await; + + let votes: Vec = messages + .iter() + .map(|msg| { + transactions_info + .get(&msg.tx_id) + .map_or(Vote::NotFound, |transaction| { + verify_message(&source_gateway_address, transaction, msg) + }) + }) + .collect(); + + Ok(vec![self + .vote_msg(poll_id, votes) + .into_any() + .expect("vote msg should serialize")]) + } +} + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + use std::convert::TryInto; + + use cosmrs::cosmwasm::MsgExecuteContract; + use cosmrs::tx::Msg; + use cosmwasm_std; + use error_stack::Result; + use hex::ToHex; + use tokio::sync::watch; + use tokio::test as async_test; + use voting_verifier::events::{PollMetadata, PollStarted, TxEventConfirmation}; + + use super::PollStartedEvent; + use crate::event_processor::EventHandler; + use crate::handlers::tests::into_structured_event; + use crate::mvx::proxy::MockMvxProxy; + use crate::types::{EVMAddress, Hash, TMAddress}; + use crate::PREFIX; + + #[test] + fn should_deserialize_poll_started_event() { + let event: Result = into_structured_event( + poll_started_event(participants(5, None)), + &TMAddress::random(PREFIX), + ) + .try_into(); + + assert!(event.is_ok()); + + let event = event.unwrap(); + + assert!(event.poll_id == 100u64.into()); + assert!( + event.source_gateway_address.to_bech32_string().unwrap() + == "erd1qqqqqqqqqqqqqpgqsvzyz88e8v8j6x3wquatxuztnxjwnw92kkls6rdtzx" + ); + + let message = event.messages.first().unwrap(); + + assert!( + message.tx_id.encode_hex::() + == "dfaf64de66510723f2efbacd7ead3c4f8c856aed1afc2cb30254552aeda47312", + ); + assert!(message.event_index == 1u32); + assert!(message.destination_chain == "ethereum"); + assert!( + message.source_address.to_bech32_string().unwrap() + == "erd1qqqqqqqqqqqqqpgqzqvm5ywqqf524efwrhr039tjs29w0qltkklsa05pk7" + ); + } + + // Should not handle event if it is not a poll started event + #[async_test] + async fn not_poll_started_event() { + let event = into_structured_event( + cosmwasm_std::Event::new("transfer"), + &TMAddress::random(PREFIX), + ); + + let handler = super::Handler::new( + TMAddress::random(PREFIX), + TMAddress::random(PREFIX), + MockMvxProxy::new(), + watch::channel(0).1, + ); + + assert!(handler.handle(&event).await.is_ok()); + } + + // Should not handle event if it is not emitted from voting verifier + #[async_test] + async fn contract_is_not_voting_verifier() { + let event = into_structured_event( + poll_started_event(participants(5, None)), + &TMAddress::random(PREFIX), + ); + + let handler = super::Handler::new( + TMAddress::random(PREFIX), + TMAddress::random(PREFIX), + MockMvxProxy::new(), + watch::channel(0).1, + ); + + assert!(handler.handle(&event).await.is_ok()); + } + + // Should not handle event if worker is not a poll participant + #[async_test] + async fn verifier_is_not_a_participant() { + let voting_verifier = TMAddress::random(PREFIX); + let event = + into_structured_event(poll_started_event(participants(5, None)), &voting_verifier); + + let handler = super::Handler::new( + TMAddress::random(PREFIX), + voting_verifier, + MockMvxProxy::new(), + watch::channel(0).1, + ); + + assert!(handler.handle(&event).await.is_ok()); + } + + #[async_test] + async fn should_vote_correctly() { + let mut proxy = MockMvxProxy::new(); + proxy + .expect_transactions_info_with_results() + .returning(|_| HashMap::new()); + + let voting_verifier = TMAddress::random(PREFIX); + let worker = TMAddress::random(PREFIX); + let event = into_structured_event( + poll_started_event(participants(5, Some(worker.clone()))), + &voting_verifier, + ); + + let handler = super::Handler::new(worker, voting_verifier, proxy, watch::channel(0).1); + + let actual = handler.handle(&event).await.unwrap(); + assert_eq!(actual.len(), 1); + assert!(MsgExecuteContract::from_any(actual.first().unwrap()).is_ok()); + } + + #[async_test] + async fn should_skip_expired_poll() { + let mut proxy = MockMvxProxy::new(); + proxy + .expect_transactions_info_with_results() + .returning(|_| HashMap::new()); + + let voting_verifier = TMAddress::random(PREFIX); + let worker = TMAddress::random(PREFIX); + let expiration = 100u64; + let event = into_structured_event( + poll_started_event(participants(5, Some(worker.clone()))), + &voting_verifier, + ); + + let (tx, rx) = watch::channel(expiration - 1); + + let handler = super::Handler::new(worker, voting_verifier, proxy, rx); + + // poll is not expired yet, should hit proxy + let actual = handler.handle(&event).await.unwrap(); + assert_eq!(actual.len(), 1); + + let _ = tx.send(expiration + 1); + + // poll is expired + assert_eq!(handler.handle(&event).await.unwrap(), vec![]); + } + + fn poll_started_event(participants: Vec) -> PollStarted { + PollStarted::Messages { + metadata: PollMetadata { + poll_id: "100".parse().unwrap(), + source_chain: "multiversx".parse().unwrap(), + source_gateway_address: + "erd1qqqqqqqqqqqqqpgqsvzyz88e8v8j6x3wquatxuztnxjwnw92kkls6rdtzx" + .parse() + .unwrap(), + confirmation_height: 15, + expires_at: 100, + participants: participants + .into_iter() + .map(|addr| cosmwasm_std::Addr::unchecked(addr.to_string())) + .collect(), + }, + messages: vec![TxEventConfirmation { + tx_id: "dfaf64de66510723f2efbacd7ead3c4f8c856aed1afc2cb30254552aeda47312" + .parse() + .unwrap(), + event_index: 1, + source_address: "erd1qqqqqqqqqqqqqpgqzqvm5ywqqf524efwrhr039tjs29w0qltkklsa05pk7" + .parse() + .unwrap(), + destination_chain: "ethereum".parse().unwrap(), + destination_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), + payload_hash: Hash::random().to_fixed_bytes(), + }], + } + } + + fn participants(n: u8, worker: Option) -> Vec { + (0..n) + .map(|_| TMAddress::random(PREFIX)) + .chain(worker) + .collect() + } +} diff --git a/ampd/src/handlers/mvx_verify_verifier_set.rs b/ampd/src/handlers/mvx_verify_verifier_set.rs new file mode 100644 index 000000000..1953ee000 --- /dev/null +++ b/ampd/src/handlers/mvx_verify_verifier_set.rs @@ -0,0 +1,362 @@ +use std::convert::TryInto; + +use async_trait::async_trait; +use axelar_wasm_std::voting::{PollId, Vote}; +use cosmrs::cosmwasm::MsgExecuteContract; +use cosmrs::tx::Msg; +use cosmrs::Any; +use error_stack::ResultExt; +use events::Error::EventTypeMismatch; +use events::Event; +use events_derive::try_from; +use multisig::verifier_set::VerifierSet; +use multiversx_sdk::data::address::Address; +use serde::Deserialize; +use tokio::sync::watch::Receiver; +use tracing::{info, info_span}; +use valuable::Valuable; +use voting_verifier::msg::ExecuteMsg; + +use crate::event_processor::EventHandler; +use crate::handlers::errors::Error; +use crate::mvx::proxy::MvxProxy; +use crate::mvx::verifier::verify_verifier_set; +use crate::types::{Hash, TMAddress}; + +#[derive(Deserialize, Debug)] +pub struct VerifierSetConfirmation { + pub tx_id: Hash, + pub event_index: u32, + pub verifier_set: VerifierSet, +} + +#[derive(Deserialize, Debug)] +#[try_from("wasm-verifier_set_poll_started")] +struct PollStartedEvent { + poll_id: PollId, + source_gateway_address: Address, + verifier_set: VerifierSetConfirmation, + participants: Vec, + expires_at: u64, +} + +pub struct Handler

+where + P: MvxProxy + Send + Sync, +{ + verifier: TMAddress, + voting_verifier_contract: TMAddress, + blockchain: P, + latest_block_height: Receiver, +} + +impl

Handler

+where + P: MvxProxy + Send + Sync, +{ + pub fn new( + verifier: TMAddress, + voting_verifier_contract: TMAddress, + blockchain: P, + latest_block_height: Receiver, + ) -> Self { + Self { + verifier, + voting_verifier_contract, + blockchain, + latest_block_height, + } + } + + fn vote_msg(&self, poll_id: PollId, vote: Vote) -> MsgExecuteContract { + MsgExecuteContract { + sender: self.verifier.as_ref().clone(), + contract: self.voting_verifier_contract.as_ref().clone(), + msg: serde_json::to_vec(&ExecuteMsg::Vote { + poll_id, + votes: vec![vote], + }) + .expect("vote msg should serialize"), + funds: vec![], + } + } +} + +#[async_trait] +impl

EventHandler for Handler

+where + P: MvxProxy + Send + Sync, +{ + type Err = Error; + + async fn handle(&self, event: &Event) -> error_stack::Result, Error> { + if !event.is_from_contract(self.voting_verifier_contract.as_ref()) { + return Ok(vec![]); + } + + let PollStartedEvent { + poll_id, + source_gateway_address, + verifier_set, + participants, + expires_at, + .. + } = match event.try_into() as error_stack::Result<_, _> { + Err(report) if matches!(report.current_context(), EventTypeMismatch(_)) => { + return Ok(vec![]); + } + event => event.change_context(Error::DeserializeEvent)?, + }; + + if !participants.contains(&self.verifier) { + return Ok(vec![]); + } + + let latest_block_height = *self.latest_block_height.borrow(); + if latest_block_height >= expires_at { + info!(poll_id = poll_id.to_string(), "skipping expired poll"); + return Ok(vec![]); + } + + let transaction_info = self + .blockchain + .transaction_info_with_results(&verifier_set.tx_id) + .await; + + let vote = info_span!( + "verify a new verifier set for MultiversX", + poll_id = poll_id.to_string(), + id = format!("{}_{}", verifier_set.tx_id, verifier_set.event_index) + ) + .in_scope(|| { + info!("ready to verify a new worker set in poll"); + + let vote = transaction_info.map_or(Vote::NotFound, |transaction| { + verify_verifier_set(&source_gateway_address, &transaction, verifier_set) + }); + info!( + vote = vote.as_value(), + "ready to vote for a new worker set in poll" + ); + + vote + }); + + Ok(vec![self + .vote_msg(poll_id, vote) + .into_any() + .expect("vote msg should serialize")]) + } +} + +#[cfg(test)] +mod tests { + use std::convert::TryInto; + + use cosmrs::cosmwasm::MsgExecuteContract; + use cosmrs::tx::Msg; + use cosmwasm_std; + use cosmwasm_std::{HexBinary, Uint128}; + use error_stack::Result; + use events::Event; + use hex::ToHex; + use multisig::key::KeyType; + use multisig::test::common::{build_verifier_set, ed25519_test_data}; + use tokio::sync::watch; + use tokio::test as async_test; + use voting_verifier::events::{PollMetadata, PollStarted, VerifierSetConfirmation}; + + use super::PollStartedEvent; + use crate::event_processor::EventHandler; + use crate::handlers::tests::into_structured_event; + use crate::mvx::proxy::MockMvxProxy; + use crate::types::TMAddress; + use crate::PREFIX; + + #[test] + fn should_deserialize_verifier_set_poll_started_event() { + let event: Result = into_structured_event( + verifier_set_poll_started_event(participants(5, None), 100), + &TMAddress::random(PREFIX), + ) + .try_into(); + + assert!(event.is_ok()); + + let event = event.unwrap(); + + assert!(event.poll_id == 100u64.into()); + assert!( + event.source_gateway_address.to_bech32_string().unwrap() + == "erd1qqqqqqqqqqqqqpgqsvzyz88e8v8j6x3wquatxuztnxjwnw92kkls6rdtzx" + ); + + let verifier_set = event.verifier_set; + + assert!( + verifier_set.tx_id.encode_hex::() + == "dfaf64de66510723f2efbacd7ead3c4f8c856aed1afc2cb30254552aeda47312" + ); + assert!(verifier_set.event_index == 1u32); + assert!(verifier_set.verifier_set.signers.len() == 3); + assert_eq!(verifier_set.verifier_set.threshold, Uint128::from(2u128)); + + let mut signers = verifier_set.verifier_set.signers.values(); + let signer1 = signers.next().unwrap(); + let signer2 = signers.next().unwrap(); + + assert_eq!(signer1.pub_key.as_ref(), HexBinary::from_hex( + "45e67eaf446e6c26eb3a2b55b64339ecf3a4d1d03180bee20eb5afdd23fa644f", + ) + .unwrap().as_ref()); + assert_eq!(signer1.weight, Uint128::from(1u128)); + + assert_eq!(signer2.pub_key.as_ref(), HexBinary::from_hex( + "dd9822c7fa239dda9913ebee813ecbe69e35d88ff651548d5cc42c033a8a667b", + ) + .unwrap().as_ref()); + assert_eq!(signer2.weight, Uint128::from(1u128)); + } + + #[async_test] + async fn not_poll_started_event() { + let event = into_structured_event( + cosmwasm_std::Event::new("transfer"), + &TMAddress::random(PREFIX), + ); + + let handler = super::Handler::new( + TMAddress::random(PREFIX), + TMAddress::random(PREFIX), + MockMvxProxy::new(), + watch::channel(0).1, + ); + + assert_eq!(handler.handle(&event).await.unwrap(), vec![]); + } + + #[async_test] + async fn contract_is_not_voting_verifier() { + let event = into_structured_event( + verifier_set_poll_started_event(participants(5, None), 100), + &TMAddress::random(PREFIX), + ); + + let handler = super::Handler::new( + TMAddress::random(PREFIX), + TMAddress::random(PREFIX), + MockMvxProxy::new(), + watch::channel(0).1, + ); + + assert_eq!(handler.handle(&event).await.unwrap(), vec![]); + } + + #[async_test] + async fn verifier_is_not_a_participant() { + let voting_verifier = TMAddress::random(PREFIX); + let event = into_structured_event( + verifier_set_poll_started_event(participants(5, None), 100), + &voting_verifier, + ); + + let handler = super::Handler::new( + TMAddress::random(PREFIX), + voting_verifier, + MockMvxProxy::new(), + watch::channel(0).1, + ); + + assert_eq!(handler.handle(&event).await.unwrap(), vec![]); + } + + #[async_test] + async fn should_skip_expired_poll() { + let mut proxy = MockMvxProxy::new(); + proxy + .expect_transaction_info_with_results() + .returning(|_| None); + + let voting_verifier = TMAddress::random(PREFIX); + let verifier = TMAddress::random(PREFIX); + let expiration = 100u64; + let event: Event = into_structured_event( + verifier_set_poll_started_event( + vec![verifier.clone()].into_iter().collect(), + expiration, + ), + &voting_verifier, + ); + + let (tx, rx) = watch::channel(expiration - 1); + + let handler = super::Handler::new(verifier, voting_verifier, proxy, rx); + + // poll is not expired yet, should hit proxy + let actual = handler.handle(&event).await.unwrap(); + assert_eq!(actual.len(), 1); + + let _ = tx.send(expiration + 1); + + // poll is expired + assert_eq!(handler.handle(&event).await.unwrap(), vec![]); + } + + #[async_test] + async fn should_vote_correctly() { + let mut proxy = MockMvxProxy::new(); + proxy + .expect_transaction_info_with_results() + .returning(|_| None); + + let voting_verifier = TMAddress::random(PREFIX); + let worker = TMAddress::random(PREFIX); + + let event = into_structured_event( + verifier_set_poll_started_event(participants(5, Some(worker.clone())), 100), + &voting_verifier, + ); + + let handler = super::Handler::new(worker, voting_verifier, proxy, watch::channel(0).1); + + let actual = handler.handle(&event).await.unwrap(); + assert_eq!(actual.len(), 1); + assert!(MsgExecuteContract::from_any(actual.first().unwrap()).is_ok()); + } + + fn verifier_set_poll_started_event( + participants: Vec, + expires_at: u64, + ) -> PollStarted { + PollStarted::VerifierSet { + metadata: PollMetadata { + poll_id: "100".parse().unwrap(), + source_chain: "multiversx".parse().unwrap(), + source_gateway_address: + "erd1qqqqqqqqqqqqqpgqsvzyz88e8v8j6x3wquatxuztnxjwnw92kkls6rdtzx" + .parse() + .unwrap(), + confirmation_height: 15, + expires_at, + participants: participants + .into_iter() + .map(|addr| cosmwasm_std::Addr::unchecked(addr.to_string())) + .collect(), + }, + verifier_set: VerifierSetConfirmation { + tx_id: "dfaf64de66510723f2efbacd7ead3c4f8c856aed1afc2cb30254552aeda47312" + .parse() + .unwrap(), + event_index: 1, + verifier_set: build_verifier_set(KeyType::Ed25519, &ed25519_test_data::signers()), + }, + } + } + + fn participants(n: u8, worker: Option) -> Vec { + (0..n) + .map(|_| TMAddress::random(PREFIX)) + .chain(worker) + .collect() + } +} diff --git a/ampd/src/handlers/sui_verify_msg.rs b/ampd/src/handlers/sui_verify_msg.rs index 477514d09..3d60cb2b0 100644 --- a/ampd/src/handlers/sui_verify_msg.rs +++ b/ampd/src/handlers/sui_verify_msg.rs @@ -2,22 +2,24 @@ use std::collections::HashSet; use std::convert::TryInto; use async_trait::async_trait; +use axelar_wasm_std::voting::{PollId, Vote}; use cosmrs::cosmwasm::MsgExecuteContract; -use cosmrs::{tx::Msg, Any}; +use cosmrs::tx::Msg; +use cosmrs::Any; use error_stack::ResultExt; +use events::Error::EventTypeMismatch; +use events::Event; +use events_derive::try_from; use serde::Deserialize; use sui_types::base_types::{SuiAddress, TransactionDigest}; use tokio::sync::watch::Receiver; use tracing::info; - -use axelar_wasm_std::voting::{PollId, Vote}; -use events::{Error::EventTypeMismatch, Event}; -use events_derive::try_from; use voting_verifier::msg::ExecuteMsg; use crate::event_processor::EventHandler; use crate::handlers::errors::Error; -use crate::sui::{json_rpc::SuiClient, verifier::verify_message}; +use crate::sui::json_rpc::SuiClient; +use crate::sui::verifier::verify_message; use crate::types::{Hash, TMAddress}; type Result = error_stack::Result; @@ -154,26 +156,25 @@ mod tests { use cosmrs::tx::Msg; use cosmwasm_std; use error_stack::{Report, Result}; - use ethers::providers::ProviderError; + use ethers_providers::ProviderError; + use events::Event; use sui_types::base_types::{SuiAddress, TransactionDigest}; use tokio::sync::watch; use tokio::test as async_test; - - use events::Event; use voting_verifier::events::{PollMetadata, PollStarted, TxEventConfirmation}; + use super::PollStartedEvent; use crate::event_processor::EventHandler; - use crate::handlers::{errors::Error, tests::get_event}; + use crate::handlers::errors::Error; + use crate::handlers::tests::into_structured_event; use crate::sui::json_rpc::MockSuiClient; use crate::types::{EVMAddress, Hash, TMAddress}; - use super::PollStartedEvent; - const PREFIX: &str = "axelar"; #[test] fn should_deserialize_poll_started_event() { - let event: Result = get_event( + let event: Result = into_structured_event( poll_started_event(participants(5, None), 100), &TMAddress::random(PREFIX), ) @@ -185,7 +186,7 @@ mod tests { // Should not handle event if it is not a poll started event #[async_test] async fn not_poll_started_event() { - let event = get_event( + let event = into_structured_event( cosmwasm_std::Event::new("transfer"), &TMAddress::random(PREFIX), ); @@ -203,7 +204,7 @@ mod tests { // Should not handle event if it is not emitted from voting verifier #[async_test] async fn contract_is_not_voting_verifier() { - let event = get_event( + let event = into_structured_event( poll_started_event(participants(5, None), 100), &TMAddress::random(PREFIX), ); @@ -222,7 +223,7 @@ mod tests { #[async_test] async fn verifier_is_not_a_participant() { let voting_verifier = TMAddress::random(PREFIX); - let event = get_event( + let event = into_structured_event( poll_started_event(participants(5, None), 100), &voting_verifier, ); @@ -251,7 +252,7 @@ mod tests { let voting_verifier = TMAddress::random(PREFIX); let verifier = TMAddress::random(PREFIX); - let event = get_event( + let event = into_structured_event( poll_started_event(participants(5, Some(verifier.clone())), 100), &voting_verifier, ); @@ -274,7 +275,7 @@ mod tests { let voting_verifier = TMAddress::random(PREFIX); let verifier = TMAddress::random(PREFIX); - let event = get_event( + let event = into_structured_event( poll_started_event(participants(5, Some(verifier.clone())), 100), &voting_verifier, ); @@ -302,7 +303,7 @@ mod tests { let voting_verifier = TMAddress::random(PREFIX); let verifier = TMAddress::random(PREFIX); let expiration = 100u64; - let event: Event = get_event( + let event: Event = into_structured_event( poll_started_event(participants(5, Some(verifier.clone())), expiration), &voting_verifier, ); diff --git a/ampd/src/handlers/sui_verify_verifier_set.rs b/ampd/src/handlers/sui_verify_verifier_set.rs index c00c8ea25..4a159c916 100644 --- a/ampd/src/handlers/sui_verify_verifier_set.rs +++ b/ampd/src/handlers/sui_verify_verifier_set.rs @@ -1,22 +1,21 @@ use std::convert::TryInto; use async_trait::async_trait; +use axelar_wasm_std::msg_id::Base58TxDigestAndEventIndex; +use axelar_wasm_std::voting::{PollId, Vote}; use cosmrs::cosmwasm::MsgExecuteContract; -use cosmrs::{tx::Msg, Any}; -use cosmwasm_std::HexBinary; -use cosmwasm_std::Uint128; +use cosmrs::tx::Msg; +use cosmrs::Any; use error_stack::ResultExt; +use events::Error::EventTypeMismatch; +use events::Event; +use events_derive::try_from; use multisig::verifier_set::VerifierSet; use serde::Deserialize; use sui_types::base_types::{SuiAddress, TransactionDigest}; use tokio::sync::watch::Receiver; use tracing::{info, info_span}; use valuable::Valuable; - -use axelar_wasm_std::msg_id::base_58_event_index::Base58TxDigestAndEventIndex; -use axelar_wasm_std::voting::{PollId, Vote}; -use events::{Error::EventTypeMismatch, Event}; -use events_derive::try_from; use voting_verifier::msg::ExecuteMsg; use crate::event_processor::EventHandler; @@ -25,12 +24,6 @@ use crate::sui::json_rpc::SuiClient; use crate::sui::verifier::verify_verifier_set; use crate::types::TMAddress; -#[derive(Deserialize, Debug)] -pub struct Operators { - pub weights_by_addresses: Vec<(HexBinary, Uint128)>, - pub threshold: Uint128, -} - #[derive(Deserialize, Debug)] pub struct VerifierSetConfirmation { pub tx_id: TransactionDigest, @@ -163,30 +156,27 @@ mod tests { use std::convert::TryInto; use error_stack::{Report, Result}; - use ethers::providers::ProviderError; + use ethers_providers::ProviderError; + use events::Event; + use multisig::key::KeyType; + use multisig::test::common::{build_verifier_set, ecdsa_test_data}; use sui_types::base_types::{SuiAddress, TransactionDigest}; use tokio::sync::watch; use tokio::test as async_test; - - use events::Event; - use multisig::{ - key::KeyType, - test::common::{build_verifier_set, ecdsa_test_data}, - }; use voting_verifier::events::{PollMetadata, PollStarted, VerifierSetConfirmation}; + use super::PollStartedEvent; use crate::event_processor::EventHandler; + use crate::handlers::tests::into_structured_event; use crate::sui::json_rpc::MockSuiClient; + use crate::types::TMAddress; use crate::PREFIX; - use crate::{handlers::tests::get_event, types::TMAddress}; - - use super::PollStartedEvent; #[test] fn should_deserialize_verifier_set_poll_started_event() { let participants = (0..5).map(|_| TMAddress::random(PREFIX)).collect(); - let event: Result = get_event( + let event: Result = into_structured_event( verifier_set_poll_started_event(participants, 100), &TMAddress::random(PREFIX), ) @@ -210,7 +200,7 @@ mod tests { let voting_verifier = TMAddress::random(PREFIX); let verifier = TMAddress::random(PREFIX); let expiration = 100u64; - let event: Event = get_event( + let event: Event = into_structured_event( verifier_set_poll_started_event( vec![verifier.clone()].into_iter().collect(), expiration, diff --git a/ampd/src/health_check.rs b/ampd/src/health_check.rs index ffd366fc6..702de7598 100644 --- a/ampd/src/health_check.rs +++ b/ampd/src/health_check.rs @@ -1,11 +1,13 @@ -use error_stack::{Result, ResultExt}; use std::net::SocketAddrV4; -use thiserror::Error; -use tracing::info; -use axum::{http::StatusCode, routing::get, Json, Router}; +use axum::http::StatusCode; +use axum::routing::get; +use axum::{Json, Router}; +use error_stack::{Result, ResultExt}; use serde::{Deserialize, Serialize}; +use thiserror::Error; use tokio_util::sync::CancellationToken; +use tracing::info; #[derive(Error, Debug)] pub enum Error { @@ -55,11 +57,13 @@ struct Status { #[cfg(test)] mod tests { - use super::*; use std::net::{SocketAddr, TcpListener}; use std::time::Duration; + use tokio::test as async_test; + use super::*; + #[async_test] async fn server_lifecycle() { let bind_address = test_bind_addr(); diff --git a/ampd/src/json_rpc.rs b/ampd/src/json_rpc.rs index 7665dd8d2..38df818db 100644 --- a/ampd/src/json_rpc.rs +++ b/ampd/src/json_rpc.rs @@ -1,8 +1,9 @@ use std::fmt::Debug; use error_stack::Report; -use ethers::providers::{Http, JsonRpcClient, ProviderError}; -use serde::{de::DeserializeOwned, Serialize}; +use ethers_providers::{Http, JsonRpcClient, ProviderError}; +use serde::de::DeserializeOwned; +use serde::Serialize; use crate::url::Url; diff --git a/ampd/src/lib.rs b/ampd/src/lib.rs index 4e70c1550..89ca29a83 100644 --- a/ampd/src/lib.rs +++ b/ampd/src/lib.rs @@ -1,40 +1,36 @@ -use std::pin::Pin; use std::time::Duration; +use asyncutil::task::{CancellableTask, TaskError, TaskGroup}; use block_height_monitor::BlockHeightMonitor; -use cosmos_sdk_proto::cosmos::{ - auth::v1beta1::query_client::QueryClient, tx::v1beta1::service_client::ServiceClient, -}; -use error_stack::{report, FutureExt, Result, ResultExt}; +use broadcaster::Broadcaster; +use cosmrs::proto::cosmos::auth::v1beta1::query_client::QueryClient as AuthQueryClient; +use cosmrs::proto::cosmos::bank::v1beta1::query_client::QueryClient as BankQueryClient; +use cosmrs::proto::cosmos::tx::v1beta1::service_client::ServiceClient; +use error_stack::{FutureExt, Result, ResultExt}; +use event_processor::EventHandler; +use event_sub::EventSub; use evm::finalizer::{pick, Finalization}; use evm::json_rpc::EthereumClient; +use multiversx_sdk::blockchain::CommunicationProxy; +use queue::queued_broadcaster::QueuedBroadcaster; use router_api::ChainName; use thiserror::Error; +use tofnd::grpc::{Multisig, MultisigClient}; use tokio::signal::unix::{signal, SignalKind}; -use tokio::sync::oneshot; -use tokio_stream::Stream; +use tokio::sync::mpsc; +use tokio::time::interval; use tokio_util::sync::CancellationToken; +use tonic::transport::Channel; use tracing::info; - -use asyncutil::task::{CancellableTask, TaskError, TaskGroup}; -use broadcaster::Broadcaster; -use event_processor::EventHandler; -use event_sub::EventSub; -use events::Event; -use queue::queued_broadcaster::{QueuedBroadcaster, QueuedBroadcasterDriver}; -use state::StateUpdater; -use tofnd::grpc::{MultisigClient, SharableEcdsaClient}; use types::TMAddress; use crate::config::Config; -use crate::state::State; mod asyncutil; mod block_height_monitor; mod broadcaster; pub mod commands; pub mod config; -pub mod error; mod event_processor; mod event_sub; mod evm; @@ -42,99 +38,99 @@ mod grpc; mod handlers; mod health_check; mod json_rpc; +mod mvx; mod queue; -pub mod state; mod sui; mod tm_client; mod tofnd; mod types; mod url; -const PREFIX: &str = "axelar"; -const DEFAULT_RPC_TIMEOUT: Duration = Duration::from_secs(3); +pub use grpc::{client, proto}; -type HandlerStream = Pin> + Send>>; +use crate::asyncutil::future::RetryPolicy; +use crate::broadcaster::confirm_tx::TxConfirmer; -pub async fn run(cfg: Config, state: State) -> (State, Result<(), Error>) { - let app = prepare_app(cfg, state.clone()).await; +const PREFIX: &str = "axelar"; +const DEFAULT_RPC_TIMEOUT: Duration = Duration::from_secs(3); - match app { - Ok(app) => app.run().await, - Err(err) => (state, Err(err)), - } +pub async fn run(cfg: Config) -> Result<(), Error> { + prepare_app(cfg).await?.run().await } -async fn prepare_app(cfg: Config, state: State) -> Result, Error> { +async fn prepare_app(cfg: Config) -> Result, Error> { let Config { tm_jsonrpc, tm_grpc, broadcast, handlers, tofnd_config, - event_buffer_cap, - event_stream_timeout, + event_processor, service_registry: _service_registry, health_check_bind_addr, } = cfg; let tm_client = tendermint_rpc::HttpClient::new(tm_jsonrpc.to_string().as_str()) - .change_context(Error::Connection)?; + .change_context(Error::Connection) + .attach_printable(tm_jsonrpc.clone())?; let service_client = ServiceClient::connect(tm_grpc.to_string()) .await - .change_context(Error::Connection)?; - let query_client = QueryClient::connect(tm_grpc.to_string()) + .change_context(Error::Connection) + .attach_printable(tm_grpc.clone())?; + let auth_query_client = AuthQueryClient::connect(tm_grpc.to_string()) + .await + .change_context(Error::Connection) + .attach_printable(tm_grpc.clone())?; + let bank_query_client = BankQueryClient::connect(tm_grpc.to_string()) .await - .change_context(Error::Connection)?; - let multisig_client = MultisigClient::connect(tofnd_config.party_uid, tofnd_config.url) + .change_context(Error::Connection) + .attach_printable(tm_grpc.clone())?; + let multisig_client = MultisigClient::new(tofnd_config.party_uid, tofnd_config.url.clone()) .await - .change_context(Error::Connection)?; - let ecdsa_client = SharableEcdsaClient::new(multisig_client); + .change_context(Error::Connection) + .attach_printable(tofnd_config.url)?; let block_height_monitor = BlockHeightMonitor::connect(tm_client.clone()) .await - .change_context(Error::Connection)?; - - let mut state_updater = StateUpdater::new(state); - let pub_key = match state_updater.state().pub_key { - Some(pub_key) => pub_key, - None => { - let pub_key = ecdsa_client - .keygen(&tofnd_config.key_uid) - .await - .change_context(Error::Tofnd)?; - state_updater.as_mut().pub_key = Some(pub_key); - - pub_key - } - }; + .change_context(Error::Connection) + .attach_printable(tm_jsonrpc)?; - let verifier: TMAddress = pub_key - .account_id(PREFIX) - .expect("failed to convert to account identifier") - .into(); - - let broadcaster = broadcaster::BroadcastClient::builder() - .query_client(query_client) - .address(verifier.clone()) - .client(service_client) - .signer(ecdsa_client.clone()) + let pub_key = multisig_client + .keygen(&tofnd_config.key_uid, tofnd::Algorithm::Ecdsa) + .await + .change_context(Error::Tofnd)?; + + let broadcaster = broadcaster::UnvalidatedBasicBroadcaster::builder() + .auth_query_client(auth_query_client) + .bank_query_client(bank_query_client) + .address_prefix(PREFIX.to_string()) + .client(service_client.clone()) + .signer(multisig_client.clone()) .pub_key((tofnd_config.key_uid, pub_key)) .config(broadcast.clone()) - .build(); + .build() + .validate_fee_denomination() + .await + .change_context(Error::Broadcaster)?; let health_check_server = health_check::Server::new(health_check_bind_addr); + let verifier: TMAddress = pub_key + .account_id(PREFIX) + .expect("failed to convert to account identifier") + .into(); + App::new( tm_client, broadcaster, - state_updater, - ecdsa_client, + service_client, + multisig_client, broadcast, - event_buffer_cap, + event_processor.stream_buffer_size, block_height_monitor, health_check_server, ) - .configure_handlers(verifier, handlers, event_stream_timeout) + .configure_handlers(verifier, handlers, event_processor) .await } @@ -162,10 +158,8 @@ where event_subscriber: event_sub::EventSubscriber, event_processor: TaskGroup, broadcaster: QueuedBroadcaster, - #[allow(dead_code)] - broadcaster_driver: QueuedBroadcasterDriver, - state_updater: StateUpdater, - ecdsa_client: SharableEcdsaClient, + tx_confirmer: TxConfirmer>, + multisig_client: MultisigClient, block_height_monitor: BlockHeightMonitor, health_check_server: health_check::Server, token: CancellationToken, @@ -179,8 +173,8 @@ where fn new( tm_client: tendermint_rpc::HttpClient, broadcaster: T, - state_updater: StateUpdater, - ecdsa_client: SharableEcdsaClient, + service_client: ServiceClient, + multisig_client: MultisigClient, broadcast_cfg: broadcaster::Config, event_buffer_cap: usize, block_height_monitor: BlockHeightMonitor, @@ -190,17 +184,27 @@ where let (event_publisher, event_subscriber) = event_sub::EventPublisher::new(tm_client, event_buffer_cap); - let event_publisher = match state_updater.state().min_handler_block_height() { - Some(min_height) => event_publisher.start_from(min_height.increment()), - None => event_publisher, - }; + + let (tx_hash_sender, tx_hash_receiver) = mpsc::channel(1000); + let (tx_response_sender, tx_response_receiver) = mpsc::channel(1000); let event_processor = TaskGroup::new(); - let (broadcaster, broadcaster_driver) = QueuedBroadcaster::new( + let broadcaster = QueuedBroadcaster::new( broadcaster, broadcast_cfg.batch_gas_limit, broadcast_cfg.queue_cap, - broadcast_cfg.broadcast_interval, + interval(broadcast_cfg.broadcast_interval), + tx_hash_sender, + tx_response_receiver, + ); + let tx_confirmer = TxConfirmer::new( + service_client, + RetryPolicy::RepeatConstant { + sleep: broadcast_cfg.tx_fetch_interval, + max_attempts: broadcast_cfg.tx_fetch_max_retries.saturating_add(1).into(), + }, + tx_hash_receiver, + tx_response_sender, ); Self { @@ -208,9 +212,8 @@ where event_subscriber, event_processor, broadcaster, - broadcaster_driver, - state_updater, - ecdsa_client, + tx_confirmer, + multisig_client, block_height_monitor, health_check_server, token, @@ -221,7 +224,7 @@ where mut self, verifier: TMAddress, handler_configs: Vec, - stream_timeout: Duration, + event_processor_config: event_processor::Config, ) -> Result, Error> { for config in handler_configs { let task = match config { @@ -251,7 +254,7 @@ where rpc_client, self.block_height_monitor.latest_block_height(), ), - stream_timeout, + event_processor_config.clone(), ) } handlers::config::Config::EvmVerifierSetVerifier { @@ -280,7 +283,7 @@ where rpc_client, self.block_height_monitor.latest_block_height(), ), - stream_timeout, + event_processor_config.clone(), ) } handlers::config::Config::MultisigSigner { cosmwasm_contract } => self @@ -289,10 +292,10 @@ where handlers::multisig::Handler::new( verifier.clone(), cosmwasm_contract, - self.ecdsa_client.clone(), + self.multisig_client.clone(), self.block_height_monitor.latest_block_height(), ), - stream_timeout, + event_processor_config.clone(), ), handlers::config::Config::SuiMsgVerifier { cosmwasm_contract, @@ -313,7 +316,7 @@ where ), self.block_height_monitor.latest_block_height(), ), - stream_timeout, + event_processor_config.clone(), ), handlers::config::Config::SuiVerifierSetVerifier { cosmwasm_contract, @@ -334,7 +337,33 @@ where ), self.block_height_monitor.latest_block_height(), ), - stream_timeout, + event_processor_config.clone(), + ), + handlers::config::Config::MvxMsgVerifier { + cosmwasm_contract, + proxy_url, + } => self.create_handler_task( + "mvx-msg-verifier", + handlers::mvx_verify_msg::Handler::new( + verifier.clone(), + cosmwasm_contract, + CommunicationProxy::new(proxy_url.to_string().trim_end_matches('/').into()), + self.block_height_monitor.latest_block_height(), + ), + event_processor_config.clone(), + ), + handlers::config::Config::MvxVerifierSetVerifier { + cosmwasm_contract, + proxy_url, + } => self.create_handler_task( + "mvx-worker-set-verifier", + handlers::mvx_verify_verifier_set::Handler::new( + verifier.clone(), + cosmwasm_contract, + CommunicationProxy::new(proxy_url.to_string().trim_end_matches('/').into()), + self.block_height_monitor.latest_block_height(), + ), + event_processor_config.clone(), ), }; self.event_processor = self.event_processor.add_task(task); @@ -347,39 +376,34 @@ where &mut self, label: L, handler: H, - stream_timeout: Duration, + event_processor_config: event_processor::Config, ) -> CancellableTask> where L: AsRef, H: EventHandler + Send + Sync + 'static, { - let (handler, rx) = handlers::end_block::with_block_height_notifier(handler); - self.state_updater.register_event(label.as_ref(), rx); - + let label = label.as_ref().to_string(); let broadcaster = self.broadcaster.client(); - let sub: HandlerStream<_> = match self - .state_updater - .state() - .handler_block_height(label.as_ref()) - { - None => Box::pin(self.event_subscriber.subscribe()), - Some(&completed_height) => Box::pin(event_sub::skip_to_block( - self.event_subscriber.subscribe(), - completed_height.increment(), - )), - }; + let sub = self.event_subscriber.subscribe(); CancellableTask::create(move |token| { - event_processor::consume_events(handler, broadcaster, sub, stream_timeout, token) + event_processor::consume_events( + label, + handler, + broadcaster, + sub, + event_processor_config, + token, + ) }) } - async fn run(self) -> (State, Result<(), Error>) { + async fn run(self) -> Result<(), Error> { let Self { event_publisher, event_processor, broadcaster, - state_updater, + tx_confirmer, block_height_monitor, health_check_server, token, @@ -401,9 +425,7 @@ where exit_token.cancel(); }); - let (state_tx, mut state_rx) = oneshot::channel::(); - - let execution_result = TaskGroup::new() + TaskGroup::new() .add_task(CancellableTask::create(|token| { block_height_monitor .run(token) @@ -425,23 +447,13 @@ where .change_context(Error::EventProcessor) })) .add_task(CancellableTask::create(|_| { - broadcaster.run().change_context(Error::Broadcaster) + tx_confirmer.run().change_context(Error::TxConfirmation) })) - .add_task(CancellableTask::create(|_| async move { - // assert: the state updater only stops when all handlers that are updating their states have stopped - state_tx - .send(state_updater.run().await) - .map_err(|_| report!(Error::ReturnState)) + .add_task(CancellableTask::create(|_| { + broadcaster.run().change_context(Error::Broadcaster) })) .run(token) - .await; - - // assert: all tasks have exited, it is safe to receive the state - let state = state_rx - .try_recv() - .expect("the state sender should have been able to send the state"); - - (state, execution_result) + .await } } @@ -453,6 +465,8 @@ pub enum Error { EventProcessor, #[error("broadcaster failed")] Broadcaster, + #[error("tx confirmation failed")] + TxConfirmation, #[error("tofnd failed")] Tofnd, #[error("connection failed")] diff --git a/ampd/src/main.rs b/ampd/src/main.rs index 4eea80f94..5445cca7a 100644 --- a/ampd/src/main.rs +++ b/ampd/src/main.rs @@ -4,21 +4,19 @@ use std::path::{Path, PathBuf}; use std::process::ExitCode; use ::config::{Config as cfg, Environment, File, FileFormat, FileSourceFile}; -use clap::{arg, command, Parser, ValueEnum}; -use config::ConfigError; -use error_stack::{Report, ResultExt}; -use tracing::{error, info}; -use valuable::Valuable; - use ampd::commands::{ - bond_verifier, daemon, deregister_chain_support, register_chain_support, register_public_key, - verifier_address, SubCommand, + bond_verifier, claim_stake, daemon, deregister_chain_support, register_chain_support, + register_public_key, send_tokens, unbond_verifier, verifier_address, SubCommand, }; use ampd::config::Config; use ampd::Error; -use axelar_wasm_std::utils::InspectorResult; use axelar_wasm_std::FnExt; +use clap::{arg, command, Parser, ValueEnum}; +use config::ConfigError; +use error_stack::{Report, ResultExt}; use report::LoggableError; +use tracing::{error, info}; +use valuable::Valuable; #[derive(Debug, Parser, Valuable)] #[command(version)] @@ -27,10 +25,6 @@ struct Args { #[arg(short, long, default_values_os_t = vec![std::path::PathBuf::from("~/.ampd/config.toml"), std::path::PathBuf::from("config.toml")])] pub config: Vec, - /// Set the paths for state file lookup - #[arg(short, long, default_value_os_t = std::path::PathBuf::from("~/.ampd/state.json"))] - pub state: PathBuf, - /// Set the output style of the logs #[arg(short, long, value_enum, default_value_t = Output::Text)] pub output: Output, @@ -51,28 +45,28 @@ async fn main() -> ExitCode { set_up_logger(&args.output); let cfg = init_config(&args.config); - let state_path = expand_home_dir(&args.state); let result = match args.cmd { Some(SubCommand::Daemon) | None => { info!(args = args.as_value(), "starting daemon"); - daemon::run(cfg, &state_path).await.then(|result| { + daemon::run(cfg).await.then(|result| { info!("shutting down"); result }) } - Some(SubCommand::BondVerifier(args)) => bond_verifier::run(cfg, &state_path, args).await, + Some(SubCommand::BondVerifier(args)) => bond_verifier::run(cfg, args).await, Some(SubCommand::RegisterChainSupport(args)) => { - register_chain_support::run(cfg, &state_path, args).await + register_chain_support::run(cfg, args).await } Some(SubCommand::DeregisterChainSupport(args)) => { - deregister_chain_support::run(cfg, &state_path, args).await - } - Some(SubCommand::RegisterPublicKey) => register_public_key::run(cfg, &state_path).await, - Some(SubCommand::VerifierAddress) => { - verifier_address::run(cfg.tofnd_config, &state_path).await + deregister_chain_support::run(cfg, args).await } + Some(SubCommand::RegisterPublicKey(args)) => register_public_key::run(cfg, args).await, + Some(SubCommand::VerifierAddress) => verifier_address::run(cfg.tofnd_config).await, + Some(SubCommand::UnbondVerifier(args)) => unbond_verifier::run(cfg, args).await, + Some(SubCommand::ClaimStake(args)) => claim_stake::run(cfg, args).await, + Some(SubCommand::SendTokens(args)) => send_tokens::run(cfg, args).await, }; match result { @@ -111,8 +105,8 @@ fn init_config(config_paths: &[PathBuf]) -> Config { parse_config(files) .change_context(Error::LoadConfig) - .tap_err(|report| error!(err = LoggableError::from(report).as_value(), "{report}")) - .unwrap_or(Config::default()) + .inspect_err(|report| error!(err = LoggableError::from(report).as_value(), "{report}")) + .unwrap_or_default() } fn find_config_files(config: &[PathBuf]) -> Vec> { diff --git a/ampd/src/mvx/error.rs b/ampd/src/mvx/error.rs new file mode 100644 index 000000000..4a22a59d9 --- /dev/null +++ b/ampd/src/mvx/error.rs @@ -0,0 +1,9 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum Error { + #[error("provided key is not ed25519")] + NotEd25519Key, + #[error("required property is empty")] + PropertyEmpty, +} diff --git a/ampd/src/mvx/mod.rs b/ampd/src/mvx/mod.rs new file mode 100644 index 000000000..331b66aa9 --- /dev/null +++ b/ampd/src/mvx/mod.rs @@ -0,0 +1,85 @@ +use axelar_wasm_std::hash::Hash; +use cosmwasm_std::Uint256; +use multisig::key::PublicKey; +use multisig::msg::Signer; +use multisig::verifier_set::VerifierSet; +use sha3::{Digest, Keccak256}; + +use crate::mvx::error::Error; + +pub mod error; +pub mod proxy; +pub mod verifier; + +pub struct WeightedSigner { + pub signer: [u8; 32], + pub weight: Vec, +} + +pub struct WeightedSigners { + pub signers: Vec, + pub threshold: Vec, + pub nonce: [u8; 32], +} + +impl WeightedSigners { + pub fn hash(&self) -> Hash { + let mut encoded = Vec::new(); + + for signer in self.signers.iter() { + encoded.push(signer.signer.as_slice()); + encoded.push(signer.weight.as_slice()); + } + + encoded.push(self.threshold.as_slice()); + encoded.push(self.nonce.as_slice()); + + Keccak256::digest(encoded.concat()).into() + } +} + +impl From<&Signer> for WeightedSigner { + fn from(signer: &Signer) -> Self { + WeightedSigner { + signer: ed25519_key(&signer.pub_key).expect("not ed25519 key"), + weight: uint256_to_compact_vec(signer.weight.into()), + } + } +} + +impl From<&VerifierSet> for WeightedSigners { + fn from(verifier_set: &VerifierSet) -> Self { + let mut signers = verifier_set + .signers + .values() + .map(WeightedSigner::from) + .collect::>(); + + signers.sort_by_key(|weighted_signer| weighted_signer.signer); + + WeightedSigners { + signers, + threshold: uint256_to_compact_vec(verifier_set.threshold.into()), + nonce: Uint256::from(verifier_set.created_at).to_be_bytes(), + } + } +} + +fn uint256_to_compact_vec(value: Uint256) -> Vec { + if value.is_zero() { + return Vec::new(); + } + + let bytes = value.to_be_bytes(); + let slice_from = bytes.iter().position(|byte| *byte != 0).unwrap_or(0); + + bytes[slice_from..].to_vec() +} + +pub fn ed25519_key(pub_key: &PublicKey) -> Result<[u8; 32], Error> { + return match pub_key { + PublicKey::Ed25519(ed25519_key) => Ok(<[u8; 32]>::try_from(ed25519_key.as_ref()) + .expect("couldn't convert pubkey to ed25519 public key")), + _ => Err(Error::NotEd25519Key), + }; +} diff --git a/ampd/src/mvx/proxy.rs b/ampd/src/mvx/proxy.rs new file mode 100644 index 000000000..de5299c4e --- /dev/null +++ b/ampd/src/mvx/proxy.rs @@ -0,0 +1,63 @@ +use std::collections::{HashMap, HashSet}; + +use async_trait::async_trait; +use futures::future::join_all; +use hex::ToHex; +use mockall::automock; +use multiversx_sdk::blockchain::CommunicationProxy; +use multiversx_sdk::data::transaction::TransactionOnNetwork; + +use crate::types::Hash; + +const STATUS_SUCCESS: &str = "success"; + +#[automock] +#[async_trait] +pub trait MvxProxy { + async fn transactions_info_with_results( + &self, + tx_hashes: HashSet, + ) -> HashMap; + + async fn transaction_info_with_results(&self, tx_hash: &Hash) -> Option; + + fn is_valid_transaction(tx: &TransactionOnNetwork) -> bool; +} + +#[async_trait] +impl MvxProxy for CommunicationProxy { + async fn transactions_info_with_results( + &self, + tx_hashes: HashSet, + ) -> HashMap { + let tx_hashes = Vec::from_iter(tx_hashes); + + let txs = join_all( + tx_hashes + .iter() + .map(|tx_hash| self.transaction_info_with_results(tx_hash)), + ) + .await; + + tx_hashes + .into_iter() + .zip(txs) + .filter_map(|(hash, tx)| { + tx.as_ref()?; + + Some((hash, tx.unwrap())) + }) + .collect() + } + + async fn transaction_info_with_results(&self, tx_hash: &Hash) -> Option { + self.get_transaction_info_with_results(tx_hash.encode_hex::().as_str()) + .await + .ok() + .filter(Self::is_valid_transaction) + } + + fn is_valid_transaction(tx: &TransactionOnNetwork) -> bool { + tx.hash.is_some() && tx.logs.is_some() && tx.status == *STATUS_SUCCESS + } +} diff --git a/ampd/src/mvx/verifier.rs b/ampd/src/mvx/verifier.rs new file mode 100644 index 000000000..3b201471a --- /dev/null +++ b/ampd/src/mvx/verifier.rs @@ -0,0 +1,586 @@ +use axelar_wasm_std::voting::Vote; +use base64::engine::general_purpose::STANDARD; +use base64::Engine; +use hex::ToHex; +use multiversx_sdk::data::address::Address; +use multiversx_sdk::data::transaction::{Events, TransactionOnNetwork}; +use num_traits::cast; + +use crate::handlers::mvx_verify_msg::Message; +use crate::handlers::mvx_verify_verifier_set::VerifierSetConfirmation; +use crate::mvx::error::Error; +use crate::mvx::WeightedSigners; +use crate::types::Hash; + +const CONTRACT_CALL_IDENTIFIER: &str = "callContract"; +const CONTRACT_CALL_EVENT: &str = "contract_call_event"; + +const ROTATE_SIGNERS_IDENTIFIER: &str = "rotateSigners"; +const SIGNERS_ROTATED_EVENT: &str = "signers_rotated_event"; + +impl Message { + fn eq_event(&self, event: &Events) -> Result> { + if event.identifier != CONTRACT_CALL_IDENTIFIER { + return Ok(false); + } + + let topics = event.topics.as_ref().ok_or(Error::PropertyEmpty)?; + + let event_name = topics.first().ok_or(Error::PropertyEmpty)?; + let event_name = STANDARD.decode(event_name)?; + if event_name.as_slice() != CONTRACT_CALL_EVENT.as_bytes() { + return Ok(false); + } + + let sender = topics.get(1).ok_or(Error::PropertyEmpty)?; + let sender = STANDARD.decode(sender)?; + if sender.len() != 32 || sender[0..32] != self.source_address.to_bytes() { + return Ok(false); + } + + let destination_chain = topics.get(2).ok_or(Error::PropertyEmpty)?; + let destination_chain = STANDARD.decode(destination_chain)?; + let destination_chain = String::from_utf8(destination_chain)?; + if destination_chain != self.destination_chain.as_ref() { + return Ok(false); + } + + let destination_address = topics.get(3).ok_or(Error::PropertyEmpty)?; + let destination_address = STANDARD.decode(destination_address)?; + let destination_address = String::from_utf8(destination_address)?; + if destination_address != self.destination_address { + return Ok(false); + } + + let payload_hash = topics.get(4).ok_or(Error::PropertyEmpty)?; + let payload_hash = STANDARD.decode(payload_hash)?; + if payload_hash.len() != 32 + || Hash::from_slice(payload_hash.as_slice()) != self.payload_hash + { + return Ok(false); + } + + Ok(true) + } +} + +impl VerifierSetConfirmation { + fn eq_event(&self, event: &Events) -> Result> { + if event.identifier != ROTATE_SIGNERS_IDENTIFIER { + return Ok(false); + } + + let topics = event.topics.as_ref().ok_or(Error::PropertyEmpty)?; + + let event_name = topics.first().ok_or(Error::PropertyEmpty)?; + let event_name = STANDARD.decode(event_name)?; + if event_name.as_slice() != SIGNERS_ROTATED_EVENT.as_bytes() { + return Ok(false); + } + + let signers_hash = topics.get(2).ok_or(Error::PropertyEmpty)?; + let signers_hash = STANDARD.decode(signers_hash)?; + + let weighted_signers = WeightedSigners::from(&self.verifier_set); + + if signers_hash.len() != 32 || signers_hash.as_slice() != weighted_signers.hash().as_slice() + { + return Ok(false); + } + + Ok(true) + } +} + +fn find_event<'a>( + transaction: &'a TransactionOnNetwork, + gateway_address: &Address, + log_index: u32, +) -> Option<&'a Events> { + let log_index: usize = cast(log_index).expect("log_index must be a valid usize"); + + let event = transaction.logs.as_ref()?.events.get(log_index)?; + + if event.address.to_bytes() != gateway_address.to_bytes() { + return None; + } + + Some(event) +} + +pub fn verify_message( + gateway_address: &Address, + transaction: &TransactionOnNetwork, + message: &Message, +) -> Vote { + let hash = transaction.hash.as_deref().unwrap_or_default(); + + if hash.is_empty() { + return Vote::NotFound; + } + + match find_event(transaction, gateway_address, message.event_index) { + Some(event) + if hash == message.tx_id.encode_hex::().as_str() + && message.eq_event(event).unwrap_or(false) => + { + Vote::SucceededOnChain + } + _ => Vote::NotFound, + } +} + +pub fn verify_verifier_set( + gateway_address: &Address, + transaction: &TransactionOnNetwork, + verifier_set: VerifierSetConfirmation, +) -> Vote { + let hash = transaction.hash.as_deref().unwrap_or_default(); + + if hash.is_empty() { + return Vote::NotFound; + } + + match find_event(transaction, gateway_address, verifier_set.event_index) { + Some(event) + if hash == verifier_set.tx_id.encode_hex::().as_str() + && verifier_set.eq_event(event).unwrap_or(false) => + { + Vote::SucceededOnChain + } + _ => Vote::NotFound, + } +} + +#[cfg(test)] +mod tests { + use axelar_wasm_std::voting::Vote; + use base64::engine::general_purpose::STANDARD; + use base64::Engine; + use cosmwasm_std::{HexBinary, Uint128}; + use hex::ToHex; + use multisig::key::KeyType; + use multisig::test::common::{build_verifier_set, ed25519_test_data}; + use multiversx_sdk::data::address::Address; + use multiversx_sdk::data::transaction::{ApiLogs, Events, TransactionOnNetwork}; + + use crate::handlers::mvx_verify_msg::Message; + use crate::handlers::mvx_verify_verifier_set::VerifierSetConfirmation; + use crate::mvx::verifier::{ + verify_message, verify_verifier_set, CONTRACT_CALL_EVENT, CONTRACT_CALL_IDENTIFIER, + ROTATE_SIGNERS_IDENTIFIER, SIGNERS_ROTATED_EVENT, + }; + use crate::types::{EVMAddress, Hash}; + + // test verify message + #[test] + fn should_not_verify_msg_if_tx_id_does_not_match() { + let (gateway_address, tx, mut msg) = get_matching_msg_and_tx(); + + msg.tx_id = "ffaf64de66510723f2efbacd7ead3c4f8c856aed1afc2cb30254552aeda47313" + .parse() + .unwrap(); + assert_eq!(verify_message(&gateway_address, &tx, &msg), Vote::NotFound); + } + + #[test] + fn should_not_verify_msg_if_no_logs() { + let (gateway_address, mut tx, msg) = get_matching_msg_and_tx(); + + tx.logs = None; + assert_eq!(verify_message(&gateway_address, &tx, &msg), Vote::NotFound); + } + + #[test] + fn should_not_verify_msg_if_no_log_for_event_index() { + let (gateway_address, tx, mut msg) = get_matching_msg_and_tx(); + + msg.event_index = 2; + assert_eq!(verify_message(&gateway_address, &tx, &msg), Vote::NotFound); + } + + #[test] + fn should_not_verify_msg_if_event_index_does_not_match() { + let (gateway_address, tx, mut msg) = get_matching_msg_and_tx(); + + msg.event_index = 0; + assert_eq!(verify_message(&gateway_address, &tx, &msg), Vote::NotFound); + } + + #[test] + fn should_not_verify_msg_if_not_from_gateway() { + let (gateway_address, mut tx, msg) = get_matching_msg_and_tx(); + + let events = &mut tx.logs.as_mut().unwrap().events; + let event = events.get_mut(1).unwrap(); + event.address = Address::from_bech32_string( + "erd1qqqqqqqqqqqqqpgqzqvm5ywqqf524efwrhr039tjs29w0qltkklsa05pk7", + ) + .unwrap(); + assert_eq!(verify_message(&gateway_address, &tx, &msg), Vote::NotFound); + } + + #[test] + fn should_not_verify_msg_if_not_call_contract_identifier() { + let (gateway_address, mut tx, msg) = get_matching_msg_and_tx(); + + let events = &mut tx.logs.as_mut().unwrap().events; + let event = events.get_mut(1).unwrap(); + event.identifier = "other".into(); + assert_eq!(verify_message(&gateway_address, &tx, &msg), Vote::NotFound); + } + + #[test] + fn should_not_verify_msg_if_not_call_contract_event() { + let (gateway_address, mut tx, msg) = get_matching_msg_and_tx(); + + let events = &mut tx.logs.as_mut().unwrap().events; + let event = events.get_mut(1).unwrap(); + + let topics = event.topics.as_mut().unwrap(); + let topic = topics.get_mut(0).unwrap(); + *topic = "other".into(); + assert_eq!(verify_message(&gateway_address, &tx, &msg), Vote::NotFound); + } + + #[test] + fn should_not_verify_msg_if_source_address_does_not_match() { + let (gateway_address, tx, mut msg) = get_matching_msg_and_tx(); + + msg.source_address = Address::from_bech32_string( + "erd1qqqqqqqqqqqqqpgqsvzyz88e8v8j6x3wquatxuztnxjwnw92kkls6rdtzx", + ) + .unwrap(); + assert_eq!(verify_message(&gateway_address, &tx, &msg), Vote::NotFound); + } + + #[test] + fn should_not_verify_msg_if_destination_chain_does_not_match() { + let (gateway_address, tx, mut msg) = get_matching_msg_and_tx(); + + msg.destination_chain = "otherchain".parse().unwrap(); + assert_eq!(verify_message(&gateway_address, &tx, &msg), Vote::NotFound); + } + + #[test] + fn should_not_verify_msg_if_destination_address_does_not_match() { + let (gateway_address, tx, mut msg) = get_matching_msg_and_tx(); + + msg.destination_address = EVMAddress::random().to_string(); + assert_eq!(verify_message(&gateway_address, &tx, &msg), Vote::NotFound); + } + + #[test] + fn should_not_verify_msg_if_payload_hash_does_not_match() { + let (gateway_address, tx, mut msg) = get_matching_msg_and_tx(); + + msg.payload_hash = Hash::random(); + assert_eq!(verify_message(&gateway_address, &tx, &msg), Vote::NotFound); + } + + #[test] + fn should_verify_msg() { + let (gateway_address, tx, msg) = get_matching_msg_and_tx(); + + assert_eq!( + verify_message(&gateway_address, &tx, &msg), + Vote::SucceededOnChain + ); + } + + // test verify worker set + #[test] + fn should_not_verify_verifier_set_if_tx_id_does_not_match() { + let (gateway_address, tx, mut verifier_set) = get_matching_verifier_set_and_tx(); + + verifier_set.tx_id = "ffaf64de66510723f2efbacd7ead3c4f8c856aed1afc2cb30254552aeda47313" + .parse() + .unwrap(); + assert_eq!( + verify_verifier_set(&gateway_address, &tx, verifier_set), + Vote::NotFound + ); + } + + #[test] + fn should_not_verify_verifier_set_if_no_logs() { + let (gateway_address, mut tx, verifier_set) = get_matching_verifier_set_and_tx(); + + tx.logs = None; + assert_eq!( + verify_verifier_set(&gateway_address, &tx, verifier_set), + Vote::NotFound + ); + } + + #[test] + fn should_not_verify_verifier_set_if_no_log_for_event_index() { + let (gateway_address, tx, mut verifier_set) = get_matching_verifier_set_and_tx(); + + verifier_set.event_index = 2; + assert_eq!( + verify_verifier_set(&gateway_address, &tx, verifier_set), + Vote::NotFound + ); + } + + #[test] + fn should_not_verify_verifier_set_if_event_index_does_not_match() { + let (gateway_address, tx, mut verifier_set) = get_matching_verifier_set_and_tx(); + + verifier_set.event_index = 0; + assert_eq!( + verify_verifier_set(&gateway_address, &tx, verifier_set), + Vote::NotFound + ); + } + + #[test] + fn should_not_verify_verifier_set_if_not_from_gateway() { + let (gateway_address, mut tx, verifier_set) = get_matching_verifier_set_and_tx(); + + let events = &mut tx.logs.as_mut().unwrap().events; + let event = events.get_mut(1).unwrap(); + event.address = Address::from_bech32_string( + "erd1qqqqqqqqqqqqqpgqzqvm5ywqqf524efwrhr039tjs29w0qltkklsa05pk7", + ) + .unwrap(); + assert_eq!( + verify_verifier_set(&gateway_address, &tx, verifier_set), + Vote::NotFound + ); + } + + #[test] + fn should_not_verify_verifier_set_if_not_rotate_signers_identifier() { + let (gateway_address, mut tx, verifier_set) = get_matching_verifier_set_and_tx(); + + let events = &mut tx.logs.as_mut().unwrap().events; + let event = events.get_mut(1).unwrap(); + event.identifier = "callContract".into(); + assert_eq!( + verify_verifier_set(&gateway_address, &tx, verifier_set), + Vote::NotFound + ); + } + + #[test] + fn should_not_verify_verifier_set_if_not_signers_rotated_event() { + let (gateway_address, mut tx, verifier_set) = get_matching_verifier_set_and_tx(); + + let events = &mut tx.logs.as_mut().unwrap().events; + let event = events.get_mut(1).unwrap(); + + let topics = event.topics.as_mut().unwrap(); + let topic = topics.get_mut(0).unwrap(); + *topic = "otherEvent".into(); + assert_eq!( + verify_verifier_set(&gateway_address, &tx, verifier_set), + Vote::NotFound + ); + } + + #[test] + fn should_not_verify_worker_set_if_verifier_set_does_not_match() { + let (gateway_address, tx, mut verifier_set) = get_matching_verifier_set_and_tx(); + + verifier_set.verifier_set.threshold = Uint128::from(10u128); + assert_eq!( + verify_verifier_set(&gateway_address, &tx, verifier_set), + Vote::NotFound + ); + } + + #[test] + fn should_verify_verifier() { + let (gateway_address, tx, verifier_set) = get_matching_verifier_set_and_tx(); + + assert_eq!( + verify_verifier_set(&gateway_address, &tx, verifier_set), + Vote::SucceededOnChain + ); + } + + fn get_matching_msg_and_tx() -> (Address, TransactionOnNetwork, Message) { + let gateway_address = Address::from_bech32_string( + "erd1qqqqqqqqqqqqqpgqsvzyz88e8v8j6x3wquatxuztnxjwnw92kkls6rdtzx", + ) + .unwrap(); + let source_address = Address::from_bech32_string( + "erd1qqqqqqqqqqqqqpgqzqvm5ywqqf524efwrhr039tjs29w0qltkklsa05pk7", + ) + .unwrap(); + let tx_id = "dfaf64de66510723f2efbacd7ead3c4f8c856aed1afc2cb30254552aeda47312" + .parse() + .unwrap(); + + let msg = Message { + tx_id, + event_index: 1, + source_address, + destination_chain: "ethereum".parse().unwrap(), + destination_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), + payload_hash: Hash::random(), + }; + + // Only the first 32 bytes matter for data + let payload_hash = msg.payload_hash; + + let wrong_event = Events { + address: gateway_address.clone(), + identifier: CONTRACT_CALL_IDENTIFIER.into(), + topics: Some(vec![STANDARD.encode(SIGNERS_ROTATED_EVENT)]), // wrong event name + data: None, + }; + + // On MultiversX, topics and data are base64 encoded + let event = Events { + address: gateway_address.clone(), + identifier: CONTRACT_CALL_IDENTIFIER.into(), + topics: Some(vec![ + STANDARD.encode(CONTRACT_CALL_EVENT), + STANDARD.encode(msg.source_address.clone().to_bytes()), + STANDARD.encode(msg.destination_chain.to_string()), + STANDARD.encode(msg.destination_address.clone()), + STANDARD.encode(payload_hash), + ]), + data: Some("".into()), // data is irrelevant here since it contains only the offchain payload + }; + + let other_address = Address::from_bech32_string( + "erd1qqqqqqqqqqqqqpgqzqvm5ywqqf524efwrhr039tjs29w0qltkklsa05pk7", + ) + .unwrap(); + let tx_block = TransactionOnNetwork { + hash: Some(msg.tx_id.encode_hex::()), + logs: Some(ApiLogs { + address: other_address.clone(), + events: vec![wrong_event, event], + }), + status: "success".into(), + // The rest are irrelevant but there is no default + kind: "".into(), + nonce: 1, + round: 1, + epoch: 1, + value: "".into(), + receiver: other_address.clone(), + sender: other_address, + gas_price: 0, + gas_limit: 0, + signature: "".into(), + source_shard: 1, + destination_shard: 1, + block_nonce: 1, + block_hash: "".into(), + notarized_at_source_in_meta_nonce: Some(0), + notarized_at_source_in_meta_hash: Some("".into()), + notarized_at_destination_in_meta_nonce: Some(0), + notarized_at_destination_in_meta_hash: Some("".into()), + miniblock_type: "".into(), + miniblock_hash: "".into(), + timestamp: 1, + data: None, + hyperblock_nonce: Some(1), + hyperblock_hash: Some("".into()), + smart_contract_results: None, + processing_type_on_destination: "".into(), + }; + + (gateway_address, tx_block, msg) + } + + fn get_matching_verifier_set_and_tx() -> (Address, TransactionOnNetwork, VerifierSetConfirmation) + { + let gateway_address = Address::from_bech32_string( + "erd1qqqqqqqqqqqqqpgqsvzyz88e8v8j6x3wquatxuztnxjwnw92kkls6rdtzx", + ) + .unwrap(); + let tx_id = "dfaf64de66510723f2efbacd7ead3c4f8c856aed1afc2cb30254552aeda47312" + .parse() + .unwrap(); + + let mut verifier_set_confirmation = VerifierSetConfirmation { + tx_id, + event_index: 1, + verifier_set: build_verifier_set(KeyType::Ed25519, &ed25519_test_data::signers()), + }; + verifier_set_confirmation.verifier_set.created_at = 5; + + // 00000003 - length of new signers + // 45e67eaf446e6c26eb3a2b55b64339ecf3a4d1d03180bee20eb5afdd23fa644f - first new signer + // 00000001 01 - length of biguint weight followed by 1 as hex + // c387253d29085a8036d6ae2cafb1b14699751417c0ce302cfe03da279e6b5c04 - second new signer + // 00000001 01 - length of biguint weight followed by 1 as hex + // dd9822c7fa239dda9913ebee813ecbe69e35d88ff651548d5cc42c033a8a667b - third new signer + // 00000001 01 - length of biguint weight followed by 1 as hex + // 00000001 02 - length of biguint threshold followed by 2 as hex + // 0000000000000000000000000000000000000000000000000000000000000005 - the nonce (created_at date as uint256) + let data = HexBinary::from_hex("0000000345e67eaf446e6c26eb3a2b55b64339ecf3a4d1d03180bee20eb5afdd23fa644f0000000101c387253d29085a8036d6ae2cafb1b14699751417c0ce302cfe03da279e6b5c040000000101dd9822c7fa239dda9913ebee813ecbe69e35d88ff651548d5cc42c033a8a667b000000010100000001020000000000000000000000000000000000000000000000000000000000000005") + .unwrap(); + let signers_hash = + HexBinary::from_hex("29f81aa379fa1f5973d05dd25e5ae4bc1afa2aa30156b1db5ec437a46ba4fd28") + .unwrap(); + + let wrong_event = Events { + address: gateway_address.clone(), + identifier: ROTATE_SIGNERS_IDENTIFIER.into(), + topics: Some(vec![STANDARD.encode(CONTRACT_CALL_EVENT)]), // wrong event name + data: None, + }; + + // On MultiversX, topics and data are base64 encoded + let event = Events { + address: gateway_address.clone(), + identifier: ROTATE_SIGNERS_IDENTIFIER.into(), + topics: Some(vec![ + STANDARD.encode(SIGNERS_ROTATED_EVENT), + STANDARD.encode("0"), // epoch (irrelevant here) + STANDARD.encode(signers_hash), // signers hash + ]), + data: Some(STANDARD.encode(data)), + }; + + let other_address = Address::from_bech32_string( + "erd1qqqqqqqqqqqqqpgqzqvm5ywqqf524efwrhr039tjs29w0qltkklsa05pk7", + ) + .unwrap(); + let tx_block = TransactionOnNetwork { + hash: Some(tx_id.encode_hex::()), + logs: Some(ApiLogs { + address: other_address.clone(), + events: vec![wrong_event, event], + }), + status: "success".into(), + // The rest are irrelevant but there is no default + kind: "".into(), + nonce: 1, + round: 1, + epoch: 1, + value: "".into(), + receiver: other_address.clone(), + sender: other_address, + gas_price: 0, + gas_limit: 0, + signature: "".into(), + source_shard: 1, + destination_shard: 1, + block_nonce: 1, + block_hash: "".into(), + notarized_at_source_in_meta_nonce: Some(0), + notarized_at_source_in_meta_hash: Some("".into()), + notarized_at_destination_in_meta_nonce: Some(0), + notarized_at_destination_in_meta_hash: Some("".into()), + miniblock_type: "".into(), + miniblock_hash: "".into(), + timestamp: 1, + data: None, + hyperblock_nonce: Some(1), + hyperblock_hash: Some("".into()), + smart_contract_results: None, + processing_type_on_destination: "".into(), + }; + + (gateway_address, tx_block, verifier_set_confirmation) + } +} diff --git a/ampd/src/queue/mod.rs b/ampd/src/queue/mod.rs index 2a6ce0415..d7568b662 100644 --- a/ampd/src/queue/mod.rs +++ b/ampd/src/queue/mod.rs @@ -1,2 +1,3 @@ mod msg_queue; +mod proto; pub mod queued_broadcaster; diff --git a/ampd/src/queue/msg_queue.rs b/ampd/src/queue/msg_queue.rs index b9d98a51d..d12527e2e 100644 --- a/ampd/src/queue/msg_queue.rs +++ b/ampd/src/queue/msg_queue.rs @@ -44,8 +44,9 @@ impl MsgQueue { #[cfg(test)] mod test { - use cosmos_sdk_proto::Any; - use cosmrs::{bank::MsgSend, tx::Msg, AccountId}; + use cosmrs::bank::MsgSend; + use cosmrs::tx::Msg; + use cosmrs::{AccountId, Any}; use super::MsgQueue; diff --git a/ampd/src/queue/proto.rs b/ampd/src/queue/proto.rs new file mode 100644 index 000000000..322f2ab62 --- /dev/null +++ b/ampd/src/queue/proto.rs @@ -0,0 +1,42 @@ +use cosmrs::proto::traits::TypeUrl; + +pub mod axelar { + pub mod auxiliary { + pub mod v1beta1 { + tonic::include_proto!("axelar.auxiliary.v1beta1"); + } + } +} + +mod cosmos { + pub mod base { + pub mod abci { + pub mod v1beta1 { + tonic::include_proto!("cosmos.base.abci.v1beta1"); + } + } + } +} + +mod tendermint { + #[allow(clippy::large_enum_variant)] + pub mod abci { + tonic::include_proto!("tendermint.abci"); + } + + pub mod crypto { + tonic::include_proto!("tendermint.crypto"); + } + + pub mod types { + tonic::include_proto!("tendermint.types"); + } + + pub mod version { + tonic::include_proto!("tendermint.version"); + } +} + +impl TypeUrl for axelar::auxiliary::v1beta1::BatchRequest { + const TYPE_URL: &'static str = "/axelar.auxiliary.v1beta1.BatchRequest"; +} diff --git a/ampd/src/queue/queued_broadcaster.rs b/ampd/src/queue/queued_broadcaster.rs index dd7638463..88eb83735 100644 --- a/ampd/src/queue/queued_broadcaster.rs +++ b/ampd/src/queue/queued_broadcaster.rs @@ -1,19 +1,21 @@ -use std::time::Duration; - use async_trait::async_trait; +use cosmrs::tx::MessageExt; use cosmrs::{Any, Gas}; use error_stack::{self, Report, ResultExt}; use mockall::automock; use thiserror::Error; -use tokio::time; -use tokio::{select, sync::mpsc}; -use tracing::info; -use tracing::warn; +use tokio::select; +use tokio::sync::{mpsc, oneshot}; +use tokio::time::Interval; +use tracing::{debug, info, warn}; use super::msg_queue::MsgQueue; +use super::proto; +use crate::broadcaster::confirm_tx::{TxResponse, TxStatus}; use crate::broadcaster::Broadcaster; type Result = error_stack::Result; +type MsgAndResChan = (Any, oneshot::Sender); #[derive(Error, Debug)] pub enum Error { @@ -21,23 +23,14 @@ pub enum Error { EstimateFee, #[error("failed broadcasting messages in queue")] Broadcast, + #[error("failed returning response to client")] + Client, #[error("failed to queue message")] Queue, -} - -pub struct QueuedBroadcasterDriver { - #[allow(dead_code)] - broadcast_tx: mpsc::Sender<()>, -} - -impl QueuedBroadcasterDriver { - #[allow(dead_code)] - pub async fn force_broadcast(&self) -> Result { - self.broadcast_tx - .send(()) - .await - .map_err(|_| Report::new(Error::Broadcast)) - } + #[error("failed to confirm transaction")] + TxConfirmation, + #[error("failed to decode tx response")] + DecodeTxResponse(#[from] prost::DecodeError), } #[automock] @@ -47,16 +40,19 @@ pub trait BroadcasterClient { } pub struct QueuedBroadcasterClient { - sender: mpsc::Sender, + sender: mpsc::Sender, } #[async_trait] impl BroadcasterClient for QueuedBroadcasterClient { async fn broadcast(&self, msg: Any) -> Result { + let (tx, rx) = oneshot::channel(); self.sender - .send(msg) + .send((msg, tx)) .await - .map_err(|_| Report::new(Error::Broadcast)) + .change_context(Error::Broadcast)?; + + rx.await.change_context(Error::Broadcast)? } } @@ -67,9 +63,10 @@ where broadcaster: T, queue: MsgQueue, batch_gas_limit: Gas, - broadcast_interval: Duration, - channel: (mpsc::Sender, mpsc::Receiver), - broadcast_rx: mpsc::Receiver<()>, + channel: Option<(mpsc::Sender, mpsc::Receiver)>, + broadcast_interval: Interval, + tx_confirmer_sender: mpsc::Sender, + tx_confirmer_receiver: mpsc::Receiver, } impl QueuedBroadcaster @@ -80,160 +77,219 @@ where broadcaster: T, batch_gas_limit: Gas, capacity: usize, - broadcast_interval: Duration, - ) -> (Self, QueuedBroadcasterDriver) { - let (broadcast_tx, broadcast_rx) = mpsc::channel(1); - - ( - Self { - broadcaster, - queue: MsgQueue::default(), - batch_gas_limit, - broadcast_interval, - channel: mpsc::channel(capacity), - broadcast_rx, - }, - QueuedBroadcasterDriver { broadcast_tx }, - ) + broadcast_interval: Interval, + tx_confirmer_sender: mpsc::Sender, + tx_res_receiver: mpsc::Receiver, + ) -> Self { + Self { + broadcaster, + queue: MsgQueue::default(), + batch_gas_limit, + channel: Some(mpsc::channel(capacity)), + broadcast_interval, + tx_confirmer_sender, + tx_confirmer_receiver: tx_res_receiver, + } } pub async fn run(mut self) -> Result { - let (tx, mut rx) = self.channel; - drop(tx); - - let mut queue = self.queue; - let mut broadcaster = self.broadcaster; - - let mut interval = time::interval(self.broadcast_interval); + let (_, mut rx) = self + .channel + .take() + .expect("broadcast channel is expected to be set during initialization and must be available when running the broadcaster"); loop { select! { - msg = rx.recv() => match msg { - None => break, - Some(msg) => { - let fee = broadcaster.estimate_fee(vec![msg.clone()]).await.change_context(Error::EstimateFee)?; - - if fee.gas_limit.saturating_add(queue.gas_cost()) >= self.batch_gas_limit { - warn!(queue_size = queue.len(), queue_gas_cost = queue.gas_cost(), "exceeded batch gas limit. gas limit can be adjusted in ampd config"); - broadcast_all(&mut queue, &mut broadcaster).await?; - interval.reset(); - } - - let message_type = msg.type_url.clone(); - queue.push(msg, fee.gas_limit).change_context(Error::Queue)?; - info!( - message_type, - queue_size = queue.len(), - queue_gas_cost = queue.gas_cost(), - "pushed a new message into the queue" - ); - } - }, - _ = interval.tick() => { - broadcast_all(&mut queue, &mut broadcaster).await?; - interval.reset(); - }, - _ = self.broadcast_rx.recv() => { - broadcast_all(&mut queue, &mut broadcaster).await?; - interval.reset(); - }, + msg = rx.recv() => match msg { + None => break, + Some(msg_and_res_chan) => self.handle_msg(msg_and_res_chan).await?, + }, + _ = self.broadcast_interval.tick() => { + self.broadcast_all().await?; + self.broadcast_interval.reset(); + }, + Some(tx_res) = self.tx_confirmer_receiver.recv() => self.handle_tx_res(tx_res).await?, } } - broadcast_all(&mut queue, &mut broadcaster).await?; + self.clean_up().await + } + + async fn clean_up(mut self) -> Result { + self.broadcast_all().await?; + while let Some(tx_res) = self.tx_confirmer_receiver.recv().await { + self.handle_tx_res(tx_res).await?; + } Ok(()) } pub fn client(&self) -> QueuedBroadcasterClient { QueuedBroadcasterClient { - sender: self.channel.0.clone(), + sender: self + .channel + .as_ref() + .expect("broadcast channel is expected to be set during initialization and must be available when running the broadcaster") + .0 + .clone(), } } -} -async fn broadcast_all(queue: &mut MsgQueue, broadcaster: &mut T) -> Result -where - T: Broadcaster, -{ - let msgs = queue.pop_all(); + async fn handle_tx_res(&self, tx_res: TxResponse) -> Result { + let tx_hash = tx_res.response.txhash; + + match tx_res.status { + TxStatus::Success => { + tx_res.response.logs.iter().for_each(|log| { + let msg_index = log.msg_index; + + log.events + .iter() + .enumerate() + .for_each(|(event_index, event)| { + debug!(tx_hash, msg_index, event_index, "tx event {:?}", event); + }); + }); + } + TxStatus::Failure => { + warn!(tx_hash, "tx failed"); + } + } + + Ok(()) + } - match msgs.len() { - 0 => Ok(()), - n => { - info!(message_count = n, "ready to broadcast messages"); + async fn broadcast_all(&mut self) -> Result { + let msgs = self.queue.pop_all(); - broadcaster - .broadcast(msgs) - .await - .map(|_| ()) - .change_context(Error::Broadcast) + match msgs.len() { + 0 => Ok(()), + n => { + info!(message_count = n, "ready to broadcast messages"); + + let batch_req = proto::axelar::auxiliary::v1beta1::BatchRequest { + sender: self.broadcaster.sender_address().as_ref().to_bytes(), + messages: msgs, + } + .to_any() + .expect("failed to serialize proto message for batch request"); + + let tx_hash = self + .broadcaster + .broadcast(vec![batch_req]) + .await + .change_context(Error::Broadcast)? + .txhash; + self.tx_confirmer_sender + .send(tx_hash) + .await + .change_context(Error::TxConfirmation)?; + + Ok(()) + } + } + } + + async fn handle_msg(&mut self, msg_and_res_chan: MsgAndResChan) -> Result<()> { + let (msg, tx) = msg_and_res_chan; + + match self.broadcaster.estimate_fee(vec![msg.clone()]).await { + Ok(fee) => { + tx.send(Ok(())).map_err(|_| Report::new(Error::Client))?; + + if fee.gas_limit.saturating_add(self.queue.gas_cost()) >= self.batch_gas_limit { + warn!( + queue_size = self.queue.len(), + queue_gas_cost = self.queue.gas_cost(), + "exceeded batch gas limit. gas limit can be adjusted in ampd config" + ); + self.broadcast_all().await?; + self.broadcast_interval.reset(); + } + + let message_type = msg.type_url.clone(); + self.queue + .push(msg, fee.gas_limit) + .change_context(Error::Queue)?; + info!( + message_type, + queue_size = self.queue.len(), + queue_gas_cost = self.queue.gas_cost(), + "pushed a new message into the queue" + ); + } + Err(err) => { + tx.send(Err(err).change_context(Error::EstimateFee)) + .map_err(|_| Report::new(Error::Client))?; + } } + + Ok(()) } } #[cfg(test)] mod test { - use cosmos_sdk_proto::cosmos::base::abci::v1beta1::TxResponse; - use cosmrs::tx::Fee; - use cosmrs::Any; - use cosmrs::{bank::MsgSend, tx::Msg, AccountId}; + use cosmrs::bank::MsgSend; + use cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse; + use cosmrs::tx::{Fee, MessageExt, Msg}; + use cosmrs::{AccountId, Any}; + use error_stack::Report; + use futures::StreamExt; + use tokio::sync::mpsc; use tokio::test; - use tokio::time::{sleep, Duration}; + use tokio::time::{interval, timeout, Duration, Instant}; + use tokio_stream::wrappers::ReceiverStream; - use super::QueuedBroadcaster; - use crate::broadcaster::MockBroadcaster; + use super::{Error, QueuedBroadcaster}; + use crate::broadcaster::{self, MockBroadcaster}; + use crate::queue::proto; use crate::queue::queued_broadcaster::BroadcasterClient; + use crate::PREFIX; #[test] - async fn should_not_broadcast_when_gas_limit_has_not_been_reached() { - let tx_count = 9; - let batch_gas_limit = 100; - let gas_limit = 10; - + async fn should_ignore_msg_when_fee_estimation_fails() { let mut broadcaster = MockBroadcaster::new(); broadcaster .expect_estimate_fee() - .times(tx_count) - .returning(move |_| { - Ok(Fee { - gas_limit, - amount: vec![], - granter: None, - payer: None, - }) - }); - broadcaster - .expect_broadcast() - .once() - .returning(move |msgs| { - assert!(msgs.len() == tx_count); - - Ok(TxResponse::default()) - }); + .return_once(|_| Err(Report::new(broadcaster::Error::FeeEstimation))); - let (client, _driver) = QueuedBroadcaster::new( + let (tx_confirmer_sender, _tx_confirmer_receiver) = mpsc::channel(1000); + let (tx_res_sender, tx_res_receiver) = mpsc::channel(1000); + let broadcast_interval = interval(Duration::from_secs(5)); + let queued_broadcaster = QueuedBroadcaster::new( broadcaster, - batch_gas_limit, - tx_count, - Duration::from_secs(5), + 100, + 10, + broadcast_interval, + tx_confirmer_sender, + tx_res_receiver, ); + let client = queued_broadcaster.client(); + let handle = tokio::spawn(queued_broadcaster.run()); - let tx = client.client(); - for _ in 0..tx_count { - tx.broadcast(dummy_msg()).await.unwrap(); - } - drop(tx); - - assert!(client.run().await.is_ok()); + assert!(matches!( + client + .broadcast(dummy_msg()) + .await + .unwrap_err() + .current_context(), + Error::EstimateFee + )); + drop(client); + drop(tx_res_sender); + + assert!(handle.await.unwrap().is_ok()); } - #[test] - async fn should_broadcast_when_broadcast_interval_has_been_reached() { - let tx_count = 9; + #[test(start_paused = true)] + async fn should_broadcast_after_interval_in_low_load() { + let tx_count = 5; // Less than what would exceed batch_gas_limit let batch_gas_limit = 100; - let broadcast_interval = Duration::from_millis(100); let gas_limit = 10; + let interval_duration = Duration::from_secs(5); + + let (tx, mut rx) = mpsc::channel(5); let mut broadcaster = MockBroadcaster::new(); broadcaster @@ -247,36 +303,72 @@ mod test { payer: None, }) }); + broadcaster + .expect_sender_address() + .once() + .returning(|| AccountId::new(PREFIX, &[1, 2, 3]).unwrap().into()); broadcaster .expect_broadcast() .once() .returning(move |msgs| { - assert!(msgs.len() == tx_count); + assert_eq!(msgs.len(), 1); + let msg = msgs.first().unwrap(); + let msg = proto::axelar::auxiliary::v1beta1::BatchRequest::from_any(msg).unwrap(); + assert_eq!(msg.messages.len(), tx_count); + + tx.try_send(()) + .expect("Failed to send broadcast completion signal"); Ok(TxResponse::default()) }); - let (client, _driver) = - QueuedBroadcaster::new(broadcaster, batch_gas_limit, tx_count, broadcast_interval); - let tx = client.client(); + let (tx_confirmer_sender, tx_confirmer_receiver) = mpsc::channel(1000); + let (tx_res_sender, tx_res_receiver) = mpsc::channel(1000); + let mut broadcast_interval = interval(interval_duration); + broadcast_interval.tick().await; + let queued_broadcaster = QueuedBroadcaster::new( + broadcaster, + batch_gas_limit, + tx_count, + broadcast_interval, + tx_confirmer_sender, + tx_res_receiver, + ); + let client = queued_broadcaster.client(); + let handle = tokio::spawn(queued_broadcaster.run()); - let handler = tokio::spawn(async move { - assert!(client.run().await.is_ok()); - }); + let start_time = Instant::now(); for _ in 0..tx_count { - tx.broadcast(dummy_msg()).await.unwrap(); + client.broadcast(dummy_msg()).await.unwrap(); } - sleep(broadcast_interval).await; - handler.abort(); + // Advance time to just after one interval + tokio::time::advance(interval_duration + Duration::from_millis(10)).await; + + match timeout(interval_duration, rx.recv()).await { + Ok(_) => { + let elapsed = start_time.elapsed(); + assert!(elapsed > interval_duration); + assert!(elapsed < interval_duration * 2); + } + Err(_) => panic!("broadcast did not occur within the expected timeframe"), + } + + drop(client); + drop(tx_res_sender); + + assert!(handle.await.unwrap().is_ok()); + assert_eq!(ReceiverStream::new(tx_confirmer_receiver).count().await, 1); } - #[test] - async fn should_broadcast_when_gas_limit_has_been_reached() { - let tx_count = 10; + #[test(start_paused = true)] + async fn should_broadcast_full_batches_in_high_load() { + let tx_count = 20; + let batch_size = 10; let batch_gas_limit = 100; - let gas_limit = 11; + let gas_limit = 11; // This will cause a batch to be full after 9 messages + let interval_duration = Duration::from_secs(5); let mut broadcaster = MockBroadcaster::new(); broadcaster @@ -291,43 +383,68 @@ mod test { }) }); broadcaster - .expect_broadcast() - .once() - .returning(move |msgs| { - assert!(msgs.len() == tx_count - 1); - - Ok(TxResponse::default()) - }); + .expect_sender_address() + .times(3) + .returning(|| AccountId::new(PREFIX, &[1, 2, 3]).unwrap().into()); + let mut call_count = 0; broadcaster .expect_broadcast() - .once() + .times(3) .returning(move |msgs| { - assert!(msgs.len() == 1); + call_count += 1; + + assert_eq!(msgs.len(), 1); + let msg = msgs.first().unwrap(); + let msg = proto::axelar::auxiliary::v1beta1::BatchRequest::from_any(msg).unwrap(); + + if call_count < 3 { + assert_eq!(msg.messages.len(), 9); + } else { + assert_eq!(msg.messages.len(), 2); + } Ok(TxResponse::default()) }); - - let (client, _driver) = QueuedBroadcaster::new( + let (tx_confirmer_sender, tx_confirmer_receiver) = mpsc::channel(1000); + let (tx_res_sender, tx_res_receiver) = mpsc::channel(1000); + let mut broadcast_interval = interval(interval_duration); + // get rid of tick on startup + broadcast_interval.tick().await; + let queued_broadcaster = QueuedBroadcaster::new( broadcaster, batch_gas_limit, - tx_count, - Duration::from_secs(5), + batch_size, + broadcast_interval, + tx_confirmer_sender, + tx_res_receiver, ); + let client = queued_broadcaster.client(); + let handle = tokio::spawn(queued_broadcaster.run()); + + let start_time = Instant::now(); - let tx = client.client(); for _ in 0..tx_count { - tx.broadcast(dummy_msg()).await.unwrap(); + client.broadcast(dummy_msg()).await.unwrap(); } - drop(tx); + // Advance time by a small amount to allow processing + tokio::time::advance(Duration::from_millis(100)).await; + + let elapsed = start_time.elapsed(); + // Assert that broadcasts happened faster than the interval + assert!(elapsed < interval_duration); + + drop(client); + drop(tx_res_sender); - assert!(client.run().await.is_ok()); + assert_eq!(ReceiverStream::new(tx_confirmer_receiver).count().await, 3); + assert!(handle.await.unwrap().is_ok()); } - #[test] - async fn should_broadcast_when_forced_to() { + #[test(start_paused = true)] + async fn should_broadcast_when_gas_limit_has_been_reached() { let tx_count = 10; let batch_gas_limit = 100; - let gas_limit = 2; + let gas_limit = 11; let mut broadcaster = MockBroadcaster::new(); broadcaster @@ -341,35 +458,54 @@ mod test { payer: None, }) }); + broadcaster + .expect_sender_address() + .times(2) + .returning(|| AccountId::new(PREFIX, &[1, 2, 3]).unwrap().into()); + let mut broadcast_count = 0; broadcaster .expect_broadcast() - .once() + .times(2) .returning(move |msgs| { - assert!(msgs.len() == tx_count); + broadcast_count += 1; + + assert_eq!(msgs.len(), 1); + let msg = msgs.first().unwrap(); + let msg = proto::axelar::auxiliary::v1beta1::BatchRequest::from_any(msg).unwrap(); + + if broadcast_count == 1 { + assert_eq!(msg.messages.len(), tx_count - 1); + } else { + assert_eq!(msg.messages.len(), 1); + } Ok(TxResponse::default()) }); - let (client, driver) = QueuedBroadcaster::new( + let (tx_confirmer_sender, tx_confirmer_receiver) = mpsc::channel(1000); + let (tx_res_sender, tx_res_receiver) = mpsc::channel(1000); + let mut broadcast_interval = interval(Duration::from_secs(5)); + // get rid of tick on startup + broadcast_interval.tick().await; + let queued_broadcaster = QueuedBroadcaster::new( broadcaster, batch_gas_limit, tx_count, - Duration::from_secs(5), + broadcast_interval, + tx_confirmer_sender, + tx_res_receiver, ); + let client = queued_broadcaster.client(); + let handle = tokio::spawn(queued_broadcaster.run()); - let tx = client.client(); for _ in 0..tx_count { - tx.broadcast(dummy_msg()).await.unwrap(); + client.broadcast(dummy_msg()).await.unwrap(); } - let handler = tokio::spawn(async move { - assert!(client.run().await.is_ok()); - }); - - sleep(Duration::from_millis(100)).await; - driver.force_broadcast().await.unwrap(); - drop(tx); + drop(client); + drop(tx_res_sender); + assert_eq!(ReceiverStream::new(tx_confirmer_receiver).count().await, 2); - assert!(handler.await.is_ok()); + assert!(handle.await.unwrap().is_ok()); } fn dummy_msg() -> Any { diff --git a/ampd/src/state.rs b/ampd/src/state.rs deleted file mode 100644 index 74978e994..000000000 --- a/ampd/src/state.rs +++ /dev/null @@ -1,214 +0,0 @@ -use std::path::Path; -use std::{collections::HashMap, fs}; - -use error_stack::{Result, ResultExt}; -use serde::{Deserialize, Serialize}; -use tendermint::block; -use thiserror::Error; -use tokio::sync::mpsc::Receiver; -use tokio_stream::wrappers::ReceiverStream; -use tokio_stream::{StreamExt, StreamMap}; -use tracing::info; - -use crate::types::PublicKey; - -#[derive(Error, Debug)] -pub enum Error { - #[error("invalid state file")] - InvalidState, - #[error("failed to serialize the state")] - SerializationFailure, - #[error("failed to write the state file to disk")] - WriteFailure, -} - -#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq, Eq)] -pub struct State { - handlers: HashMap, - pub pub_key: Option, -} - -impl State { - pub fn min_handler_block_height(&self) -> Option<&block::Height> { - self.handlers.values().min() - } - - pub fn handler_block_height(&self, handler_name: &str) -> Option<&block::Height> { - self.handlers.get(handler_name) - } - - pub fn set_handler_block_height( - &mut self, - handler_name: impl Into, - height: impl Into, - ) { - self.handlers.insert(handler_name.into(), height.into()); - } -} - -pub struct StateUpdater { - update_stream: StreamMap>, - state: State, -} - -impl StateUpdater { - pub fn new(state: State) -> Self { - Self { - update_stream: StreamMap::new(), - state, - } - } - - pub fn state(&self) -> &State { - &self.state - } - - pub fn register_event( - &mut self, - label: impl Into, - height_changed: Receiver, - ) { - self.update_stream - .insert(label.into(), ReceiverStream::new(height_changed)); - } - - pub async fn run(mut self) -> State { - while let Some((handler, height)) = self.update_stream.next().await { - info!(handler, height = height.value(), "state updated"); - self.state.set_handler_block_height(handler, height); - } - - self.state - } -} - -impl AsMut for StateUpdater { - fn as_mut(&mut self) -> &mut State { - &mut self.state - } -} - -pub fn load(path: impl AsRef) -> Result { - info!("loading state from disk"); - - match fs::read_to_string(path) { - Ok(state) => serde_json::from_str(&state).change_context(Error::InvalidState), - Err(_) => { - info!("state file does not exist, starting from current blockchain state"); - Ok(State::default()) - } - } -} - -pub fn flush(state: &State, path: impl AsRef) -> Result<(), Error> { - info!("persisting state to disk"); - - let state = serde_json::to_string(state).change_context(Error::SerializationFailure)?; - ensure_parent_dirs_exist(&path)?; - - fs::write(&path, state) - .change_context(Error::WriteFailure) - .attach_printable(format!("{}", path.as_ref().display()))?; - - Ok(()) -} - -fn ensure_parent_dirs_exist(path: impl AsRef) -> Result<(), Error> { - match path.as_ref().parent() { - Some(parent) if !parent.exists() => fs::create_dir_all(parent) - .change_context(Error::WriteFailure) - .attach_printable(format!("{}", parent.display())), - _ => Ok(()), - } -} - -#[cfg(test)] -mod tests { - use std::panic::UnwindSafe; - use std::path::{Path, PathBuf}; - use std::{fs, panic}; - - use ecdsa::signature::rand_core::OsRng; - use tokio::sync::mpsc; - - use crate::state; - - use super::{State, StateUpdater}; - - fn run_test(state_path: impl AsRef, test: T) - where - T: FnOnce() + UnwindSafe, - { - let result = panic::catch_unwind(test); - let _ = fs::remove_file(&state_path); - let _ = state_path.as_ref().parent().map(fs::remove_dir); - assert!(result.is_ok()) - } - - #[allow(clippy::field_reassign_with_default)] // State has private fields, so using the object initializer is not possible - #[test] - fn can_load_and_flush_state() { - let path = PathBuf::from("./state_subfolder/can_load_and_flush_state.json"); - run_test(&path, || { - let mut state = State::default(); - state.pub_key = Some(ecdsa::SigningKey::random(&mut OsRng).verifying_key().into()); - state.set_handler_block_height("handler1", 10u16); - state.set_handler_block_height("handler2", 15u16); - state.set_handler_block_height("handler3", 7u16); - - state::flush(&state, &path).unwrap(); - let loaded_state = state::load(&path).unwrap(); - - assert_eq!(state, loaded_state); - }); - } - - #[tokio::test] - async fn can_update_state() { - let mut state_updater = StateUpdater::new(State::default()); - let state = state_updater.state(); - assert_eq!(state.handlers.len(), 0); - - let (tx1, rx1) = mpsc::channel(10); - let (tx2, rx2) = mpsc::channel(10); - state_updater.register_event("handler1", rx1); - state_updater.register_event("handler2", rx2); - - let pub_key = Some(ecdsa::SigningKey::random(&mut OsRng).verifying_key().into()); - state_updater.as_mut().pub_key = pub_key; - - let handle1 = tokio::spawn(async move { - tx1.send(1u16.into()).await.unwrap(); - tx1.send(2u16.into()).await.unwrap(); - tx1.send(3u16.into()).await.unwrap(); - }); - - let handle2 = tokio::spawn(async move { - tx2.send(1u16.into()).await.unwrap(); - tx2.send(2u16.into()).await.unwrap(); - tx2.send(3u16.into()).await.unwrap(); - tx2.send(4u16.into()).await.unwrap(); - }); - - let state_runner = state_updater.run(); - - assert!(handle1.await.is_ok()); - assert!(handle2.await.is_ok()); - - let modified_state = state_runner.await; - - let mut expected_state = State { - pub_key, - ..State::default() - }; - expected_state.set_handler_block_height("handler1", 3u16); - expected_state.set_handler_block_height("handler2", 4u16); - - assert_eq!(modified_state, expected_state); - - assert_eq!( - modified_state.min_handler_block_height(), - Some(&3u16.into()) - ); - } -} diff --git a/ampd/src/sui/json_rpc.rs b/ampd/src/sui/json_rpc.rs index fdae009c2..ca7897c91 100644 --- a/ampd/src/sui/json_rpc.rs +++ b/ampd/src/sui/json_rpc.rs @@ -1,7 +1,7 @@ use std::collections::{HashMap, HashSet}; use async_trait::async_trait; -use ethers::providers::{JsonRpcClient, ProviderError}; +use ethers_providers::{JsonRpcClient, ProviderError}; use mockall::automock; use sui_json_rpc_types::{SuiTransactionBlockResponse, SuiTransactionBlockResponseOptions}; use sui_types::digests::TransactionDigest; diff --git a/ampd/src/sui/verifier.rs b/ampd/src/sui/verifier.rs index 434ee7b5f..5b04dcd40 100644 --- a/ampd/src/sui/verifier.rs +++ b/ampd/src/sui/verifier.rs @@ -1,30 +1,18 @@ use axelar_wasm_std::voting::Vote; +use axelar_wasm_std::{self}; +use cosmwasm_std::HexBinary; use move_core_types::language_storage::StructTag; -use serde::Deserialize; +use sui_gateway::events::{ContractCall, SignersRotated}; +use sui_gateway::{WeightedSigner, WeightedSigners}; use sui_json_rpc_types::{SuiEvent, SuiTransactionBlockResponse}; use sui_types::base_types::SuiAddress; use crate::handlers::sui_verify_msg::Message; use crate::handlers::sui_verify_verifier_set::VerifierSetConfirmation; -use crate::types::Hash; - -#[derive(Deserialize)] -struct ContractCall { - pub source_id: SuiAddress, - pub destination_chain: String, - pub destination_address: String, - pub payload_hash: Hash, -} - -#[derive(Deserialize)] -struct OperatorshipTransferred { - #[allow(dead_code)] - pub payload: Vec, -} enum EventType { ContractCall, - OperatorshipTransferred, + SignersRotated, } impl EventType { @@ -32,12 +20,12 @@ impl EventType { fn struct_tag(&self, gateway_address: &SuiAddress) -> StructTag { let event = match self { EventType::ContractCall => "ContractCall", - EventType::OperatorshipTransferred => "OperatorshipTransferred", + EventType::SignersRotated => "SignersRotated", }; let module = match self { EventType::ContractCall => "gateway", - EventType::OperatorshipTransferred => "validators", + EventType::SignersRotated => "auth", }; format!("{}::{}::{}", gateway_address, module, event) @@ -48,12 +36,18 @@ impl EventType { impl PartialEq<&Message> for &SuiEvent { fn eq(&self, msg: &&Message) -> bool { - match serde_json::from_value::(self.parsed_json.clone()) { - Ok(contract_call) => { - contract_call.source_id == msg.source_address - && msg.destination_chain == contract_call.destination_chain - && contract_call.destination_address == msg.destination_address - && contract_call.payload_hash == msg.payload_hash + match bcs::from_bytes::(&self.bcs) { + Ok(ContractCall { + source_id, + destination_chain, + destination_address, + payload_hash, + .. + }) => { + msg.source_address.as_ref() == source_id.as_bytes() + && msg.destination_chain == destination_chain + && msg.destination_address == destination_address + && msg.payload_hash.to_fixed_bytes().to_vec() == payload_hash.to_vec() } _ => false, } @@ -61,11 +55,39 @@ impl PartialEq<&Message> for &SuiEvent { } impl PartialEq<&VerifierSetConfirmation> for &SuiEvent { - fn eq(&self, _verifier_set: &&VerifierSetConfirmation) -> bool { - match serde_json::from_value::(self.parsed_json.clone()) { - Ok(_event) => { - // TODO: convert verifier set to Sui gateway V2 WeightedSigners struct - todo!() + fn eq(&self, verifier_set: &&VerifierSetConfirmation) -> bool { + let expected = &verifier_set.verifier_set; + + let mut expected_signers = expected + .signers + .values() + .map(|signer| WeightedSigner { + pub_key: HexBinary::from(signer.pub_key.clone()).to_vec(), + weight: signer.weight.u128(), + }) + .collect::>(); + expected_signers.sort(); + + let expected_created_at = [0u8; 24] + .into_iter() + .chain(expected.created_at.to_be_bytes()) + .collect::>(); + + match bcs::from_bytes::(&self.bcs) { + Ok(SignersRotated { + signers: + WeightedSigners { + mut signers, + threshold, + nonce, + }, + .. + }) => { + signers.sort(); + + signers == expected_signers + && threshold == expected.threshold.u128() + && nonce.as_ref() == expected_created_at.as_slice() } _ => false, } @@ -109,8 +131,7 @@ pub fn verify_verifier_set( match find_event(transaction_block, confirmation.event_index as u64) { Some(event) if transaction_block.digest == confirmation.tx_id - && event.type_ - == EventType::OperatorshipTransferred.struct_tag(gateway_address) + && event.type_ == EventType::SignersRotated.struct_tag(gateway_address) && event == confirmation => { Vote::SucceededOnChain @@ -121,31 +142,33 @@ pub fn verify_verifier_set( #[cfg(test)] mod tests { - use std::collections::BTreeMap; - - use cosmwasm_std::Uint128; - use ethers::abi::AbiEncode; - use move_core_types::language_storage::StructTag; - use random_string::generate; - use sui_json_rpc_types::{SuiEvent, SuiTransactionBlockEvents, SuiTransactionBlockResponse}; - use sui_types::{ - base_types::{SuiAddress, TransactionDigest}, - event::EventID, - }; - use axelar_wasm_std::voting::Vote; + use cosmrs::crypto::PublicKey; + use cosmwasm_std::{Addr, HexBinary, Uint128}; + use ecdsa::SigningKey; + use move_core_types::language_storage::StructTag; + use multisig::key::KeyType; + use multisig::msg::Signer; use multisig::verifier_set::VerifierSet; + use rand::rngs::OsRng; + use random_string::generate; use router_api::ChainName; + use serde_json::json; + use sui_gateway::events::{ContractCall, SignersRotated}; + use sui_gateway::{WeightedSigner, WeightedSigners}; + use sui_json_rpc_types::{SuiEvent, SuiTransactionBlockEvents, SuiTransactionBlockResponse}; + use sui_types::base_types::{SuiAddress, TransactionDigest}; + use sui_types::event::EventID; - use crate::handlers::{ - sui_verify_msg::Message, sui_verify_verifier_set::VerifierSetConfirmation, - }; + use crate::handlers::sui_verify_msg::Message; + use crate::handlers::sui_verify_verifier_set::VerifierSetConfirmation; use crate::sui::verifier::{verify_message, verify_verifier_set}; use crate::types::{EVMAddress, Hash}; + use crate::PREFIX; #[test] fn should_not_verify_msg_if_tx_id_does_not_match() { - let (gateway_address, tx_receipt, mut msg) = get_matching_msg_and_tx_block(); + let (gateway_address, tx_receipt, mut msg) = matching_msg_and_tx_block(); msg.tx_id = TransactionDigest::random(); assert_eq!( @@ -156,7 +179,7 @@ mod tests { #[test] fn should_not_verify_msg_if_event_index_does_not_match() { - let (gateway_address, tx_receipt, mut msg) = get_matching_msg_and_tx_block(); + let (gateway_address, tx_receipt, mut msg) = matching_msg_and_tx_block(); msg.event_index = rand::random::(); assert_eq!( @@ -167,7 +190,7 @@ mod tests { #[test] fn should_not_verify_msg_if_source_address_does_not_match() { - let (gateway_address, tx_receipt, mut msg) = get_matching_msg_and_tx_block(); + let (gateway_address, tx_receipt, mut msg) = matching_msg_and_tx_block(); msg.source_address = SuiAddress::random_for_testing_only(); assert_eq!( @@ -178,7 +201,7 @@ mod tests { #[test] fn should_not_verify_msg_if_destination_chain_does_not_match() { - let (gateway_address, tx_receipt, mut msg) = get_matching_msg_and_tx_block(); + let (gateway_address, tx_receipt, mut msg) = matching_msg_and_tx_block(); msg.destination_chain = rand_chain_name(); assert_eq!( @@ -189,7 +212,7 @@ mod tests { #[test] fn should_not_verify_msg_if_destination_address_does_not_match() { - let (gateway_address, tx_receipt, mut msg) = get_matching_msg_and_tx_block(); + let (gateway_address, tx_receipt, mut msg) = matching_msg_and_tx_block(); msg.destination_address = EVMAddress::random().to_string(); assert_eq!( @@ -200,7 +223,7 @@ mod tests { #[test] fn should_not_verify_msg_if_payload_hash_does_not_match() { - let (gateway_address, tx_receipt, mut msg) = get_matching_msg_and_tx_block(); + let (gateway_address, tx_receipt, mut msg) = matching_msg_and_tx_block(); msg.payload_hash = Hash::random(); assert_eq!( @@ -211,25 +234,120 @@ mod tests { #[test] fn should_verify_msg_if_correct() { - let (gateway_address, tx_block, msg) = get_matching_msg_and_tx_block(); + let (gateway_address, tx_block, msg) = matching_msg_and_tx_block(); assert_eq!( verify_message(&gateway_address, &tx_block, &msg), Vote::SucceededOnChain ); } - #[ignore = "TODO: remove ignore once integrated with Sui gateway v2"] #[test] - fn should_verify_verifier_set() { - let (gateway_address, tx_receipt, verifier_set) = get_matching_verifier_set_and_tx_block(); + fn should_verify_verifier_set_if_correct() { + let (gateway_address, tx_block, verifier_set) = matching_verifier_set_and_tx_block(); assert_eq!( - verify_verifier_set(&gateway_address, &tx_receipt, &verifier_set), + verify_verifier_set(&gateway_address, &tx_block, &verifier_set), Vote::SucceededOnChain ); } - fn get_matching_msg_and_tx_block() -> (SuiAddress, SuiTransactionBlockResponse, Message) { + #[test] + fn should_not_verify_verifier_set_if_gateway_address_mismatch() { + let (_, tx_block, verifier_set) = matching_verifier_set_and_tx_block(); + + assert_eq!( + verify_verifier_set( + &SuiAddress::random_for_testing_only(), + &tx_block, + &verifier_set + ), + Vote::NotFound + ); + } + + #[test] + fn should_not_verify_verifier_set_if_tx_digest_mismatch() { + let (gateway_address, mut tx_block, verifier_set) = matching_verifier_set_and_tx_block(); + tx_block.digest = TransactionDigest::random(); + + assert_eq!( + verify_verifier_set(&gateway_address, &tx_block, &verifier_set), + Vote::NotFound + ); + } + + #[test] + fn should_not_verify_verifier_set_if_event_seq_mismatch() { + let (gateway_address, tx_block, mut verifier_set) = matching_verifier_set_and_tx_block(); + verifier_set.event_index = rand::random(); + + assert_eq!( + verify_verifier_set(&gateway_address, &tx_block, &verifier_set), + Vote::NotFound + ); + } + + #[test] + fn should_not_verify_verifier_set_if_struct_tag_mismatch() { + let (gateway_address, mut tx_block, verifier_set) = matching_verifier_set_and_tx_block(); + tx_block + .events + .as_mut() + .unwrap() + .data + .first_mut() + .unwrap() + .type_ = StructTag { + address: SuiAddress::random_for_testing_only().into(), + module: "module".parse().unwrap(), + name: "Name".parse().unwrap(), + type_params: vec![], + }; + + assert_eq!( + verify_verifier_set(&gateway_address, &tx_block, &verifier_set), + Vote::NotFound + ); + } + + #[test] + fn should_not_verify_verifier_set_if_threshold_mismatch() { + let (gateway_address, tx_block, mut verifier_set) = matching_verifier_set_and_tx_block(); + verifier_set.verifier_set.threshold = Uint128::new(2); + + assert_eq!( + verify_verifier_set(&gateway_address, &tx_block, &verifier_set), + Vote::NotFound + ); + } + + #[test] + fn should_not_verify_verifier_set_if_nonce_mismatch() { + let (gateway_address, tx_block, mut verifier_set) = matching_verifier_set_and_tx_block(); + verifier_set.verifier_set.created_at = rand::random(); + + assert_eq!( + verify_verifier_set(&gateway_address, &tx_block, &verifier_set), + Vote::NotFound + ); + } + + #[test] + fn should_not_verify_verifier_set_if_signers_mismatch() { + let (gateway_address, tx_block, mut verifier_set) = matching_verifier_set_and_tx_block(); + let signer = random_signer(); + verifier_set + .verifier_set + .signers + .insert(signer.address.to_string(), signer); + + assert_eq!( + verify_verifier_set(&gateway_address, &tx_block, &verifier_set), + Vote::NotFound + ); + } + + fn matching_msg_and_tx_block() -> (SuiAddress, SuiTransactionBlockResponse, Message) { let gateway_address = SuiAddress::random_for_testing_only(); let msg = Message { @@ -241,16 +359,13 @@ mod tests { payload_hash: Hash::random(), }; - let json_str = format!( - r#"{{"destination_address": "{}", "destination_chain": "{}", "payload": "[1,2,3]", - "payload_hash": "{}", "source_id": "{}"}}"#, - msg.destination_address, - msg.destination_chain, - msg.payload_hash.encode_hex(), - msg.source_address - ); - let parsed: serde_json::Value = serde_json::from_str(json_str.as_str()).unwrap(); - + let contract_call = ContractCall { + destination_address: msg.destination_address.clone(), + destination_chain: msg.destination_chain.to_string(), + payload: msg.payload_hash.to_fixed_bytes().to_vec(), + payload_hash: msg.payload_hash.to_fixed_bytes(), + source_id: msg.source_address.to_string().parse().unwrap(), + }; let event = SuiEvent { id: EventID { tx_digest: msg.tx_id, @@ -265,8 +380,8 @@ mod tests { name: "ContractCall".parse().unwrap(), type_params: vec![], }, - parsed_json: parsed, - bcs: vec![], + parsed_json: json!({}), + bcs: bcs::to_bytes(&contract_call).unwrap(), timestamp_ms: None, }; @@ -279,29 +394,65 @@ mod tests { (gateway_address, tx_block, msg) } - fn get_matching_verifier_set_and_tx_block() -> ( + fn random_signer() -> Signer { + let priv_key = SigningKey::random(&mut OsRng); + let pub_key: PublicKey = priv_key.verifying_key().into(); + let address = Addr::unchecked(pub_key.account_id(PREFIX).unwrap()); + let pub_key = (KeyType::Ecdsa, HexBinary::from(pub_key.to_bytes())) + .try_into() + .unwrap(); + + Signer { + address, + weight: Uint128::one(), + pub_key, + } + } + + fn matching_verifier_set_and_tx_block() -> ( SuiAddress, SuiTransactionBlockResponse, VerifierSetConfirmation, ) { let gateway_address = SuiAddress::random_for_testing_only(); - + let signers = vec![random_signer(), random_signer(), random_signer()]; + let created_at = rand::random(); + let threshold = Uint128::one(); let verifier_set_confirmation = VerifierSetConfirmation { tx_id: TransactionDigest::random(), - event_index: rand::random::(), + event_index: rand::random(), verifier_set: VerifierSet { - signers: BTreeMap::new(), - threshold: Uint128::one(), - created_at: 2, + signers: signers + .iter() + .map(|signer| (signer.address.to_string(), signer.clone())) + .collect(), + threshold, + created_at, }, }; - let json_str = format!( - r#"{{"epoch": "{}", "payload":[9,33,2,28,79,35,229,96,199,254,112,157,252,157,33,86,76,80,174,125,71,132,149,100,185,195,50,28,56,168,173,27,148,211,13,33,2,48,84,180,104,180,217,232,81,68,34,87,5,170,93,208,110,70,34,106,18,170,230,232,84,177,96,70,223,39,33,69,243,33,2,111,165,50,83,196,229,202,139,167,22,144,71,12,136,118,134,248,101,250,219,73,67,12,46,149,223,204,58,134,78,12,140,33,2,117,97,196,77,216,94,31,8,169,159,77,164,26,249,18,252,106,73,134,164,49,179,32,156,241,200,236,219,119,96,154,174,33,2,211,238,247,108,49,105,73,69,232,85,66,59,29,114,68,216,13,187,208,76,45,190,112,127,63,78,201,189,207,232,137,80,33,2,253,243,145,109,216,125,193,53,124,210,124,157,62,195,2,187,26,78,51,29,236,222,0,247,71,157,177,44,59,201,201,110,33,3,13,71,41,67,81,196,128,14,128,66,129,231,226,77,127,173,123,58,83,198,102,149,143,165,189,207,7,26,146,127,120,223,33,3,179,111,82,200,141,104,219,127,177,163,157,28,106,41,141,191,105,54,200,199,63,140,125,57,134,20,90,19,183,153,55,68,33,3,250,161,172,32,115,91,220,86,71,57,15,155,185,167,99,209,57,194,132,114,11,176,91,70,232,219,84,202,119,5,157,125,9,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}}"#, - rand::random::(), - ); - let parsed: serde_json::Value = serde_json::from_str(json_str.as_str()).unwrap(); - + let signers_rotated = SignersRotated { + epoch: 0, + signers_hash: [0; 32].into(), + signers: WeightedSigners { + signers: signers + .into_iter() + .map(|signer| WeightedSigner { + pub_key: HexBinary::from(signer.pub_key).to_vec(), + weight: signer.weight.u128(), + }) + .collect(), + threshold: threshold.u128(), + nonce: <[u8; 32]>::try_from( + [0u8; 24] + .into_iter() + .chain(created_at.to_be_bytes()) + .collect::>(), + ) + .unwrap() + .into(), + }, + }; let event = SuiEvent { id: EventID { tx_digest: verifier_set_confirmation.tx_id, @@ -312,12 +463,12 @@ mod tests { sender: SuiAddress::random_for_testing_only(), type_: StructTag { address: gateway_address.into(), - module: "validators".parse().unwrap(), - name: "OperatorshipTransferred".parse().unwrap(), + module: "auth".parse().unwrap(), + name: "SignersRotated".parse().unwrap(), type_params: vec![], }, - parsed_json: parsed, - bcs: vec![], + parsed_json: json!({}), + bcs: bcs::to_bytes(&signers_rotated).unwrap(), timestamp_ms: None, }; diff --git a/ampd/src/tests/config_template.toml b/ampd/src/tests/config_template.toml index df4cb492b..c50c2b107 100644 --- a/ampd/src/tests/config_template.toml +++ b/ampd/src/tests/config_template.toml @@ -1,8 +1,12 @@ health_check_bind_addr = '0.0.0.0:3000' tm_jsonrpc = 'http://localhost:26657/' tm_grpc = 'tcp://localhost:9090' -event_buffer_cap = 100000 -event_stream_timeout = '15s' + +[event_processor] +retry_delay = '1s' +retry_max_attempts = 3 +stream_timeout = '15s' +stream_buffer_size = 100000 [broadcast] chain_id = 'axelar-dojo-1' @@ -58,6 +62,16 @@ rpc_url = 'http://127.0.0.1/' secs = 3 nanos = 0 +[[handlers]] +type = 'MvxMsgVerifier' +cosmwasm_contract = 'axelar1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqecnww6' +proxy_url = 'http://127.0.0.1/' + +[[handlers]] +type = 'MvxVerifierSetVerifier' +cosmwasm_contract = 'axelar1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqecnww6' +proxy_url = 'http://127.0.0.1/' + [tofnd_config] url = 'http://localhost:50051/' party_uid = 'ampd' diff --git a/ampd/src/tm_client.rs b/ampd/src/tm_client.rs index 44f91b475..fddf7feca 100644 --- a/ampd/src/tm_client.rs +++ b/ampd/src/tm_client.rs @@ -1,9 +1,13 @@ +use std::time::Duration; + use async_trait::async_trait; use error_stack::{Report, Result}; use mockall::automock; use tendermint::block::Height; use tendermint_rpc::{Client, HttpClient}; +use crate::asyncutil::future::{self, RetryPolicy}; + pub type BlockResultsResponse = tendermint_rpc::endpoint::block_results::Response; pub type BlockResponse = tendermint_rpc::endpoint::block::Response; pub type Error = tendermint_rpc::Error; @@ -18,12 +22,26 @@ pub trait TmClient { #[async_trait] impl TmClient for HttpClient { async fn latest_block(&self) -> Result { - Client::latest_block(self).await.map_err(Report::from) + future::with_retry( + || Client::latest_block(self), + RetryPolicy::RepeatConstant { + sleep: Duration::from_secs(1), + max_attempts: 15, + }, + ) + .await + .map_err(Report::from) } async fn block_results(&self, height: Height) -> Result { - Client::block_results(self, height) - .await - .map_err(Report::from) + future::with_retry( + || Client::block_results(self, height), + RetryPolicy::RepeatConstant { + sleep: Duration::from_secs(1), + max_attempts: 15, + }, + ) + .await + .map_err(Report::from) } } diff --git a/ampd/src/tofnd/grpc.rs b/ampd/src/tofnd/grpc.rs index 2907f621b..91a805bfa 100644 --- a/ampd/src/tofnd/grpc.rs +++ b/ampd/src/tofnd/grpc.rs @@ -1,60 +1,68 @@ use std::sync::Arc; use async_trait::async_trait; -use ecdsa::VerifyingKey; -use error_stack::ResultExt; +use cosmrs::tendermint::public_key::PublicKey as TMPublicKey; +use error_stack::{Report, ResultExt}; use k256::Secp256k1; use mockall::automock; use tokio::sync::Mutex; -use tonic::{transport::Channel, Status}; +use tonic::transport::Channel; +use tonic::Status; -use crate::{types::PublicKey, url::Url}; - -use super::proto::{ - keygen_response::KeygenResponse, multisig_client, sign_response::SignResponse, KeygenRequest, - SignRequest, -}; -use super::{error::Error, error::TofndError, MessageDigest, Signature}; +use super::error::{Error, TofndError}; +use super::proto::keygen_response::KeygenResponse; +use super::proto::sign_response::SignResponse; +use super::proto::{multisig_client, Algorithm, KeygenRequest, SignRequest}; +use super::{MessageDigest, Signature}; +use crate::types::PublicKey; +use crate::url::Url; type Result = error_stack::Result; #[automock] #[async_trait] -pub trait EcdsaClient: Send { - async fn keygen(&mut self, key_uid: &str) -> Result; +pub trait Multisig { + async fn keygen(&self, key_uid: &str, algorithm: Algorithm) -> Result; async fn sign( - &mut self, + &self, key_uid: &str, data: MessageDigest, pub_key: &PublicKey, + algorithm: Algorithm, ) -> Result; } +#[derive(Clone)] pub struct MultisigClient { - client: multisig_client::MultisigClient, party_uid: String, + client: Arc>>, } impl MultisigClient { - pub async fn connect(party_uid: String, url: Url) -> Result { + pub async fn new(party_uid: String, url: Url) -> Result { Ok(Self { party_uid, - client: multisig_client::MultisigClient::connect(url.to_string()) - .await - .change_context(Error::Grpc)?, + client: Arc::new(Mutex::new( + multisig_client::MultisigClient::connect(url.to_string()) + .await + .change_context(Error::Grpc)?, + )), }) } } #[async_trait] -impl EcdsaClient for MultisigClient { - async fn keygen(&mut self, key_uid: &str) -> Result { +impl Multisig for MultisigClient { + async fn keygen(&self, key_uid: &str, algorithm: Algorithm) -> Result { let request = KeygenRequest { key_uid: key_uid.to_string(), party_uid: self.party_uid.to_string(), + algorithm: algorithm.into(), }; self.client + .lock() + .await .keygen(request) .await .and_then(|response| { @@ -65,12 +73,13 @@ impl EcdsaClient for MultisigClient { }) .change_context(Error::Grpc) .and_then(|response| match response { - KeygenResponse::PubKey(pub_key) => { - VerifyingKey::from_sec1_bytes(pub_key.as_slice()) - .change_context(Error::ParsingFailed) - .attach_printable(format!("{{ invalid_value = {:?} }}", pub_key)) - .map(Into::into) + KeygenResponse::PubKey(pub_key) => match algorithm { + Algorithm::Ecdsa => TMPublicKey::from_raw_secp256k1(&pub_key), + Algorithm::Ed25519 => TMPublicKey::from_raw_ed25519(&pub_key), } + .ok_or_else(|| Report::new(Error::ParsingFailed)) + .attach_printable(format!("{{ invalid_value = {:?} }}", pub_key)) + .map(Into::into), KeygenResponse::Error(error_msg) => { Err(TofndError::ExecutionFailed(error_msg)).change_context(Error::KeygenFailed) } @@ -78,19 +87,23 @@ impl EcdsaClient for MultisigClient { } async fn sign( - &mut self, + &self, key_uid: &str, data: MessageDigest, pub_key: &PublicKey, + algorithm: Algorithm, ) -> Result { let request = SignRequest { key_uid: key_uid.to_string(), msg_to_sign: data.into(), party_uid: self.party_uid.to_string(), pub_key: pub_key.to_bytes(), + algorithm: algorithm.into(), }; self.client + .lock() + .await .sign(request) .await .and_then(|response| { @@ -101,177 +114,18 @@ impl EcdsaClient for MultisigClient { }) .change_context(Error::Grpc) .and_then(|response| match response { - SignResponse::Signature(signature) => { - ecdsa::Signature::::from_der(&signature) - .change_context(Error::ParsingFailed) + SignResponse::Signature(signature) => match algorithm { + Algorithm::Ecdsa => ecdsa::Signature::::from_der(&signature) .map(|sig| sig.to_vec()) - .map(Into::into) - } + .change_context(Error::ParsingFailed), + Algorithm::Ed25519 => ed25519::Signature::from_slice(&signature) + .map(|sig| sig.to_vec()) + .change_context(Error::ParsingFailed), + }, + SignResponse::Error(error_msg) => { Err(TofndError::ExecutionFailed(error_msg)).change_context(Error::SignFailed) } }) } } - -#[derive(Clone)] -pub struct SharableEcdsaClient(Arc>); - -impl SharableEcdsaClient { - pub fn new(client: impl EcdsaClient + 'static) -> Self { - Self(Arc::new(Mutex::new(client))) - } - - pub async fn keygen(&self, key_uid: &str) -> Result { - self.0.lock().await.keygen(key_uid).await - } - - pub async fn sign( - &self, - key_uid: &str, - data: MessageDigest, - pub_key: &PublicKey, - ) -> Result { - self.0.lock().await.sign(key_uid, data, pub_key).await - } -} - -#[cfg(test)] -mod tests { - use ecdsa::SigningKey; - use error_stack::Report; - use futures::future::join_all; - use k256::Secp256k1; - use rand::rngs::OsRng; - use rand::{thread_rng, RngCore}; - use tokio::test; - - use crate::tofnd::{ - error::Error, - grpc::{MockEcdsaClient, SharableEcdsaClient}, - MessageDigest, Signature, - }; - - const KEY_UID: &str = "key_1"; - - #[test] - async fn keygen_error_response() { - let mut client = MockEcdsaClient::new(); - client - .expect_keygen() - .returning(|_| Err(Report::from(Error::KeygenFailed))); - - assert!(matches!( - SharableEcdsaClient::new(client) - .keygen(KEY_UID) - .await - .unwrap_err() - .current_context(), - Error::KeygenFailed - )); - } - - #[test] - async fn keygen_succeeded() { - let rand_pub_key = SigningKey::random(&mut OsRng).verifying_key().into(); - - let mut client = MockEcdsaClient::new(); - client.expect_keygen().returning(move |_| Ok(rand_pub_key)); - - assert_eq!( - SharableEcdsaClient::new(client) - .keygen(KEY_UID) - .await - .unwrap(), - rand_pub_key - ); - } - - #[test] - async fn sign_error_response() { - let mut client = MockEcdsaClient::new(); - client - .expect_sign() - .returning(move |_, _, _| Err(Report::from(Error::SignFailed))); - - let digest: MessageDigest = rand::random::<[u8; 32]>().into(); - assert!(matches!( - SharableEcdsaClient::new(client) - .sign( - KEY_UID, - digest, - &SigningKey::random(&mut OsRng).verifying_key().into() - ) - .await - .unwrap_err() - .current_context(), - Error::SignFailed - )); - } - - #[test] - async fn sign_succeeded() { - let mut hash = [0u8; 64]; - thread_rng().fill_bytes(&mut hash); - - let priv_key = SigningKey::::random(&mut OsRng); - let (signature, _) = priv_key.sign_prehash_recoverable(hash.as_ref()).unwrap(); - - let mut client = MockEcdsaClient::new(); - client - .expect_sign() - .returning(move |_, _, _| Ok(signature.to_vec())); - - let digest: MessageDigest = rand::random::<[u8; 32]>().into(); - - assert_eq!( - SharableEcdsaClient::new(client) - .sign( - KEY_UID, - digest, - &SigningKey::random(&mut OsRng).verifying_key().into(), - ) - .await - .unwrap(), - Signature::from(signature.to_vec()), - ); - } - - #[test] - async fn share_across_threads() { - let mut hash = [0u8; 64]; - thread_rng().fill_bytes(&mut hash); - - let priv_key = SigningKey::::random(&mut OsRng); - let (signature, _) = priv_key.sign_prehash_recoverable(&hash).unwrap(); - - let mut client = MockEcdsaClient::new(); - client - .expect_sign() - .returning(move |_, _, _| Ok(signature.to_vec())); - - let client = SharableEcdsaClient::new(client); - - let mut handles = Vec::new(); - for _ in 0..5 { - let cloned = client.clone(); - let handle = tokio::spawn(async move { - let digest: MessageDigest = rand::random::<[u8; 32]>().into(); - assert_eq!( - cloned - .sign( - KEY_UID, - digest, - &SigningKey::random(&mut OsRng).verifying_key().into() - ) - .await - .unwrap(), - Signature::from(signature.to_vec()), - ) - }); - handles.push(handle); - } - - join_all(handles).await; - } -} diff --git a/ampd/src/tofnd/mod.rs b/ampd/src/tofnd/mod.rs index 110cc152e..3ae656b22 100644 --- a/ampd/src/tofnd/mod.rs +++ b/ampd/src/tofnd/mod.rs @@ -11,6 +11,8 @@ mod proto { tonic::include_proto!("tofnd"); } +pub use proto::Algorithm; + #[derive(Debug, Deserialize, Serialize, PartialEq, Clone)] pub struct Config { pub url: Url, @@ -31,7 +33,7 @@ impl Default for Config { // Signature is an alias for signature in raw bytes pub type Signature = Vec; -#[derive(Clone, Debug, Deserialize)] +#[derive(Clone, Debug, Deserialize, PartialEq)] pub struct MessageDigest([u8; 32]); impl FromHex for MessageDigest { diff --git a/ampd/src/types.rs b/ampd/src/types.rs index cde390033..5d21e2312 100644 --- a/ampd/src/types.rs +++ b/ampd/src/types.rs @@ -1,9 +1,8 @@ use std::fmt; use std::hash::{Hash as StdHash, Hasher}; -use cosmrs::crypto; -use cosmrs::AccountId; -use ethers::types::{Address, H256}; +use cosmrs::{crypto, AccountId}; +use ethers_core::types::{Address, H256}; use serde::{Deserialize, Serialize}; pub type EVMAddress = Address; diff --git a/ampd/src/url.rs b/ampd/src/url.rs index 654c99062..90a1fdc10 100644 --- a/ampd/src/url.rs +++ b/ampd/src/url.rs @@ -1,8 +1,9 @@ +use std::fmt::{Display, Formatter}; +use std::str::FromStr; + use deref_derive::Deref; use serde::de::{Error, Visitor}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::fmt::{Display, Formatter}; -use std::str::FromStr; use url::ParseError; #[derive(Debug, Deref, Hash, PartialEq, Eq, Clone)] diff --git a/codecov.yml b/codecov.yml index d81edca72..c4525eb0b 100644 --- a/codecov.yml +++ b/codecov.yml @@ -4,3 +4,7 @@ coverage: default: target: auto threshold: 0.25% + patch: + default: + target: auto + threshold: 1% diff --git a/contracts/gateway/.cargo/config b/contracts/axelarnet-gateway/.cargo/config.toml similarity index 100% rename from contracts/gateway/.cargo/config rename to contracts/axelarnet-gateway/.cargo/config.toml diff --git a/contracts/axelarnet-gateway/Cargo.toml b/contracts/axelarnet-gateway/Cargo.toml new file mode 100644 index 000000000..4c5499714 --- /dev/null +++ b/contracts/axelarnet-gateway/Cargo.toml @@ -0,0 +1,54 @@ +[package] +name = "axelarnet-gateway" +version = "0.1.0" +rust-version = { workspace = true } +edition = { workspace = true } +description = "The Axelarnet Gateway contract allows apps on the Axelar Network to send/receive cross-chain messages to/from other chains." + +exclude = [ + # Those files are rust-optimizer artifacts. You might want to commit them for convenience, but they should not be part of the source code publication. + "contract.wasm", + "hash.txt", +] + +[lib] +crate-type = ["cdylib", "rlib"] + +[[bin]] +name = "axelarnet-gateway-schema" +path = "src/bin/schema.rs" + +[features] +# for more explicit tests, cargo test --features=backtraces +backtraces = ["cosmwasm-std/backtraces"] +# use library feature to disable all instantiate/execute/query exports +library = [] + +[package.metadata.scripts] +optimize = """docker run --rm -v "$(pwd)":/code \ + --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ + --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ + cosmwasm/optimizer:0.16.0 +""" + +[dependencies] +axelar-wasm-std = { workspace = true, features = ["derive"] } +client = { workspace = true } +cosmwasm-schema = { workspace = true } +cosmwasm-std = { workspace = true } +cw-storage-plus = { workspace = true } +cw2 = { workspace = true } +error-stack = { workspace = true } +msgs-derive = { workspace = true } +report = { workspace = true } +router-api = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +sha3 = { workspace = true } +thiserror = { workspace = true } + +[dev-dependencies] +cw-multi-test = "0.15.1" + +[lints] +workspace = true diff --git a/contracts/axelarnet-gateway/src/bin/schema.rs b/contracts/axelarnet-gateway/src/bin/schema.rs new file mode 100644 index 000000000..5a060e62b --- /dev/null +++ b/contracts/axelarnet-gateway/src/bin/schema.rs @@ -0,0 +1,10 @@ +use axelarnet_gateway::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use cosmwasm_schema::write_api; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + execute: ExecuteMsg, + query: QueryMsg, + } +} diff --git a/contracts/axelarnet-gateway/src/client.rs b/contracts/axelarnet-gateway/src/client.rs new file mode 100644 index 000000000..29ea04f8e --- /dev/null +++ b/contracts/axelarnet-gateway/src/client.rs @@ -0,0 +1,159 @@ +use axelar_wasm_std::vec::VecExt; +use cosmwasm_std::{Addr, HexBinary, WasmMsg}; +use error_stack::{Result, ResultExt}; +use router_api::{Address, ChainName, CrossChainId, Message}; + +use crate::msg::{ExecuteMsg, QueryMsg}; + +#[derive(thiserror::Error, Debug, PartialEq)] +pub enum Error { + #[error("failed to query the chain name at gateway contract {0}")] + QueryChainName(Addr), +} + +impl<'a> From> for Client<'a> { + fn from(client: client::Client<'a, ExecuteMsg, QueryMsg>) -> Self { + Client { client } + } +} + +pub struct Client<'a> { + client: client::Client<'a, ExecuteMsg, QueryMsg>, +} + +impl<'a> Client<'a> { + pub fn call_contract( + &self, + destination_chain: ChainName, + destination_address: Address, + payload: HexBinary, + ) -> WasmMsg { + self.client.execute(&ExecuteMsg::CallContract { + destination_chain, + destination_address, + payload, + }) + } + + pub fn execute(&self, cc_id: CrossChainId, payload: HexBinary) -> WasmMsg { + self.client.execute(&ExecuteMsg::Execute { cc_id, payload }) + } + + pub fn route_messages(&self, msgs: Vec) -> Option { + msgs.to_none_if_empty() + .map(|messages| self.client.execute(&ExecuteMsg::RouteMessages(messages))) + } + + pub fn chain_name(&self) -> Result { + self.client + .query(&QueryMsg::ChainName) + .change_context_lazy(|| Error::QueryChainName(self.client.address.clone())) + } +} + +#[cfg(test)] +mod test { + use std::str::FromStr; + + use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockQuerier}; + use cosmwasm_std::{from_json, to_json_binary, Addr, DepsMut, QuerierWrapper, WasmQuery}; + + use super::*; + use crate::contract::{instantiate, query}; + use crate::msg::InstantiateMsg; + + #[test] + fn chain_name() { + let (querier, _, addr) = setup(); + let client: Client = + client::Client::new(QuerierWrapper::new(&querier), addr.clone()).into(); + + assert_eq!( + client.chain_name().unwrap(), + ChainName::from_str("source-chain").unwrap() + ); + } + + #[test] + fn call_contract() { + let (querier, _, addr) = setup(); + let client: Client = + client::Client::new(QuerierWrapper::new(&querier), addr.clone()).into(); + + let destination_chain: ChainName = "destination-chain".parse().unwrap(); + let destination_address: Address = "destination-address".parse().unwrap(); + let payload = HexBinary::from(vec![1, 2, 3]); + + let msg = client.call_contract( + destination_chain.clone(), + destination_address.clone(), + payload.clone(), + ); + + assert_eq!( + msg, + WasmMsg::Execute { + contract_addr: addr.to_string(), + msg: to_json_binary(&ExecuteMsg::CallContract { + destination_chain, + destination_address, + payload, + }) + .unwrap(), + funds: vec![], + } + ); + } + + #[test] + fn execute_message() { + let (querier, _, addr) = setup(); + let client: Client = + client::Client::new(QuerierWrapper::new(&querier), addr.clone()).into(); + + let payload = HexBinary::from(vec![1, 2, 3]); + let cc_id = CrossChainId::new("source-chain", "message-id").unwrap(); + + let msg = client.execute(cc_id.clone(), payload.clone()); + + assert_eq!( + msg, + WasmMsg::Execute { + contract_addr: addr.to_string(), + msg: to_json_binary(&ExecuteMsg::Execute { cc_id, payload }).unwrap(), + funds: vec![], + } + ); + } + + fn setup() -> (MockQuerier, InstantiateMsg, Addr) { + let addr = "axelarnet-gateway"; + let mut deps = mock_dependencies(); + let instantiate_msg = instantiate_contract(deps.as_mut()); + + let mut querier = MockQuerier::default(); + querier.update_wasm(move |msg| match msg { + WasmQuery::Smart { contract_addr, msg } if contract_addr == addr => { + let msg = from_json::(msg).unwrap(); + Ok(query(deps.as_ref(), mock_env(), msg).into()).into() + } + _ => panic!("unexpected query: {:?}", msg), + }); + + (querier, instantiate_msg, Addr::unchecked(addr)) + } + + fn instantiate_contract(deps: DepsMut) -> InstantiateMsg { + let env = mock_env(); + let info = mock_info("deployer", &[]); + + let msg = InstantiateMsg { + chain_name: "source-chain".parse().unwrap(), + router_address: "router".to_string(), + }; + + instantiate(deps, env, info, msg.clone()).unwrap(); + + msg + } +} diff --git a/contracts/axelarnet-gateway/src/contract.rs b/contracts/axelarnet-gateway/src/contract.rs new file mode 100644 index 000000000..a0655f142 --- /dev/null +++ b/contracts/axelarnet-gateway/src/contract.rs @@ -0,0 +1,160 @@ +use axelar_wasm_std::{address, FnExt}; +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; +use cosmwasm_std::{to_json_binary, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response}; +use error_stack::ResultExt; +use router_api::client::Router; +use router_api::CrossChainId; + +use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use crate::state::{self, Config}; + +mod execute; +mod query; + +const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); +const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("contract config is missing")] + ConfigMissing, + #[error("invalid store access")] + InvalidStoreAccess, + #[error("failed to serialize the response")] + SerializeResponse, + #[error("failed to serialize wasm message")] + SerializeWasmMsg, + #[error("invalid address {0}")] + InvalidAddress(String), + #[error("invalid message id")] + InvalidMessageId, + #[error("failed to save outgoing message")] + SaveOutgoingMessage, + #[error("message with ID {0} not found")] + MessageNotFound(CrossChainId), + #[error("message with ID {0} is different")] + MessageMismatch(CrossChainId), + #[error("message with ID {0} not in approved status")] + MessageNotApproved(CrossChainId), + #[error("failed to set message with ID {0} as executed")] + SetMessageStatusExecutedFailed(CrossChainId), + #[error("payload hash doesn't match message")] + PayloadHashMismatch, + #[error("failed to route messages")] + RoutingFailed, +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate( + deps: DepsMut, + _env: Env, + _msg: Empty, +) -> Result { + // any version checks should be done before here + + cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + + Ok(Response::default()) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + deps: DepsMut, + _env: Env, + _info: MessageInfo, + msg: InstantiateMsg, +) -> Result { + cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + + let router = address::validate_cosmwasm_address(deps.api, &msg.router_address)?; + + let config = Config { + chain_name: msg.chain_name, + router, + }; + + state::save_config(deps.storage, &config).change_context(Error::InvalidStoreAccess)?; + + Ok(Response::new()) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> Result { + let msg = msg.ensure_permissions(deps.storage, &info.sender)?; + + let config = state::load_config(deps.storage).change_context(Error::ConfigMissing)?; + + let router = Router { + address: config.router, + }; + let chain_name = config.chain_name; + + match msg { + ExecuteMsg::CallContract { + destination_chain, + destination_address, + payload, + } => execute::call_contract( + deps.storage, + env.block.height, + &router, + chain_name, + info.sender, + destination_chain, + destination_address, + payload, + ), + ExecuteMsg::RouteMessages(msgs) => { + if info.sender == router.address { + execute::receive_messages(deps.storage, chain_name, msgs) + } else { + // Messages initiated via call contract can be routed again + execute::send_messages(deps.storage, &router, msgs) + } + } + ExecuteMsg::Execute { cc_id, payload } => { + execute::execute(deps.storage, deps.api, deps.querier, cc_id, payload) + } + }? + .then(Ok) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query( + deps: Deps, + _env: Env, + msg: QueryMsg, +) -> Result { + match msg { + QueryMsg::SentMessages { cc_ids } => to_json_binary(&query::sent_messages(deps, cc_ids)?), + QueryMsg::ReceivedMessages { cc_ids } => { + to_json_binary(&query::received_messages(deps, cc_ids)?) + } + QueryMsg::ChainName => to_json_binary(&state::load_config(deps.storage)?.chain_name), + } + .map_err(axelar_wasm_std::error::ContractError::from) +} + +#[cfg(test)] +mod tests { + use cosmwasm_std::testing::{mock_dependencies, mock_env}; + + use super::*; + + #[test] + fn migrate_sets_contract_version() { + let mut deps = mock_dependencies(); + + migrate(deps.as_mut(), mock_env(), Empty {}).unwrap(); + + let contract_version = cw2::get_contract_version(deps.as_mut().storage).unwrap(); + assert_eq!(contract_version.contract, "axelarnet-gateway"); + assert_eq!(contract_version.version, CONTRACT_VERSION); + } +} diff --git a/contracts/axelarnet-gateway/src/contract/execute.rs b/contracts/axelarnet-gateway/src/contract/execute.rs new file mode 100644 index 000000000..dca3271ce --- /dev/null +++ b/contracts/axelarnet-gateway/src/contract/execute.rs @@ -0,0 +1,375 @@ +use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; +use cosmwasm_std::{ + Addr, Api, Event, HexBinary, QuerierWrapper, Response, Storage, Uint256, WasmMsg, +}; +use error_stack::{report, Result, ResultExt}; +use router_api::client::Router; +use router_api::{Address, ChainName, CrossChainId, Message}; +use sha3::{Digest, Keccak256}; + +use crate::contract::Error; +use crate::events::AxelarnetGatewayEvent; +use crate::executable::AxelarExecutableClient; +use crate::state::{self}; + +#[allow(clippy::too_many_arguments)] +pub fn call_contract( + store: &mut dyn Storage, + block_height: u64, + router: &Router, + chain_name: ChainName, + sender: Addr, + destination_chain: ChainName, + destination_address: Address, + payload: HexBinary, +) -> Result { + let counter = state::increment_msg_counter(store).change_context(Error::InvalidStoreAccess)?; + + // TODO: Retrieve the actual tx hash from core, since cosmwasm doesn't provide it. Use the block height as the placeholder in the meantime. + let message_id = HexTxHashAndEventIndex { + tx_hash: Uint256::from(block_height).to_be_bytes(), + event_index: counter, + } + .into(); + + let cc_id = CrossChainId { + source_chain: chain_name.into(), + message_id, + }; + + let payload_hash = Keccak256::digest(payload.as_slice()).into(); + + let msg = Message { + cc_id: cc_id.clone(), + source_address: Address::try_from(sender.into_string()) + .expect("failed to convert sender address"), + destination_chain, + destination_address, + payload_hash, + }; + + state::save_sent_msg(store, cc_id, &msg).change_context(Error::InvalidStoreAccess)?; + + let (wasm_msg, events) = route(router, vec![msg.clone()])?; + + Ok(Response::new() + .add_message(wasm_msg) + .add_event(AxelarnetGatewayEvent::ContractCalled { msg, payload }.into()) + .add_events(events)) +} + +// Because the messages came from the router, we can assume they are already verified +pub fn receive_messages( + store: &mut dyn Storage, + chain_name: ChainName, + msgs: Vec, +) -> Result { + for msg in msgs.iter() { + if chain_name != msg.destination_chain { + panic!("message destination chain should match chain name in the gateway") + } + + state::save_received_msg(store, msg.cc_id.clone(), msg.clone()) + .change_context(Error::SaveOutgoingMessage)?; + } + + Ok(Response::new().add_events( + msgs.into_iter() + .map(|msg| AxelarnetGatewayEvent::Routing { msg }.into()), + )) +} + +pub fn send_messages( + store: &mut dyn Storage, + router: &Router, + msgs: Vec, +) -> Result { + for msg in msgs.iter() { + let stored_msg = state::may_load_sent_msg(store, &msg.cc_id) + .change_context(Error::InvalidStoreAccess)?; + + match stored_msg { + Some(message) if msg != &message => { + Err(report!(Error::MessageMismatch(msg.cc_id.clone()))) + } + Some(_) => Ok(()), + None => Err(report!(Error::MessageNotFound(msg.cc_id.clone()))), + }? + } + + let (wasm_msg, events) = route(router, msgs)?; + + Ok(Response::new().add_message(wasm_msg).add_events(events)) +} + +fn route( + router: &Router, + msgs: Vec, +) -> Result<(WasmMsg, impl IntoIterator), Error> { + Ok(( + router.route(msgs.clone()).ok_or(Error::RoutingFailed)?, + msgs.into_iter() + .map(|msg| AxelarnetGatewayEvent::Routing { msg }.into()), + )) +} + +pub fn execute( + store: &mut dyn Storage, + api: &dyn Api, + querier: QuerierWrapper, + cc_id: CrossChainId, + payload: HexBinary, +) -> Result { + let msg = state::set_msg_as_executed(store, cc_id.clone()) + .change_context(Error::SetMessageStatusExecutedFailed(cc_id))?; + + let payload_hash: [u8; 32] = Keccak256::digest(payload.as_slice()).into(); + if payload_hash != msg.payload_hash { + return Err(report!(Error::PayloadHashMismatch)); + } + + let destination_contract = api + .addr_validate(&msg.destination_address) + .change_context(Error::InvalidAddress(msg.destination_address.to_string()))?; + + let executable: AxelarExecutableClient = + client::Client::new(querier, destination_contract).into(); + + // Call the destination contract + // Apps are required to expose AxelarExecutableMsg::Execute interface + Ok(Response::new() + .add_message(executable.execute(msg.cc_id.clone(), msg.source_address.clone(), payload)) + .add_event(AxelarnetGatewayEvent::MessageExecuted { msg }.into())) +} + +#[cfg(test)] +mod tests { + use axelar_wasm_std::err_contains; + use cosmwasm_std::testing::{ + mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage, + }; + use cosmwasm_std::{Addr, CosmosMsg, Empty, Env, MessageInfo, OwnedDeps}; + use router_api::{ChainName, CrossChainId, Message}; + + use super::*; + use crate::contract::{execute, instantiate}; + use crate::msg::{ExecuteMsg, InstantiateMsg}; + use crate::state::{self, MessageStatus}; + + const CHAIN: &str = "chain"; + const SOURCE_CHAIN: &str = "source-chain"; + const ROUTER: &str = "router"; + const PAYLOAD: [u8; 3] = [1, 2, 3]; + const SENDER: &str = "sender"; + + fn setup() -> ( + OwnedDeps, + Env, + MessageInfo, + ) { + let mut deps = mock_dependencies(); + let env = mock_env(); + let info = mock_info(SENDER, &[]); + + let chain_name: ChainName = CHAIN.parse().unwrap(); + let router = Addr::unchecked(ROUTER); + + let msg = InstantiateMsg { + chain_name: chain_name.clone(), + router_address: router.to_string(), + }; + + let _res = instantiate(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); + + (deps, env, info) + } + + fn dummy_message() -> Message { + Message { + cc_id: CrossChainId::new(SOURCE_CHAIN, "message-id").unwrap(), + source_address: "source-address".parse().unwrap(), + destination_chain: CHAIN.parse().unwrap(), + destination_address: "destination-address".parse().unwrap(), + payload_hash: Keccak256::digest(PAYLOAD).into(), + } + } + + #[test] + fn call_contract_and_send_message() { + let (mut deps, env, info) = setup(); + + let expected_message_id = HexTxHashAndEventIndex { + tx_hash: Uint256::from(env.block.height).to_be_bytes(), + event_index: 1, + }; + let expected_cc_id = CrossChainId::new(CHAIN, expected_message_id).unwrap(); + let message = Message { + cc_id: expected_cc_id.clone(), + source_address: info.sender.clone().into_string().parse().unwrap(), + destination_chain: "destination-chain".parse().unwrap(), + destination_address: "destination-address".parse().unwrap(), + payload_hash: Keccak256::digest(PAYLOAD).into(), + }; + + let msg = ExecuteMsg::CallContract { + destination_chain: message.destination_chain.clone(), + destination_address: message.destination_address.clone(), + payload: PAYLOAD.into(), + }; + + let res = execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); + let sent_message = state::may_load_sent_msg(deps.as_mut().storage, &expected_cc_id) + .unwrap() + .unwrap(); + assert_eq!(sent_message, message); + + let router: Router = Router { + address: Addr::unchecked(ROUTER), + }; + assert_eq!(res.messages.len(), 1); + assert_eq!( + res.messages[0].msg, + CosmosMsg::Wasm(router.route(vec![message.clone()]).unwrap()) + ); + + // Re-route the message again + let msg = ExecuteMsg::RouteMessages(vec![message.clone()]); + let res = execute(deps.as_mut(), env, info, msg).unwrap(); + assert_eq!(res.messages.len(), 1); + assert_eq!( + res.messages[0].msg, + CosmosMsg::Wasm(router.route(vec![message]).unwrap()) + ); + } + + #[test] + fn route_messages_from_router() { + let (mut deps, env, _) = setup(); + + let message = dummy_message(); + let msg = ExecuteMsg::RouteMessages(vec![message.clone()]); + + // Execute RouteMessages as if it's coming from the router + let info = mock_info(ROUTER, &[]); + execute(deps.as_mut(), env, info, msg).unwrap(); + + // Check that the message was saved as received + let received_message = state::may_load_received_msg(deps.as_mut().storage, &message.cc_id) + .unwrap() + .unwrap(); + assert_eq!(received_message.msg, message); + assert!(matches!(received_message.status, MessageStatus::Approved)); + } + + #[test] + fn execute_message() { + let (mut deps, env, info) = setup(); + + let message = dummy_message(); + let cc_id = message.cc_id.clone(); + + // Save the message as received + state::save_received_msg(deps.as_mut().storage, cc_id.clone(), message).unwrap(); + + let msg = ExecuteMsg::Execute { + cc_id: cc_id.clone(), + payload: PAYLOAD.into(), + }; + + let res = execute(deps.as_mut(), env, info, msg).unwrap(); + + // Check that a message was sent to the destination contract + assert_eq!(res.messages.len(), 1); + + // Check that the message status was updated to Executed + let executed_message = state::may_load_received_msg(deps.as_mut().storage, &cc_id) + .unwrap() + .unwrap(); + assert!(matches!(executed_message.status, MessageStatus::Executed)); + } + + #[test] + fn execute_not_found() { + let (mut deps, env, info) = setup(); + + let cc_id = CrossChainId::new(SOURCE_CHAIN, "message-id").unwrap(); + let msg = ExecuteMsg::Execute { + cc_id: cc_id.clone(), + payload: PAYLOAD.into(), + }; + + let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert!(err_contains!( + err.report, + state::Error, + state::Error::MessageNotApproved(..) + )); + assert!(err_contains!( + err.report, + Error, + Error::SetMessageStatusExecutedFailed(..) + )); + } + + #[test] + fn execute_already_executed() { + let (mut deps, env, info) = setup(); + + let message = dummy_message(); + let cc_id = message.cc_id.clone(); + + // Save the message as already executed + state::save_received_msg(deps.as_mut().storage, cc_id.clone(), message).unwrap(); + state::set_msg_as_executed(deps.as_mut().storage, cc_id.clone()).unwrap(); + + let msg = ExecuteMsg::Execute { + cc_id, + payload: PAYLOAD.into(), + }; + + let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert!(err_contains!( + err.report, + state::Error, + state::Error::MessageAlreadyExecuted(..) + )); + assert!(err_contains!( + err.report, + Error, + Error::SetMessageStatusExecutedFailed(..) + )); + } + + #[test] + fn execute_payload_mismatch() { + let (mut deps, env, info) = setup(); + + let message = dummy_message(); + let cc_id = message.cc_id.clone(); + + state::save_received_msg(deps.as_mut().storage, cc_id.clone(), message).unwrap(); + + let msg = ExecuteMsg::Execute { + cc_id, + payload: [4, 5, 6].into(), + }; + + let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert!(err_contains!(err.report, Error, Error::PayloadHashMismatch)); + } + + #[test] + #[should_panic(expected = "should match chain name")] + fn receive_messages_wrong_chain() { + let (mut deps, _, _) = setup(); + + let mut message = dummy_message(); + message.destination_chain = "wrong-chain".parse().unwrap(); + + let msg = ExecuteMsg::RouteMessages(vec![message]); + let info = mock_info(ROUTER, &[]); + + // This should panic because the destination chain doesn't match the gateway's chain name + execute(deps.as_mut(), mock_env(), info, msg).unwrap(); + } +} diff --git a/contracts/axelarnet-gateway/src/contract/query.rs b/contracts/axelarnet-gateway/src/contract/query.rs new file mode 100644 index 000000000..8476227ae --- /dev/null +++ b/contracts/axelarnet-gateway/src/contract/query.rs @@ -0,0 +1,165 @@ +use cosmwasm_std::Deps; +use router_api::{CrossChainId, Message}; + +use crate::state::{self, MessageWithStatus}; + +pub fn sent_messages(deps: Deps, cc_ids: Vec) -> Result, state::Error> { + cc_ids + .into_iter() + .map(|cc_id| { + state::may_load_sent_msg(deps.storage, &cc_id)? + .ok_or(state::Error::MessageNotFound(cc_id)) + }) + .collect::, _>>() +} + +pub fn received_messages( + deps: Deps, + cc_ids: Vec, +) -> Result, state::Error> { + cc_ids + .into_iter() + .map(|cc_id| { + state::may_load_received_msg(deps.storage, &cc_id)? + .ok_or(state::Error::MessageNotFound(cc_id)) + }) + .collect::, _>>() +} + +#[cfg(test)] +mod tests { + use axelar_wasm_std::{err_contains, FnExt}; + use cosmwasm_std::from_json; + use cosmwasm_std::testing::{mock_dependencies, mock_env}; + use router_api::{CrossChainId, Message}; + use serde::de::DeserializeOwned; + use state::MessageStatus; + + use super::*; + use crate::contract; + use crate::msg::QueryMsg; + + const SOURCE_CHAIN: &str = "source-chain"; + const DESTINATION_CHAIN: &str = "destination-chain"; + + fn dummy_message(id: &str) -> Message { + Message { + cc_id: CrossChainId::new(SOURCE_CHAIN, id).unwrap(), + source_address: "source-address".parse().unwrap(), + destination_chain: DESTINATION_CHAIN.parse().unwrap(), + destination_address: "destination-address".parse().unwrap(), + payload_hash: [0; 32], + } + } + + // Query a msg and deserialize it. If the query fails, the error is returned + fn query( + deps: Deps, + msg: QueryMsg, + ) -> Result { + contract::query(deps, mock_env(), msg)? + .then(from_json::) + .unwrap() + .then(Ok) + } + + #[test] + fn query_sent_messages() { + let mut deps = mock_dependencies(); + + let message1 = dummy_message("message-1"); + let message2 = dummy_message("message-2"); + let message3 = dummy_message("message-3"); + + // Save messages + state::save_sent_msg(deps.as_mut().storage, message1.cc_id.clone(), &message1).unwrap(); + state::save_sent_msg(deps.as_mut().storage, message2.cc_id.clone(), &message2).unwrap(); + + // Query existing messages + let result: Vec = query( + deps.as_ref(), + QueryMsg::SentMessages { + cc_ids: vec![message1.cc_id.clone(), message2.cc_id.clone()], + }, + ) + .unwrap(); + assert_eq!(result, vec![message1, message2]); + + // Query with non-existent message + let err = query::>( + deps.as_ref(), + QueryMsg::SentMessages { + cc_ids: vec![message3.cc_id], + }, + ) + .unwrap_err(); + assert!(err_contains!( + err.report, + state::Error, + state::Error::MessageNotFound(..) + )); + } + + #[test] + fn query_received_messages() { + let mut deps = mock_dependencies(); + + let message1 = dummy_message("message-1"); + let message2 = dummy_message("message-2"); + let message3 = dummy_message("message-3"); + + // Save messages + state::save_received_msg( + deps.as_mut().storage, + message1.cc_id.clone(), + message1.clone(), + ) + .unwrap(); + state::save_received_msg( + deps.as_mut().storage, + message2.cc_id.clone(), + message2.clone(), + ) + .unwrap(); + + // Set message2 as executed + state::set_msg_as_executed(deps.as_mut().storage, message2.cc_id.clone()).unwrap(); + + // Query existing messages + let result: Vec = query( + deps.as_ref(), + QueryMsg::ReceivedMessages { + cc_ids: vec![message1.cc_id.clone(), message2.cc_id.clone()], + }, + ) + .unwrap(); + + assert_eq!( + result, + vec![ + MessageWithStatus { + msg: message1, + status: MessageStatus::Approved + }, + MessageWithStatus { + msg: message2, + status: MessageStatus::Executed + } + ] + ); + + // Query with non-existent message + let err = query::>( + deps.as_ref(), + QueryMsg::ReceivedMessages { + cc_ids: vec![message3.cc_id], + }, + ) + .unwrap_err(); + assert!(err_contains!( + err.report, + state::Error, + state::Error::MessageNotFound(..) + )); + } +} diff --git a/contracts/axelarnet-gateway/src/events.rs b/contracts/axelarnet-gateway/src/events.rs new file mode 100644 index 000000000..c62479999 --- /dev/null +++ b/contracts/axelarnet-gateway/src/events.rs @@ -0,0 +1,37 @@ +use cosmwasm_std::{Attribute, Event, HexBinary}; +use router_api::Message; + +pub enum AxelarnetGatewayEvent { + ContractCalled { + msg: Message, + payload: HexBinary, + }, + /// Uses the same event name as `GatewayEvent` for consistency + Routing { + msg: Message, + }, + MessageExecuted { + msg: Message, + }, +} + +impl From for Event { + fn from(other: AxelarnetGatewayEvent) -> Self { + match other { + AxelarnetGatewayEvent::ContractCalled { msg, payload } => { + make_message_event("contract_called", msg) + .add_attributes(vec![("payload", payload.to_string())]) + } + AxelarnetGatewayEvent::Routing { msg } => make_message_event("routing", msg), + AxelarnetGatewayEvent::MessageExecuted { msg } => { + make_message_event("message_executed", msg) + } + } + } +} + +fn make_message_event(event_name: &str, msg: Message) -> Event { + let attrs: Vec = msg.into(); + + Event::new(event_name).add_attributes(attrs) +} diff --git a/contracts/axelarnet-gateway/src/executable.rs b/contracts/axelarnet-gateway/src/executable.rs new file mode 100644 index 000000000..7d35f4162 --- /dev/null +++ b/contracts/axelarnet-gateway/src/executable.rs @@ -0,0 +1,88 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{HexBinary, WasmMsg}; +use router_api::{Address, CrossChainId}; + +/// `AxelarExecutableMsg` is a struct containing the args used by the axelarnet gateway to execute a destination contract on Axelar. +/// Each App needs to expose a `ExecuteMsg::Execute(AxelarExecutableMsg)` variant that only the gateway is allowed to call. +#[cw_serde] +pub struct AxelarExecutableMsg { + pub cc_id: CrossChainId, + pub source_address: Address, + pub payload: HexBinary, +} + +/// Crate-specific `ExecuteMsg` type wraps the `AxelarExecutableMsg` for the AxelarExecutable client. +#[cw_serde] +pub enum AxelarExecutableExecuteMsg { + /// Execute the message at the destination contract with the corresponding payload. + Execute(AxelarExecutableMsg), +} + +impl<'a> From> for AxelarExecutableClient<'a> { + fn from(client: client::Client<'a, AxelarExecutableExecuteMsg, ()>) -> Self { + AxelarExecutableClient { client } + } +} + +pub struct AxelarExecutableClient<'a> { + client: client::Client<'a, AxelarExecutableExecuteMsg, ()>, +} + +impl<'a> AxelarExecutableClient<'a> { + pub fn execute( + &self, + cc_id: CrossChainId, + source_address: Address, + payload: HexBinary, + ) -> WasmMsg { + self.client + .execute(&AxelarExecutableExecuteMsg::Execute(AxelarExecutableMsg { + cc_id, + source_address, + payload, + })) + } +} + +#[cfg(test)] +mod test { + use cosmwasm_std::testing::MockQuerier; + use cosmwasm_std::{to_json_binary, Addr, QuerierWrapper}; + + use super::*; + + #[test] + fn execute_message() { + let (querier, addr) = setup(); + let client: AxelarExecutableClient = + client::Client::new(QuerierWrapper::new(&querier), addr.clone()).into(); + + let cc_id = CrossChainId::new("source-chain", "message-id").unwrap(); + let source_address: Address = "source-address".parse().unwrap(); + let payload = HexBinary::from(vec![1, 2, 3]); + + let msg = client.execute(cc_id.clone(), source_address.clone(), payload.clone()); + + assert_eq!( + msg, + WasmMsg::Execute { + contract_addr: addr.to_string(), + msg: to_json_binary(&AxelarExecutableExecuteMsg::Execute(AxelarExecutableMsg { + cc_id, + source_address, + payload, + })) + .unwrap(), + funds: vec![], + } + ); + } + + fn setup() -> (MockQuerier, Addr) { + let addr = Addr::unchecked("axelar-executable"); + + let querier = MockQuerier::default(); + + (querier, addr) + } +} diff --git a/contracts/axelarnet-gateway/src/lib.rs b/contracts/axelarnet-gateway/src/lib.rs new file mode 100644 index 000000000..c089ff750 --- /dev/null +++ b/contracts/axelarnet-gateway/src/lib.rs @@ -0,0 +1,10 @@ +pub mod contract; +pub mod events; +pub mod msg; +mod state; + +mod client; +pub use client::{Client, Error}; + +mod executable; +pub use executable::AxelarExecutableMsg; diff --git a/contracts/axelarnet-gateway/src/msg.rs b/contracts/axelarnet-gateway/src/msg.rs new file mode 100644 index 000000000..f5a55820f --- /dev/null +++ b/contracts/axelarnet-gateway/src/msg.rs @@ -0,0 +1,57 @@ +use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::HexBinary; +use msgs_derive::EnsurePermissions; +use router_api::{Address, ChainName, CrossChainId, Message}; + +use crate::state::MessageWithStatus; + +#[cw_serde] +pub struct InstantiateMsg { + /// The chain name for this gateway. + pub chain_name: ChainName, + /// Address of the router contract on axelar. + pub router_address: String, +} + +#[cw_serde] +#[derive(EnsurePermissions)] +pub enum ExecuteMsg { + /// Initiate a cross-chain contract call from Axelarnet to another chain. + /// The message will be routed to the destination chain's gateway via the router. + #[permission(Any)] + CallContract { + destination_chain: ChainName, + destination_address: Address, + payload: HexBinary, + }, + + /// Forward the given messages to the next step of the routing layer. + /// Messages initiated via `CallContract` can be forwarded again to the router. + /// If the messages are coming from the router, then they are marked ready for execution. + #[permission(Any)] + RouteMessages(Vec), + + /// Execute the message at the destination contract with the corresponding payload. + /// The message is marked as executed and thus can't be executed again. + #[permission(Any)] + Execute { + cc_id: CrossChainId, + payload: HexBinary, + }, +} + +#[cw_serde] +#[derive(QueryResponses)] +pub enum QueryMsg { + /// Returns the sent messages for the given cross-chain ids. + #[returns(Vec)] + SentMessages { cc_ids: Vec }, + + /// Returns the received messages with their status for the given cross-chain ids. + #[returns(Vec)] + ReceivedMessages { cc_ids: Vec }, + + /// Returns the chain name for this gateway. + #[returns(ChainName)] + ChainName, +} diff --git a/contracts/axelarnet-gateway/src/state.rs b/contracts/axelarnet-gateway/src/state.rs new file mode 100644 index 000000000..aaec95d3b --- /dev/null +++ b/contracts/axelarnet-gateway/src/state.rs @@ -0,0 +1,338 @@ +use axelar_wasm_std::counter::Counter; +use axelar_wasm_std::{FnExt, IntoContractError}; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, StdError, Storage}; +use cw_storage_plus::{Item, Map}; +use router_api::{ChainName, CrossChainId, Message}; + +#[cw_serde] +pub struct Config { + pub chain_name: ChainName, + pub router: Addr, +} + +#[cw_serde] +pub enum MessageStatus { + Approved, + Executed, +} + +#[cw_serde] +pub struct MessageWithStatus { + pub msg: Message, + pub status: MessageStatus, +} + +const CONFIG_NAME: &str = "config"; +const CONFIG: Item = Item::new(CONFIG_NAME); + +const SENT_MESSAGE_COUNTER_NAME: &str = "sent_message_counter"; +const SENT_MESSAGE_COUNTER: Counter = Counter::new(SENT_MESSAGE_COUNTER_NAME); + +const SENT_MESSAGES_NAME: &str = "sent_messages"; +const SENT_MESSAGES: Map = Map::new(SENT_MESSAGES_NAME); + +const RECEIVED_MESSAGES_NAME: &str = "received_messages"; +const RECEIVED_MESSAGES: Map = Map::new(RECEIVED_MESSAGES_NAME); + +#[derive(thiserror::Error, Debug, PartialEq, IntoContractError)] +pub enum Error { + #[error(transparent)] + Std(#[from] StdError), + #[error("gateway got into an invalid state, its config is missing")] + MissingConfig, + #[error("message with ID {0} mismatches with the stored one")] + MessageMismatch(CrossChainId), + #[error("message with ID {0} not found")] + MessageNotFound(CrossChainId), + #[error("message with ID {0} not approved")] + MessageNotApproved(CrossChainId), + #[error("message with ID {0} already executed")] + MessageAlreadyExecuted(CrossChainId), + #[error("sent message with ID {0} already exists")] + MessageAlreadyExists(CrossChainId), +} + +pub fn save_config(storage: &mut dyn Storage, value: &Config) -> Result<(), Error> { + CONFIG.save(storage, value).map_err(Error::from) +} + +pub fn load_config(storage: &dyn Storage) -> Result { + CONFIG + .may_load(storage) + .map_err(Error::from)? + .ok_or(Error::MissingConfig) +} + +pub fn save_sent_msg( + storage: &mut dyn Storage, + key: CrossChainId, + msg: &Message, +) -> Result<(), Error> { + match SENT_MESSAGES.may_load(storage, key.clone())? { + Some(_) => Err(Error::MessageAlreadyExists(key)), + None => SENT_MESSAGES.save(storage, key, msg).map_err(Error::from), + } +} + +pub fn may_load_sent_msg( + storage: &dyn Storage, + id: &CrossChainId, +) -> Result, Error> { + SENT_MESSAGES + .may_load(storage, id.clone()) + .map_err(Error::from) +} + +pub fn may_load_received_msg( + storage: &dyn Storage, + cc_id: &CrossChainId, +) -> Result, Error> { + RECEIVED_MESSAGES + .may_load(storage, cc_id.clone()) + .map_err(Error::from) +} + +pub fn save_received_msg( + storage: &mut dyn Storage, + cc_id: CrossChainId, + msg: Message, +) -> Result<(), Error> { + let existing = RECEIVED_MESSAGES + .may_load(storage, cc_id.clone()) + .map_err(Error::from)?; + + match existing { + Some(MessageWithStatus { + msg: existing_msg, .. + }) if msg != existing_msg => Err(Error::MessageMismatch(msg.cc_id.clone())), + Some(_) => Ok(()), // new message is identical, no need to store it + None => RECEIVED_MESSAGES + .save( + storage, + cc_id, + &MessageWithStatus { + msg, + status: MessageStatus::Approved, + }, + ) + .map_err(Error::from)? + .then(Ok), + } +} + +/// Update the status of a message to executed if it is in approved status, error otherwise. +pub fn set_msg_as_executed( + storage: &mut dyn Storage, + cc_id: CrossChainId, +) -> Result { + let existing = RECEIVED_MESSAGES + .may_load(storage, cc_id.clone()) + .map_err(Error::from)?; + + match existing { + Some(MessageWithStatus { + msg, + status: MessageStatus::Approved, + }) => { + RECEIVED_MESSAGES + .save( + storage, + cc_id, + &MessageWithStatus { + msg: msg.clone(), + status: MessageStatus::Executed, + }, + ) + .map_err(Error::from)?; + + Ok(msg) + } + Some(MessageWithStatus { + status: MessageStatus::Executed, + .. + }) => Err(Error::MessageAlreadyExecuted(cc_id)), + _ => Err(Error::MessageNotApproved(cc_id)), + } +} + +pub fn increment_msg_counter(storage: &mut dyn Storage) -> Result { + SENT_MESSAGE_COUNTER.incr(storage).map_err(Error::from) +} + +#[cfg(test)] +mod tests { + use cosmwasm_std::testing::mock_dependencies; + use cosmwasm_std::Addr; + use router_api::{CrossChainId, Message}; + + use super::*; + + fn create_test_message() -> Message { + Message { + cc_id: CrossChainId::new("source-chain", "message-id").unwrap(), + source_address: "source-address".parse().unwrap(), + destination_chain: "destination-chain".parse().unwrap(), + destination_address: "destination-address".parse().unwrap(), + payload_hash: [1; 32], + } + } + + #[test] + fn config_storage() { + let mut deps = mock_dependencies(); + + let config = Config { + chain_name: "test-chain".parse().unwrap(), + router: Addr::unchecked("router-address"), + }; + + // Test saving config + super::save_config(deps.as_mut().storage, &config).unwrap(); + + // Test loading config + let loaded_config = super::load_config(deps.as_ref().storage).unwrap(); + assert_eq!(config, loaded_config); + + // Test loading non-existent config + CONFIG.remove(deps.as_mut().storage); + let result = super::load_config(deps.as_ref().storage); + assert_eq!(result, Err(Error::MissingConfig)); + } + + #[test] + fn sent_message_storage() { + let mut deps = mock_dependencies(); + let message = create_test_message(); + + // Test saving sent message + super::save_sent_msg(deps.as_mut().storage, message.cc_id.clone(), &message).unwrap(); + + // Test loading sent message + let loaded_message = + super::may_load_sent_msg(deps.as_ref().storage, &message.cc_id).unwrap(); + assert_eq!(Some(message.clone()), loaded_message); + + // Test loading non-existent message + let non_existent_id = CrossChainId::new("non-existent", "id").unwrap(); + assert_eq!( + None, + super::may_load_sent_msg(deps.as_ref().storage, &non_existent_id).unwrap() + ); + + // Test saving duplicate message + let result = super::save_sent_msg(deps.as_mut().storage, message.cc_id.clone(), &message); + assert_eq!(result, Err(Error::MessageAlreadyExists(message.cc_id))); + } + + #[test] + fn received_message_storage() { + let mut deps = mock_dependencies(); + let message = create_test_message(); + + // Test saving received message + super::save_received_msg( + deps.as_mut().storage, + message.cc_id.clone(), + message.clone(), + ) + .unwrap(); + + // Test loading received message + let loaded_message = + super::may_load_received_msg(deps.as_ref().storage, &message.cc_id).unwrap(); + assert_eq!( + Some(MessageWithStatus { + msg: message.clone(), + status: MessageStatus::Approved + }), + loaded_message + ); + + // Test loading non-existent message + let non_existent_id = CrossChainId::new("non-existent", "id").unwrap(); + assert_eq!( + None, + super::may_load_received_msg(deps.as_ref().storage, &non_existent_id).unwrap() + ); + + // Test saving duplicate message (should not error, but also not change the stored message) + super::save_received_msg( + deps.as_mut().storage, + message.cc_id.clone(), + message.clone(), + ) + .unwrap(); + let loaded_message = + super::may_load_received_msg(deps.as_ref().storage, &message.cc_id).unwrap(); + assert_eq!( + Some(MessageWithStatus { + msg: message.clone(), + status: MessageStatus::Approved + }), + loaded_message + ); + + // Test saving mismatched message + let mismatched_message = Message { + cc_id: message.cc_id.clone(), + source_address: "different-address".parse().unwrap(), + ..message.clone() + }; + let result = super::save_received_msg( + deps.as_mut().storage, + message.cc_id.clone(), + mismatched_message, + ); + assert_eq!(result, Err(Error::MessageMismatch(message.cc_id))); + } + + #[test] + fn set_msg_as_executed() { + let mut deps = mock_dependencies(); + let message = create_test_message(); + + // Save a received message + super::save_received_msg( + deps.as_mut().storage, + message.cc_id.clone(), + message.clone(), + ) + .unwrap(); + + // Test setting message as executed + let executed_message = + super::set_msg_as_executed(deps.as_mut().storage, message.cc_id.clone()).unwrap(); + assert_eq!(message, executed_message); + + // Verify the message status is now Executed + let loaded_message = + super::may_load_received_msg(deps.as_ref().storage, &message.cc_id).unwrap(); + assert_eq!( + Some(MessageWithStatus { + msg: message.clone(), + status: MessageStatus::Executed + }), + loaded_message + ); + + // Test setting an already executed message + let result = super::set_msg_as_executed(deps.as_mut().storage, message.cc_id.clone()); + assert_eq!(result, Err(Error::MessageAlreadyExecuted(message.cc_id))); + + // Test setting a non-existent message + let non_existent_id = CrossChainId::new("non-existent", "id").unwrap(); + let result = super::set_msg_as_executed(deps.as_mut().storage, non_existent_id.clone()); + assert_eq!(result, Err(Error::MessageNotApproved(non_existent_id))); + } + + #[test] + fn increment_msg_counter() { + let mut deps = mock_dependencies(); + + for i in 1..=3 { + let count = super::increment_msg_counter(deps.as_mut().storage).unwrap(); + assert_eq!(i, count); + } + } +} diff --git a/contracts/multisig-prover/.cargo/config b/contracts/coordinator/.cargo/config.toml similarity index 100% rename from contracts/multisig-prover/.cargo/config rename to contracts/coordinator/.cargo/config.toml diff --git a/contracts/coordinator/Cargo.toml b/contracts/coordinator/Cargo.toml index b96990fed..bac6a740f 100644 --- a/contracts/coordinator/Cargo.toml +++ b/contracts/coordinator/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "coordinator" -version = "0.1.2" +version = "1.0.0" rust-version = { workspace = true } -edition = "2021" +edition = { workspace = true } description = "Amplifier info aggregation for external use, alongside contract management, instantiation and migration" exclude = [ @@ -29,17 +29,17 @@ library = [] optimize = """docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/rust-optimizer:0.12.6 + cosmwasm/optimizer:0.16.0 """ [dependencies] -axelar-wasm-std = { workspace = true } -axelar-wasm-std-derive = { workspace = true } +axelar-wasm-std = { workspace = true, features = ["derive"] } cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } cw-storage-plus = { workspace = true } cw2 = { workspace = true } error-stack = { workspace = true } +msgs-derive = { workspace = true } multisig = { workspace = true, features = ["library"] } report = { workspace = true } router-api = { workspace = true } @@ -48,7 +48,7 @@ thiserror = { workspace = true } [dev-dependencies] cw-multi-test = "0.15.1" integration-tests = { workspace = true } -tofn = { git = "https://github.com/axelarnetwork/tofn.git", branch = "update-deps" } +tofn = { workspace = true } [lints] workspace = true diff --git a/contracts/coordinator/src/bin/schema.rs b/contracts/coordinator/src/bin/schema.rs index e026cca8c..c99447b20 100644 --- a/contracts/coordinator/src/bin/schema.rs +++ b/contracts/coordinator/src/bin/schema.rs @@ -1,6 +1,5 @@ -use cosmwasm_schema::write_api; - use coordinator::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use cosmwasm_schema::write_api; fn main() { write_api! { diff --git a/contracts/coordinator/src/contract.rs b/contracts/coordinator/src/contract.rs index 18a4d084e..99f994074 100644 --- a/contracts/coordinator/src/contract.rs +++ b/contracts/coordinator/src/contract.rs @@ -1,23 +1,33 @@ -use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; -use crate::state::{Config, CONFIG}; -use cosmwasm_std::{entry_point, Empty}; -use cosmwasm_std::{to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response}; - +mod execute; +mod migrations; +mod query; + +use axelar_wasm_std::{address, permission_control, FnExt}; +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; +use cosmwasm_std::{ + to_json_binary, Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response, Storage, +}; +use error_stack::report; + +use crate::contract::migrations::v0_2_0; use crate::error::ContractError; -use crate::execute; -use crate::query; +use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use crate::state::is_prover_registered; -const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); -const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); +pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); +pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); #[cfg_attr(not(feature = "library"), entry_point)] pub fn migrate( deps: DepsMut, _env: Env, _msg: Empty, -) -> Result { - // any version checks should be done before here +) -> Result { + v0_2_0::migrate(deps.storage)?; + // this needs to be the last thing to do during migration, + // because previous migration steps should check the old version cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; Ok(Response::default()) @@ -29,15 +39,12 @@ pub fn instantiate( _env: Env, _info: MessageInfo, msg: InstantiateMsg, -) -> Result { +) -> Result { cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - CONFIG.save( - deps.storage, - &Config { - governance: deps.api.addr_validate(&msg.governance_address)?, - }, - )?; + let governance = address::validate_cosmwasm_address(deps.api, &msg.governance_address)?; + permission_control::set_governance(deps.storage, &governance)?; + Ok(Response::default()) } @@ -47,48 +54,59 @@ pub fn execute( _env: Env, info: MessageInfo, msg: ExecuteMsg, -) -> Result { - match msg { +) -> Result { + match msg.ensure_permissions( + deps.storage, + &info.sender, + find_prover_address(&info.sender), + )? { ExecuteMsg::RegisterProverContract { chain_name, new_prover_addr, - } => { - execute::check_governance(&deps, info)?; - execute::register_prover(deps, chain_name, new_prover_addr) + } => execute::register_prover(deps, chain_name, new_prover_addr), + ExecuteMsg::SetActiveVerifiers { verifiers } => { + execute::set_active_verifier_set(deps, info, verifiers) } - ExecuteMsg::SetActiveVerifiers { next_verifier_set } => { - execute::set_active_verifier_set(deps, info, next_verifier_set) + }? + .then(Ok) +} + +fn find_prover_address( + sender: &Addr, +) -> impl FnOnce(&dyn Storage, &ExecuteMsg) -> error_stack::Result + '_ { + |storage, _| { + if is_prover_registered(storage, sender.clone())? { + Ok(sender.clone()) + } else { + Err(report!(ContractError::ProverNotRegistered)) } } - .map_err(axelar_wasm_std::ContractError::from) } #[cfg_attr(not(feature = "library"), entry_point)] -#[allow(dead_code)] pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result { match msg { - QueryMsg::GetActiveVerifiers { chain_name } => { - to_json_binary(&query::get_active_verifier_set(deps, chain_name)?) - .map_err(|err| err.into()) - } + QueryMsg::ReadyToUnbond { worker_address } => to_json_binary( + &query::check_verifier_ready_to_unbond(deps, worker_address)?, + )?, } + .then(Ok) } #[cfg(test)] mod tests { - use crate::error::ContractError; - use axelar_wasm_std::Participant; + use std::collections::HashSet; + + use axelar_wasm_std::permission_control::Permission; use cosmwasm_std::testing::{ mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage, }; - use cosmwasm_std::{Addr, Empty, HexBinary, OwnedDeps, Uint128}; - use multisig::key::{KeyType, PublicKey}; - use multisig::verifier_set::VerifierSet; + use cosmwasm_std::{Addr, Empty, OwnedDeps}; use router_api::ChainName; - use tofn::ecdsa::KeyPair; use super::*; + use crate::state::load_prover_by_chain; struct TestSetup { deps: OwnedDeps, @@ -110,7 +128,7 @@ mod tests { assert!(res.is_ok()); let eth_prover = Addr::unchecked("eth_prover"); - let eth: ChainName = "Ethereum".to_string().try_into().unwrap(); + let eth: ChainName = "Ethereum".parse().unwrap(); TestSetup { deps, @@ -120,75 +138,33 @@ mod tests { } } - pub struct Verifier { - pub addr: Addr, - pub supported_chains: Vec, - pub key_pair: KeyPair, - } - - fn create_verifier( - keypair_seed: u32, - verifier_address: Addr, - supported_chains: Vec, - ) -> Verifier { - let seed_bytes = keypair_seed.to_be_bytes(); - let mut result = [0; 64]; - result[0..seed_bytes.len()].copy_from_slice(seed_bytes.as_slice()); - let secret_recovery_key = result.as_slice().try_into().unwrap(); - - Verifier { - addr: verifier_address, - supported_chains, - key_pair: tofn::ecdsa::keygen(&secret_recovery_key, b"tofn nonce").unwrap(), - } - } - - fn create_verifier_set_from_verifiers( - verifiers: &Vec, - block_height: u64, - ) -> VerifierSet { - let mut pub_keys = vec![]; - for verifier in verifiers { - let encoded_verifying_key = - HexBinary::from(verifier.key_pair.encoded_verifying_key().to_vec()); - let pub_key = PublicKey::try_from((KeyType::Ecdsa, encoded_verifying_key)).unwrap(); - pub_keys.push(pub_key); - } - - let participants: Vec = verifiers - .iter() - .map(|verifier| Participant { - address: verifier.addr.clone(), - weight: Uint128::one().try_into().unwrap(), - }) - .collect(); - - VerifierSet::new( - participants.clone().into_iter().zip(pub_keys).collect(), - Uint128::from(participants.len() as u128).mul_ceil((2u64, 3u64)), - block_height, - ) - } - #[test] #[allow(clippy::arithmetic_side_effects)] fn test_instantiation() { let governance = "governance_for_coordinator"; - let test_setup = setup(governance); - - let config = CONFIG.load(test_setup.deps.as_ref().storage).unwrap(); - assert_eq!(config.governance, governance); - } - - #[test] - fn migrate_sets_contract_version() { - let mut deps = mock_dependencies(); + let mut test_setup = setup(governance); - migrate(deps.as_mut(), mock_env(), Empty {}).unwrap(); + assert!(execute( + test_setup.deps.as_mut(), + test_setup.env.clone(), + mock_info("not_governance", &[]), + ExecuteMsg::RegisterProverContract { + chain_name: test_setup.chain_name.clone(), + new_prover_addr: test_setup.prover.clone(), + } + ) + .is_err()); - let contract_version = cw2::get_contract_version(deps.as_mut().storage).unwrap(); - assert_eq!(contract_version.contract, "coordinator"); - assert_eq!(contract_version.version, CONTRACT_VERSION); + assert!(execute( + test_setup.deps.as_mut(), + test_setup.env, + mock_info(governance, &[]), + ExecuteMsg::RegisterProverContract { + chain_name: test_setup.chain_name.clone(), + new_prover_addr: test_setup.prover.clone(), + } + ) + .is_ok()); } #[test] @@ -207,9 +183,12 @@ mod tests { ) .unwrap(); - let chain_provers = - query::provers(test_setup.deps.as_ref(), test_setup.chain_name.clone()).unwrap(); - assert_eq!(chain_provers, test_setup.prover); + let chain_prover = load_prover_by_chain( + test_setup.deps.as_ref().storage, + test_setup.chain_name.clone(), + ); + assert!(chain_prover.is_ok(), "{:?}", chain_prover); + assert_eq!(chain_prover.unwrap(), test_setup.prover); } #[test] @@ -228,24 +207,22 @@ mod tests { ); assert_eq!( res.unwrap_err().to_string(), - axelar_wasm_std::ContractError::from(ContractError::Unauthorized).to_string() + axelar_wasm_std::error::ContractError::from( + permission_control::Error::PermissionDenied { + expected: Permission::Governance.into(), + actual: Permission::NoPrivilege.into() + } + ) + .to_string() ); } #[test] - fn set_and_get_populated_active_verifier_set_success() { + fn set_active_verifiers_from_prover_succeeds() { let governance = "governance_for_coordinator"; let mut test_setup = setup(governance); - let new_verifier = create_verifier( - 1, - Addr::unchecked("verifier1"), - vec![test_setup.chain_name.clone()], - ); - let new_verifier_set = - create_verifier_set_from_verifiers(&vec![new_verifier], test_setup.env.block.height); - - let res = execute( + execute( test_setup.deps.as_mut(), test_setup.env.clone(), mock_info(governance, &[]), @@ -253,45 +230,40 @@ mod tests { chain_name: test_setup.chain_name.clone(), new_prover_addr: test_setup.prover.clone(), }, - ); - assert!(res.is_ok()); + ) + .unwrap(); let res = execute( test_setup.deps.as_mut(), - test_setup.env.clone(), - mock_info(test_setup.prover.as_ref(), &[]), + test_setup.env, + mock_info(test_setup.prover.as_str(), &[]), ExecuteMsg::SetActiveVerifiers { - next_verifier_set: new_verifier_set.clone(), + verifiers: HashSet::new(), }, ); - assert!(res.is_ok()); - - let eth_active_verifier_set = - query::get_active_verifier_set(test_setup.deps.as_ref(), test_setup.chain_name.clone()) - .unwrap(); - - assert_eq!(eth_active_verifier_set, Some(new_verifier_set)); + assert!(res.is_ok(), "{:?}", res); } #[test] - fn set_and_get_empty_active_verifier_set_success() { + fn set_active_verifiers_from_random_address_fails() { let governance = "governance_for_coordinator"; let mut test_setup = setup(governance); - let _response = execute( + let res = execute( test_setup.deps.as_mut(), test_setup.env, - mock_info(governance, &[]), - ExecuteMsg::RegisterProverContract { - chain_name: test_setup.chain_name.clone(), - new_prover_addr: test_setup.prover.clone(), + mock_info(test_setup.prover.as_str(), &[]), + ExecuteMsg::SetActiveVerifiers { + verifiers: HashSet::new(), }, ); - - let query_result = - query::get_active_verifier_set(test_setup.deps.as_ref(), test_setup.chain_name.clone()) - .unwrap(); - - assert_eq!(query_result, None); + assert!(res.unwrap_err().to_string().contains( + &axelar_wasm_std::error::ContractError::from( + permission_control::Error::WhitelistNotFound { + sender: test_setup.prover + } + ) + .to_string() + )); } } diff --git a/contracts/coordinator/src/contract/execute.rs b/contracts/coordinator/src/contract/execute.rs new file mode 100644 index 000000000..23aa67db3 --- /dev/null +++ b/contracts/coordinator/src/contract/execute.rs @@ -0,0 +1,25 @@ +use std::collections::HashSet; + +use cosmwasm_std::{Addr, DepsMut, MessageInfo, Response}; +use router_api::ChainName; + +use crate::error::ContractError; +use crate::state::{save_prover_for_chain, update_verifier_set_for_prover}; + +pub fn register_prover( + deps: DepsMut, + chain_name: ChainName, + new_prover_addr: Addr, +) -> Result { + save_prover_for_chain(deps.storage, chain_name, new_prover_addr)?; + Ok(Response::new()) +} + +pub fn set_active_verifier_set( + deps: DepsMut, + info: MessageInfo, + verifiers: HashSet, +) -> Result { + update_verifier_set_for_prover(deps.storage, info.sender, verifiers)?; + Ok(Response::new()) +} diff --git a/contracts/coordinator/src/contract/migrations/mod.rs b/contracts/coordinator/src/contract/migrations/mod.rs new file mode 100644 index 000000000..693d0ab24 --- /dev/null +++ b/contracts/coordinator/src/contract/migrations/mod.rs @@ -0,0 +1,2 @@ +#[allow(deprecated)] +pub mod v0_2_0; diff --git a/contracts/coordinator/src/contract/migrations/v0_2_0.rs b/contracts/coordinator/src/contract/migrations/v0_2_0.rs new file mode 100644 index 000000000..1e5dba106 --- /dev/null +++ b/contracts/coordinator/src/contract/migrations/v0_2_0.rs @@ -0,0 +1,181 @@ +use axelar_wasm_std::permission_control; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, Storage}; +use cw_storage_plus::{Item, Map}; +use router_api::ChainName; + +use crate::contract::CONTRACT_NAME; +use crate::error::ContractError; +use crate::state::save_prover_for_chain; + +const BASE_VERSION: &str = "0.2.0"; + +pub fn migrate(storage: &mut dyn Storage) -> Result<(), ContractError> { + cw2::assert_contract_version(storage, CONTRACT_NAME, BASE_VERSION)?; + + migrate_config_to_permission_control(storage)?; + migrate_registered_provers(storage)?; + Ok(()) +} + +fn migrate_config_to_permission_control(storage: &mut dyn Storage) -> Result<(), ContractError> { + let config = CONFIG.load(storage).map_err(ContractError::from)?; + permission_control::set_governance(storage, &config.governance).map_err(ContractError::from)?; + CONFIG.remove(storage); + Ok(()) +} + +fn migrate_registered_provers(storage: &mut dyn Storage) -> Result<(), ContractError> { + PROVER_PER_CHAIN + .range(storage, None, None, cosmwasm_std::Order::Ascending) + .collect::, _>>()? + .into_iter() + .try_for_each(|(chain, prover)| save_prover_for_chain(storage, chain, prover))?; + + PROVER_PER_CHAIN.clear(storage); + Ok(()) +} + +#[cw_serde] +#[deprecated(since = "0.2.0", note = "only used to test the migration")] +struct Config { + pub governance: Addr, +} + +#[deprecated(since = "0.2.0", note = "only used to test the migration")] +const CONFIG: Item = Item::new("config"); + +#[deprecated(since = "0.2.0", note = "only used to test the migration")] +const PROVER_PER_CHAIN: Map = Map::new("prover_per_chain"); + +#[cfg(test)] +mod tests { + use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; + use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, Response}; + use router_api::ChainName; + + use super::PROVER_PER_CHAIN; + use crate::contract::migrations::v0_2_0; + use crate::contract::migrations::v0_2_0::BASE_VERSION; + use crate::contract::{execute, CONTRACT_NAME}; + use crate::error::ContractError; + use crate::msg::{ExecuteMsg, InstantiateMsg}; + use crate::state::{is_prover_registered, load_prover_by_chain}; + + #[test] + fn migrate_checks_contract_version() { + let mut deps = mock_dependencies(); + let _ = instantiate_0_2_0_contract(deps.as_mut()).unwrap(); + cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, "something wrong").unwrap(); + + assert!(v0_2_0::migrate(deps.as_mut().storage).is_err()); + + cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, BASE_VERSION).unwrap(); + + assert!(v0_2_0::migrate(deps.as_mut().storage).is_ok()); + } + + #[test] + fn ensure_governance_is_migrated_to_permission_control() { + let mut deps = mock_dependencies(); + + let msg = instantiate_0_2_0_contract(deps.as_mut()).unwrap(); + + assert!(v0_2_0::CONFIG.may_load(&deps.storage).unwrap().is_some()); + + assert!(v0_2_0::migrate(deps.as_mut().storage).is_ok()); + + assert!(execute( + deps.as_mut(), + mock_env(), + mock_info("anyone", &[]), + ExecuteMsg::RegisterProverContract { + chain_name: "chain".parse().unwrap(), + new_prover_addr: Addr::unchecked("any_addr"), + }, + ) + .is_err()); + + assert!(execute( + deps.as_mut(), + mock_env(), + mock_info(&msg.governance_address, &[]), + ExecuteMsg::RegisterProverContract { + chain_name: "chain".parse().unwrap(), + new_prover_addr: Addr::unchecked("any_addr"), + }, + ) + .is_ok()); + + assert!(v0_2_0::CONFIG.may_load(&deps.storage).unwrap().is_none()) + } + + #[test] + fn ensure_registered_provers_are_migrated() { + let mut deps = mock_dependencies(); + instantiate_0_2_0_contract(deps.as_mut()).unwrap(); + + let provers: Vec<(ChainName, Addr)> = vec![ + ("chain1".parse().unwrap(), Addr::unchecked("addr1")), + ("chain2".parse().unwrap(), Addr::unchecked("addr2")), + ]; + + for (chain, prover) in &provers { + register_prover_0_2_0(deps.as_mut(), chain.clone(), prover.clone()).unwrap(); + } + + assert!(v0_2_0::migrate(deps.as_mut().storage).is_ok()); + + for (chain, prover) in provers { + assert_eq!( + load_prover_by_chain(deps.as_ref().storage, chain).unwrap(), + prover.clone() + ); + + // check index is working as well + assert!(is_prover_registered(deps.as_ref().storage, prover).unwrap()); + } + + assert!(PROVER_PER_CHAIN.is_empty(deps.as_ref().storage)); + } + + fn instantiate_0_2_0_contract( + deps: DepsMut, + ) -> Result { + let governance = "governance"; + + let msg = InstantiateMsg { + governance_address: governance.to_string(), + }; + instantiate_0_2_0(deps, mock_env(), mock_info("sender", &[]), msg.clone())?; + Ok(msg) + } + + #[deprecated(since = "0.2.0", note = "only used to test the migration")] + fn instantiate_0_2_0( + deps: DepsMut, + _env: Env, + _info: MessageInfo, + msg: InstantiateMsg, + ) -> Result { + cw2::set_contract_version(deps.storage, CONTRACT_NAME, BASE_VERSION)?; + + v0_2_0::CONFIG.save( + deps.storage, + &v0_2_0::Config { + governance: deps.api.addr_validate(&msg.governance_address)?, + }, + )?; + Ok(Response::default()) + } + + #[deprecated(since = "0.2.0", note = "only used to test the migration")] + fn register_prover_0_2_0( + deps: DepsMut, + chain_name: ChainName, + new_prover_addr: Addr, + ) -> Result { + PROVER_PER_CHAIN.save(deps.storage, chain_name.clone(), &(new_prover_addr))?; + Ok(Response::new()) + } +} diff --git a/contracts/coordinator/src/contract/query.rs b/contracts/coordinator/src/contract/query.rs new file mode 100644 index 000000000..275e5488e --- /dev/null +++ b/contracts/coordinator/src/contract/query.rs @@ -0,0 +1,16 @@ +use cosmwasm_std::{Addr, Deps, Order, StdResult}; + +use crate::state::VERIFIER_PROVER_INDEXED_MAP; + +fn is_verifier_in_any_verifier_set(deps: Deps, verifier_address: &Addr) -> bool { + VERIFIER_PROVER_INDEXED_MAP + .idx + .by_verifier + .prefix(verifier_address.clone()) + .range(deps.storage, None, None, Order::Ascending) + .any(|_| true) +} + +pub fn check_verifier_ready_to_unbond(deps: Deps, verifier_address: Addr) -> StdResult { + Ok(!is_verifier_in_any_verifier_set(deps, &verifier_address)) +} diff --git a/contracts/coordinator/src/error.rs b/contracts/coordinator/src/error.rs index 8a69ce8ba..a60817fc5 100644 --- a/contracts/coordinator/src/error.rs +++ b/contracts/coordinator/src/error.rs @@ -1,6 +1,6 @@ -use axelar_wasm_std_derive::IntoContractError; +use axelar_wasm_std::IntoContractError; use cosmwasm_std::StdError; -use router_api::ChainName; +use cw2::VersionError; use thiserror::Error; #[derive(Error, Debug, PartialEq, IntoContractError)] @@ -8,9 +8,12 @@ pub enum ContractError { #[error(transparent)] Std(#[from] StdError), + #[error(transparent)] + Version(#[from] VersionError), + #[error("caller not unauthorized to perform this action")] Unauthorized, - #[error("no provers registered for chain {0}")] - NoProversRegisteredForChain(ChainName), + #[error("prover is not registered")] + ProverNotRegistered, } diff --git a/contracts/coordinator/src/execute.rs b/contracts/coordinator/src/execute.rs deleted file mode 100644 index 72756cdb1..000000000 --- a/contracts/coordinator/src/execute.rs +++ /dev/null @@ -1,33 +0,0 @@ -use cosmwasm_std::{Addr, DepsMut, MessageInfo, Response}; - -use multisig::verifier_set::VerifierSet; -use router_api::ChainName; - -use crate::error::ContractError; -use crate::state::{ACTIVE_VERIFIER_SET_FOR_PROVER, CONFIG, PROVER_PER_CHAIN}; - -pub fn check_governance(deps: &DepsMut, info: MessageInfo) -> Result<(), ContractError> { - let config = CONFIG.load(deps.storage)?; - if config.governance != info.sender { - return Err(ContractError::Unauthorized); - } - Ok(()) -} - -pub fn register_prover( - deps: DepsMut, - chain_name: ChainName, - new_prover_addr: Addr, -) -> Result { - PROVER_PER_CHAIN.save(deps.storage, chain_name.clone(), &(new_prover_addr))?; - Ok(Response::new()) -} - -pub fn set_active_verifier_set( - deps: DepsMut, - info: MessageInfo, - next_verifier_set: VerifierSet, -) -> Result { - ACTIVE_VERIFIER_SET_FOR_PROVER.save(deps.storage, info.sender, &(next_verifier_set))?; - Ok(Response::new()) -} diff --git a/contracts/coordinator/src/lib.rs b/contracts/coordinator/src/lib.rs index b1acd54e1..f6466fdf3 100644 --- a/contracts/coordinator/src/lib.rs +++ b/contracts/coordinator/src/lib.rs @@ -1,6 +1,4 @@ pub mod contract; pub mod error; -pub mod execute; pub mod msg; -pub mod query; -pub mod state; +mod state; diff --git a/contracts/coordinator/src/msg.rs b/contracts/coordinator/src/msg.rs index dfe908e14..b0e95f90f 100644 --- a/contracts/coordinator/src/msg.rs +++ b/contracts/coordinator/src/msg.rs @@ -1,6 +1,8 @@ +use std::collections::HashSet; + use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::Addr; -use multisig::verifier_set::VerifierSet; +use msgs_derive::EnsurePermissions; use router_api::ChainName; #[cw_serde] @@ -9,20 +11,20 @@ pub struct InstantiateMsg { } #[cw_serde] +#[derive(EnsurePermissions)] pub enum ExecuteMsg { - // Can only be called by governance + #[permission(Governance)] RegisterProverContract { chain_name: ChainName, new_prover_addr: Addr, }, - SetActiveVerifiers { - next_verifier_set: VerifierSet, - }, + #[permission(Specific(prover))] + SetActiveVerifiers { verifiers: HashSet }, } #[cw_serde] #[derive(QueryResponses)] pub enum QueryMsg { - #[returns(VerifierSet)] - GetActiveVerifiers { chain_name: ChainName }, + #[returns(bool)] + ReadyToUnbond { worker_address: Addr }, } diff --git a/contracts/coordinator/src/query.rs b/contracts/coordinator/src/query.rs deleted file mode 100644 index 0842c83cf..000000000 --- a/contracts/coordinator/src/query.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::error::ContractError; -use crate::state::{ACTIVE_VERIFIER_SET_FOR_PROVER, PROVER_PER_CHAIN}; -use cosmwasm_std::{Addr, Deps, StdResult}; -use multisig::verifier_set::VerifierSet; -use router_api::ChainName; - -pub fn provers(deps: Deps, chain_name: ChainName) -> Result { - PROVER_PER_CHAIN - .may_load(deps.storage, chain_name.clone())? - .ok_or(ContractError::NoProversRegisteredForChain(chain_name)) -} - -// For now, we consider only one prover per chain -pub fn get_active_verifier_set( - deps: Deps, - chain_name: ChainName, -) -> StdResult> { - let prover_address = provers(deps, chain_name).unwrap(); - let active_verifier_set = - ACTIVE_VERIFIER_SET_FOR_PROVER.may_load(deps.storage, prover_address.clone())?; - - Ok(active_verifier_set) -} diff --git a/contracts/coordinator/src/state.rs b/contracts/coordinator/src/state.rs index ddd4f962d..8b953900b 100644 --- a/contracts/coordinator/src/state.rs +++ b/contracts/coordinator/src/state.rs @@ -1,20 +1,117 @@ +use std::collections::HashSet; + use cosmwasm_schema::cw_serde; -use cosmwasm_std::Addr; -use cw_storage_plus::{Item, Map}; -use multisig::verifier_set::VerifierSet; +use cosmwasm_std::{Addr, Order, Storage}; +use cw_storage_plus::{index_list, IndexedMap, MultiIndex, UniqueIndex}; use router_api::ChainName; +use crate::error::ContractError; + +type ProverAddress = Addr; +type VerifierAddress = Addr; + +#[index_list(ProverAddress)] +struct ChainProverIndexes<'a> { + pub by_prover: UniqueIndex<'a, ProverAddress, ProverAddress, ChainName>, +} + +const CHAIN_PROVER_INDEXED_MAP: IndexedMap = + IndexedMap::new( + "chain_prover_map", + ChainProverIndexes { + by_prover: UniqueIndex::new(|prover| prover.clone(), "chain_prover_map_by_prover"), + }, + ); + +pub fn is_prover_registered( + storage: &dyn Storage, + prover_address: ProverAddress, +) -> Result { + Ok(CHAIN_PROVER_INDEXED_MAP + .idx + .by_prover + .item(storage, prover_address)? + .is_some()) +} + +#[allow(dead_code)] // Used in tests, might be useful in future query +pub fn load_prover_by_chain( + storage: &dyn Storage, + chain_name: ChainName, +) -> Result { + CHAIN_PROVER_INDEXED_MAP + .may_load(storage, chain_name)? + .ok_or(ContractError::ProverNotRegistered) +} + +pub fn save_prover_for_chain( + storage: &mut dyn Storage, + chain: ChainName, + prover: ProverAddress, +) -> Result<(), ContractError> { + CHAIN_PROVER_INDEXED_MAP.save(storage, chain.clone(), &prover)?; + Ok(()) +} + +#[index_list(VerifierProverRecord)] +pub struct VerifierSetIndex<'a> { + pub by_verifier: + MultiIndex<'a, VerifierAddress, VerifierProverRecord, (ProverAddress, VerifierAddress)>, +} + #[cw_serde] -pub struct Config { - pub governance: Addr, +pub struct VerifierProverRecord { + pub prover: ProverAddress, + pub verifier: VerifierAddress, } -pub const CONFIG: Item = Item::new("config"); +pub const VERIFIER_PROVER_INDEXED_MAP: IndexedMap< + (ProverAddress, VerifierAddress), + VerifierProverRecord, + VerifierSetIndex, +> = IndexedMap::new( + "verifier_prover_map", + VerifierSetIndex { + by_verifier: MultiIndex::new( + |_pk: &[u8], d| d.verifier.clone(), + "verifier_prover_map", + "verifier_prover_map_by_verifier", + ), + }, +); -type ProverAddress = Addr; +pub fn update_verifier_set_for_prover( + storage: &mut dyn Storage, + prover_address: ProverAddress, + new_verifiers: HashSet, +) -> Result<(), ContractError> { + let existing_verifiers = VERIFIER_PROVER_INDEXED_MAP + .prefix(prover_address.clone()) + .keys(storage, None, None, Order::Ascending) + .filter_map(Result::ok) + .collect::>(); -pub const PROVER_PER_CHAIN: Map = Map::new("prover_per_chain"); + for verifier in existing_verifiers.difference(&new_verifiers) { + VERIFIER_PROVER_INDEXED_MAP + .remove(storage, (prover_address.clone(), verifier.clone())) + .unwrap_or_else(|_| { + panic!( + "Failed to remove verifier {:?} for prover {:?}", + verifier, prover_address + ) + }); + } -// TODO: migrate this? -pub const ACTIVE_VERIFIER_SET_FOR_PROVER: Map = - Map::new("active_prover_verifier_set"); + for verifier in new_verifiers.difference(&existing_verifiers) { + VERIFIER_PROVER_INDEXED_MAP.save( + storage, + (prover_address.clone(), verifier.clone()), + &VerifierProverRecord { + prover: prover_address.clone(), + verifier: verifier.clone(), + }, + )?; + } + + Ok(()) +} diff --git a/contracts/multisig/.cargo/config b/contracts/gateway/.cargo/config.toml similarity index 100% rename from contracts/multisig/.cargo/config rename to contracts/gateway/.cargo/config.toml diff --git a/contracts/gateway/Cargo.toml b/contracts/gateway/Cargo.toml index 3945483da..dce577e75 100644 --- a/contracts/gateway/Cargo.toml +++ b/contracts/gateway/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "gateway" -version = "0.2.2" +version = "1.0.0" rust-version = { workspace = true } -edition = "2021" +edition = { workspace = true } description = "Gateway contract" exclude = [ @@ -31,12 +31,11 @@ generate_golden_files = [] optimize = """docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/rust-optimizer:0.12.6 + cosmwasm/optimizer:0.16.0 """ [dependencies] -axelar-wasm-std = { workspace = true } -axelar-wasm-std-derive = { workspace = true } +axelar-wasm-std = { workspace = true, features = ["derive"] } client = { workspace = true } cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } @@ -54,6 +53,7 @@ voting-verifier = { workspace = true, features = ["library"] } [dev-dependencies] cw-multi-test = "0.15.1" +rand = "0.8.5" [lints] workspace = true diff --git a/contracts/gateway/src/bin/schema.rs b/contracts/gateway/src/bin/schema.rs index afb8679ae..edf3475d3 100644 --- a/contracts/gateway/src/bin/schema.rs +++ b/contracts/gateway/src/bin/schema.rs @@ -1,7 +1,6 @@ use cosmwasm_schema::write_api; -use gateway_api::msg::{ExecuteMsg, QueryMsg}; - use gateway::msg::InstantiateMsg; +use gateway_api::msg::{ExecuteMsg, QueryMsg}; fn main() { write_api! { diff --git a/contracts/gateway/src/contract.rs b/contracts/gateway/src/contract.rs index da85f155c..a3ec708fd 100644 --- a/contracts/gateway/src/contract.rs +++ b/contracts/gateway/src/contract.rs @@ -1,165 +1,111 @@ +use std::fmt::Debug; + +use axelar_wasm_std::{address, FnExt}; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response}; +use error_stack::ResultExt; use gateway_api::msg::{ExecuteMsg, QueryMsg}; -use router_api::CrossChainId; -use std::fmt::Debug; +use router_api::client::Router; use crate::msg::InstantiateMsg; +use crate::state; +use crate::state::Config; mod execute; +mod migrations; mod query; const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("batch contains duplicate message ids")] + DuplicateMessageIds, + #[error("failed to query message status")] + MessageStatus, + #[error("failed to verify messages")] + VerifyMessages, + #[error("failed to route outgoing messages to gateway")] + RouteOutgoingMessages, + #[error("failed to route messages from gateway to router")] + RouteIncomingMessages, + #[error("failed to query outgoing messages")] + OutgoingMessages, + #[error("failed to save outgoing message")] + SaveOutgoingMessage, + #[error("failed to execute gateway command")] + Execute, +} + #[cfg_attr(not(feature = "library"), entry_point)] pub fn migrate( - deps: DepsMut, + _deps: DepsMut, _env: Env, _msg: Empty, -) -> Result { - // any version checks should be done before here - - cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - +) -> Result { Ok(Response::default()) } #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( deps: DepsMut, - env: Env, - info: MessageInfo, + _: Env, + _: MessageInfo, msg: InstantiateMsg, -) -> Result { +) -> Result { cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - Ok(internal::instantiate(deps, env, info, msg)?) + let router = address::validate_cosmwasm_address(deps.api, &msg.router_address)?; + let verifier = address::validate_cosmwasm_address(deps.api, &msg.verifier_address)?; + + state::save_config(deps.storage, &Config { verifier, router })?; + Ok(Response::new()) } #[cfg_attr(not(feature = "library"), entry_point)] pub fn execute( deps: DepsMut, - env: Env, + _: Env, info: MessageInfo, msg: ExecuteMsg, -) -> Result { - Ok(internal::execute(deps, env, info, msg)?) +) -> Result { + let config = state::load_config(deps.storage).change_context(Error::Execute)?; + let verifier = client::Client::new(deps.querier, config.verifier).into(); + + match msg.ensure_permissions(deps.storage, &info.sender)? { + ExecuteMsg::VerifyMessages(msgs) => { + execute::verify_messages(&verifier, msgs).change_context(Error::VerifyMessages) + } + ExecuteMsg::RouteMessages(msgs) => { + let router = Router { + address: config.router, + }; + + if info.sender == router.address { + execute::route_outgoing_messages(deps.storage, msgs) + .change_context(Error::RouteOutgoingMessages) + } else { + execute::route_incoming_messages(&verifier, &router, msgs) + .change_context(Error::RouteIncomingMessages) + } + } + }? + .then(Ok) } #[cfg_attr(not(feature = "library"), entry_point)] pub fn query( deps: Deps, - env: Env, + _: Env, msg: QueryMsg, -) -> Result { - Ok(internal::query(deps, env, msg)?) -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("gateway contract config is missing")] - ConfigMissing, - #[error("invalid store access")] - InvalidStoreAccess, - #[error("failed to serialize the response")] - SerializeResponse, - #[error("batch contains duplicate message ids")] - DuplicateMessageIds, - #[error("invalid address")] - InvalidAddress, - #[error("failed to query message status")] - MessageStatus, - #[error("message with ID {0} not found")] - MessageNotFound(CrossChainId), -} - -mod internal { - use client::Client; - use cosmwasm_std::{to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response}; - use error_stack::{Result, ResultExt}; - use gateway_api::msg::{ExecuteMsg, QueryMsg}; - use router_api::client::Router; - - use crate::contract::Error; - use crate::msg::InstantiateMsg; - use crate::state::Config; - use crate::{contract, state}; - - pub(crate) fn instantiate( - deps: DepsMut, - _env: Env, - _info: MessageInfo, - msg: InstantiateMsg, - ) -> Result { - let router = deps - .api - .addr_validate(&msg.router_address) - .change_context(Error::InvalidAddress) - .attach_printable(msg.router_address)?; - - let verifier = deps - .api - .addr_validate(&msg.verifier_address) - .change_context(Error::InvalidAddress) - .attach_printable(msg.verifier_address)?; - - state::save_config(deps.storage, &Config { verifier, router }) - .change_context(Error::InvalidStoreAccess)?; - - Ok(Response::new()) - } - - pub(crate) fn execute( - deps: DepsMut, - _env: Env, - info: MessageInfo, - msg: ExecuteMsg, - ) -> Result { - let config = state::load_config(deps.storage).change_context(Error::ConfigMissing)?; - let verifier = Client::new(deps.querier, config.verifier).into(); - - let router = Router { - address: config.router, - }; - - match msg { - ExecuteMsg::VerifyMessages(msgs) => contract::execute::verify_messages(&verifier, msgs), - ExecuteMsg::RouteMessages(msgs) => { - if info.sender == router.address { - contract::execute::route_outgoing_messages(deps.storage, msgs) - } else { - contract::execute::route_incoming_messages(&verifier, &router, msgs) - } - } - } - } - - pub(crate) fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result { - match msg { - QueryMsg::GetOutgoingMessages { message_ids } => { - let msgs = contract::query::get_outgoing_messages(deps.storage, message_ids)?; - to_json_binary(&msgs).change_context(Error::SerializeResponse) - } +) -> Result { + match msg { + QueryMsg::OutgoingMessages(message_ids) => { + query::outgoing_messages(deps.storage, message_ids.iter()) + .change_context(Error::OutgoingMessages) } - } -} - -#[cfg(test)] -mod tests { - use cosmwasm_std::testing::{mock_dependencies, mock_env}; - - use super::*; - - #[test] - fn migrate_sets_contract_version() { - let mut deps = mock_dependencies(); - - migrate(deps.as_mut(), mock_env(), Empty {}).unwrap(); - - let contract_version = cw2::get_contract_version(deps.as_mut().storage).unwrap(); - assert_eq!(contract_version.contract, "gateway"); - assert_eq!(contract_version.version, CONTRACT_VERSION); - } + }? + .then(Ok) } diff --git a/contracts/gateway/src/contract/execute.rs b/contracts/gateway/src/contract/execute.rs index efd1721cd..0369d96c9 100644 --- a/contracts/gateway/src/contract/execute.rs +++ b/contracts/gateway/src/contract/execute.rs @@ -19,7 +19,7 @@ pub fn verify_messages( }) } -pub(crate) fn route_incoming_messages( +pub fn route_incoming_messages( verifier: &voting_verifier::Client, router: &Router, msgs: Vec, @@ -30,15 +30,15 @@ pub(crate) fn route_incoming_messages( } // because the messages came from the router, we can assume they are already verified -pub(crate) fn route_outgoing_messages( +pub fn route_outgoing_messages( store: &mut dyn Storage, verified: Vec, ) -> Result { let msgs = check_for_duplicates(verified)?; for msg in msgs.iter() { - state::save_outgoing_msg(store, msg.cc_id.clone(), msg) - .change_context(Error::InvalidStoreAccess)?; + state::save_outgoing_message(store, &msg.cc_id, msg) + .change_context(Error::SaveOutgoingMessage)?; } Ok(Response::new().add_events( @@ -179,3 +179,39 @@ fn flat_unzip(x: impl Iterator, Vec)>) -> (Vec, Vec, transform: fn(Message) -> GatewayEvent) -> Vec { msgs.into_iter().map(|msg| transform(msg).into()).collect() } + +#[cfg(test)] +mod tests { + use axelar_wasm_std::err_contains; + use cosmwasm_std::testing::mock_dependencies; + use router_api::{CrossChainId, Message}; + + use crate::contract::execute::route_outgoing_messages; + use crate::state; + + #[test] + fn reject_reroute_outgoing_message_with_different_contents() { + let mut msg = Message { + cc_id: CrossChainId::new("source-chain", "0x1234-1").unwrap(), + source_address: "source-address".parse().unwrap(), + destination_chain: "destination-chain".parse().unwrap(), + destination_address: "destination-address".parse().unwrap(), + payload_hash: [1; 32], + }; + + let mut deps = mock_dependencies(); + + let response = route_outgoing_messages(deps.as_mut().storage, vec![msg.clone()]); + assert!(response.is_ok()); + + // re-route with different payload + msg.payload_hash = [2; 32]; + + let response = route_outgoing_messages(deps.as_mut().storage, vec![msg]); + assert!(response.is_err_and(|err| err_contains!( + err, + state::Error, + state::Error::MessageMismatch { .. } + ))); + } +} diff --git a/contracts/voting-verifier/src/migrations/mod.rs b/contracts/gateway/src/contract/migrations/mod.rs similarity index 100% rename from contracts/voting-verifier/src/migrations/mod.rs rename to contracts/gateway/src/contract/migrations/mod.rs diff --git a/contracts/gateway/src/contract/query.rs b/contracts/gateway/src/contract/query.rs index 13243d90d..59cc4c4e5 100644 --- a/contracts/gateway/src/contract/query.rs +++ b/contracts/gateway/src/contract/query.rs @@ -1,94 +1,85 @@ -use crate::contract::Error; use axelar_wasm_std::error::extend_err; -use cosmwasm_std::Storage; -use error_stack::{report, Result, ResultExt}; +use cosmwasm_std::{to_json_binary, Binary, Storage}; +use error_stack::Result; use router_api::{CrossChainId, Message}; use crate::state; -pub fn get_outgoing_messages( +pub fn outgoing_messages<'a>( storage: &dyn Storage, - cross_chain_ids: Vec, -) -> Result, Error> { - cross_chain_ids - .into_iter() - .map(|id| try_load_msg(storage, id)) - .fold(Ok(vec![]), accumulate_errs) -} + cross_chain_ids: impl Iterator, +) -> Result { + let msgs = cross_chain_ids + .map(|id| state::load_outgoing_message(storage, id)) + .fold(Ok(vec![]), accumulate_errs)?; -fn try_load_msg(storage: &dyn Storage, id: CrossChainId) -> Result { - state::may_load_outgoing_msg(storage, id.clone()) - .change_context(Error::InvalidStoreAccess) - .transpose() - .unwrap_or(Err(report!(Error::MessageNotFound(id)))) + Ok(to_json_binary(&msgs).map_err(state::Error::from)?) } fn accumulate_errs( - acc: Result, Error>, - msg: Result, -) -> Result, Error> { + acc: Result, state::Error>, + msg: std::result::Result, +) -> Result, state::Error> { match (acc, msg) { - (Ok(mut acc), Ok(msg)) => { - acc.push(msg); - Ok(acc) + (Ok(mut msgs), Ok(msg)) => { + msgs.push(msg); + Ok(msgs) } - (Err(acc), Ok(_)) => Err(acc), - (acc, Err(msg_err)) => extend_err(acc, msg_err), + (Err(report), Ok(_)) => Err(report), + (acc, Err(msg_err)) => extend_err(acc, msg_err.into()), } } #[cfg(test)] mod test { - use crate::state; + use cosmwasm_std::from_json; use cosmwasm_std::testing::mock_dependencies; use router_api::{CrossChainId, Message}; + use crate::state; + #[test] - fn get_outgoing_messages_all_messages_present_returns_all() { + fn outgoing_messages_all_messages_present_returns_all() { let mut deps = mock_dependencies(); let messages = generate_messages(); for message in messages.iter() { - state::save_outgoing_msg(deps.as_mut().storage, message.cc_id.clone(), message) - .unwrap(); + state::save_outgoing_message(deps.as_mut().storage, &message.cc_id, message).unwrap(); } - let ids = messages.iter().map(|msg| msg.cc_id.clone()).collect(); + let ids = messages.iter().map(|msg| &msg.cc_id); - let res = super::get_outgoing_messages(&deps.storage, ids).unwrap(); - assert_eq!(res, messages); + let res = super::outgoing_messages(&deps.storage, ids).unwrap(); + let actual_messages: Vec = from_json(res).unwrap(); + assert_eq!(actual_messages, messages); } #[test] - fn get_outgoing_messages_nothing_stored_returns_not_found_error() { + fn outgoing_messages_nothing_stored_returns_not_found_error() { let deps = mock_dependencies(); let messages = generate_messages(); - let ids = messages.iter().map(|msg| msg.cc_id.clone()).collect(); + let ids = messages.iter().map(|msg| &msg.cc_id); - let res = super::get_outgoing_messages(&deps.storage, ids); + let res = super::outgoing_messages(&deps.storage, ids); assert!(res.is_err()); assert_eq!(res.unwrap_err().current_frames().len(), messages.len()); } #[test] - fn get_outgoing_messages_only_partially_found_returns_not_found_error() { + fn outgoing_messages_only_partially_found_returns_not_found_error() { let mut deps = mock_dependencies(); let messages = generate_messages(); - state::save_outgoing_msg( - deps.as_mut().storage, - messages[1].cc_id.clone(), - &messages[1], - ) - .unwrap(); + state::save_outgoing_message(deps.as_mut().storage, &messages[1].cc_id, &messages[1]) + .unwrap(); - let ids = messages.iter().map(|msg| msg.cc_id.clone()).collect(); + let ids = messages.iter().map(|msg| &msg.cc_id); - let res = super::get_outgoing_messages(&deps.storage, ids); + let res = super::outgoing_messages(&deps.storage, ids); assert!(res.is_err()); assert_eq!(res.unwrap_err().current_frames().len(), messages.len() - 1); @@ -97,30 +88,21 @@ mod test { fn generate_messages() -> Vec { vec![ Message { - cc_id: CrossChainId { - chain: "chain1".parse().unwrap(), - id: "id1".parse().unwrap(), - }, + cc_id: CrossChainId::new("chain1", "id1").unwrap(), destination_address: "addr1".parse().unwrap(), destination_chain: "chain2".parse().unwrap(), source_address: "addr2".parse().unwrap(), payload_hash: [0; 32], }, Message { - cc_id: CrossChainId { - chain: "chain2".parse().unwrap(), - id: "id2".parse().unwrap(), - }, + cc_id: CrossChainId::new("chain2", "id2").unwrap(), destination_address: "addr3".parse().unwrap(), destination_chain: "chain3".parse().unwrap(), source_address: "addr4".parse().unwrap(), payload_hash: [1; 32], }, Message { - cc_id: CrossChainId { - chain: "chain3".parse().unwrap(), - id: "id3".parse().unwrap(), - }, + cc_id: CrossChainId::new("chain3", "id3").unwrap(), destination_address: "addr5".parse().unwrap(), destination_chain: "chain4".parse().unwrap(), source_address: "addr6".parse().unwrap(), diff --git a/contracts/gateway/src/lib.rs b/contracts/gateway/src/lib.rs index cdacc8154..b376dfa30 100644 --- a/contracts/gateway/src/lib.rs +++ b/contracts/gateway/src/lib.rs @@ -1,4 +1,4 @@ pub mod contract; -pub mod events; +mod events; pub mod msg; -pub mod state; +mod state; diff --git a/contracts/gateway/src/state.rs b/contracts/gateway/src/state.rs index c36a2fc33..35f8f8c1b 100644 --- a/contracts/gateway/src/state.rs +++ b/contracts/gateway/src/state.rs @@ -1,121 +1,115 @@ +use axelar_wasm_std::IntoContractError; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, Storage}; +use cosmwasm_std::{Addr, StdError, Storage}; use cw_storage_plus::{Item, Map}; -use error_stack::{Result, ResultExt}; use router_api::{CrossChainId, Message}; #[cw_serde] -pub(crate) struct Config { +pub struct Config { pub verifier: Addr, pub router: Addr, } -pub(crate) fn save_config(storage: &mut dyn Storage, value: &Config) -> Result<(), Error> { - CONFIG - .save(storage, value) - .change_context(Error::SaveValue(CONFIG_NAME)) +const CONFIG: Item = Item::new("config"); +const OUTGOING_MESSAGES: Map<&CrossChainId, Message> = Map::new("outgoing_messages"); + +#[derive(thiserror::Error, Debug, IntoContractError)] +pub enum Error { + #[error(transparent)] + Std(#[from] StdError), + #[error("gateway got into an invalid state, its config is missing")] + MissingConfig, + #[error("message with ID {0} mismatches with the stored one")] + MessageMismatch(CrossChainId), + #[error("message with ID {0} not found")] + MessageNotFound(CrossChainId), } -pub(crate) fn load_config(storage: &dyn Storage) -> Result { + +pub fn load_config(storage: &dyn Storage) -> Result { CONFIG - .load(storage) - .change_context(Error::LoadValue(CONFIG_NAME)) + .may_load(storage) + .map_err(Error::from)? + .ok_or(Error::MissingConfig) } -pub(crate) fn save_outgoing_msg( - storage: &mut dyn Storage, - key: CrossChainId, - value: &Message, -) -> Result<(), Error> { - OUTGOING_MESSAGES - .save(storage, key, value) - .change_context(Error::SaveValue(OUTGOING_MESSAGES_NAME)) +pub fn save_config(storage: &mut dyn Storage, config: &Config) -> Result<(), Error> { + CONFIG.save(storage, config).map_err(Error::from) } -pub(crate) fn may_load_outgoing_msg( + +pub fn load_outgoing_message( storage: &dyn Storage, - id: CrossChainId, -) -> Result, Error> { + cc_id: &CrossChainId, +) -> Result { OUTGOING_MESSAGES - .may_load(storage, id.clone()) - .change_context(Error::Parse(OUTGOING_MESSAGES_NAME)) - .attach_printable(id.to_string()) + .may_load(storage, cc_id) + .map_err(Error::from)? + .ok_or_else(|| Error::MessageNotFound(cc_id.clone())) } -#[derive(thiserror::Error, Debug)] -pub(crate) enum Error { - #[error("failed to save {0}")] - SaveValue(&'static str), - #[error("failed to load {0}")] - LoadValue(&'static str), - #[error("failed to parse key for {0}")] - Parse(&'static str), +pub fn save_outgoing_message( + storage: &mut dyn Storage, + cc_id: &CrossChainId, + msg: &Message, +) -> Result<(), Error> { + let existing = OUTGOING_MESSAGES + .may_load(storage, cc_id) + .map_err(Error::from)?; + + match existing { + Some(existing) if msg.hash() != existing.hash() => { + Err(Error::MessageMismatch(msg.cc_id.clone())) + } + Some(_) => Ok(()), // new message is identical, no need to store it + None => Ok(OUTGOING_MESSAGES + .save(storage, cc_id, msg) + .map_err(Error::from)?), + } } -const CONFIG_NAME: &str = "config"; -const CONFIG: Item = Item::new(CONFIG_NAME); -const OUTGOING_MESSAGES_NAME: &str = "outgoing_messages"; -const OUTGOING_MESSAGES: Map = Map::new(OUTGOING_MESSAGES_NAME); - #[cfg(test)] mod test { use cosmwasm_std::testing::mock_dependencies; - use cosmwasm_std::Addr; use router_api::{CrossChainId, Message}; - use crate::state::{ - load_config, may_load_outgoing_msg, save_config, save_outgoing_msg, Config, - }; - - #[test] - fn config_storage() { - let mut deps = mock_dependencies(); - - let config = Config { - verifier: Addr::unchecked("verifier"), - router: Addr::unchecked("router"), - }; - assert!(save_config(deps.as_mut().storage, &config).is_ok()); - - assert_eq!(load_config(&deps.storage).unwrap(), config); - } + use crate::state::OUTGOING_MESSAGES; #[test] fn outgoing_messages_storage() { let mut deps = mock_dependencies(); let message = Message { - cc_id: CrossChainId { - chain: "chain".parse().unwrap(), - id: "id".parse().unwrap(), - }, - source_address: "source_address".parse().unwrap(), + cc_id: CrossChainId::new("chain", "id").unwrap(), + source_address: "source-address".parse().unwrap(), destination_chain: "destination".parse().unwrap(), - destination_address: "destination_address".parse().unwrap(), + destination_address: "destination-address".parse().unwrap(), payload_hash: [1; 32], }; - assert!(save_outgoing_msg(deps.as_mut().storage, message.cc_id.clone(), &message).is_ok()); + assert!(OUTGOING_MESSAGES + .save(deps.as_mut().storage, &message.cc_id, &message) + .is_ok()); assert_eq!( - may_load_outgoing_msg(&deps.storage, message.cc_id.clone()).unwrap(), + OUTGOING_MESSAGES + .may_load(&deps.storage, &message.cc_id) + .unwrap(), Some(message) ); - let unknown_chain_id = CrossChainId { - chain: "unknown".parse().unwrap(), - id: "id".parse().unwrap(), - }; + let unknown_chain_id = CrossChainId::new("unknown", "id").unwrap(); assert_eq!( - may_load_outgoing_msg(&deps.storage, unknown_chain_id).unwrap(), + OUTGOING_MESSAGES + .may_load(&deps.storage, &unknown_chain_id) + .unwrap(), None ); - let unknown_id = CrossChainId { - chain: "chain".parse().unwrap(), - id: "unknown".parse().unwrap(), - }; + let unknown_id = CrossChainId::new("chain", "unkown").unwrap(); assert_eq!( - may_load_outgoing_msg(&deps.storage, unknown_id).unwrap(), + OUTGOING_MESSAGES + .may_load(&deps.storage, &unknown_id) + .unwrap(), None ); } diff --git a/contracts/gateway/tests/contract.rs b/contracts/gateway/tests/contract.rs index 0043914b4..cb86390aa 100644 --- a/contracts/gateway/tests/contract.rs +++ b/contracts/gateway/tests/contract.rs @@ -3,20 +3,21 @@ use std::fmt::Debug; use std::fs::File; use std::iter; -use axelar_wasm_std::{ContractError, VerificationStatus}; +use axelar_wasm_std::error::ContractError; +use axelar_wasm_std::{err_contains, VerificationStatus}; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockQuerier}; #[cfg(not(feature = "generate_golden_files"))] use cosmwasm_std::Response; use cosmwasm_std::{ from_json, to_json_binary, Addr, ContractResult, DepsMut, QuerierResult, WasmQuery, }; -use itertools::Itertools; -use router_api::{CrossChainId, Message}; -use serde::Serialize; - use gateway::contract::*; use gateway::msg::InstantiateMsg; use gateway_api::msg::{ExecuteMsg, QueryMsg}; +use itertools::Itertools; +use rand::{thread_rng, Rng}; +use router_api::{CrossChainId, Message}; +use serde::Serialize; use voting_verifier::msg::MessageStatus; #[test] @@ -138,9 +139,8 @@ fn successful_route_outgoing() { let router = "router"; instantiate_contract(deps.as_mut(), "verifier", router); - let query_msg = QueryMsg::GetOutgoingMessages { - message_ids: msgs.iter().map(|msg| msg.cc_id.clone()).collect(), - }; + let query_msg = + QueryMsg::OutgoingMessages(msgs.iter().map(|msg| msg.cc_id.clone()).collect()); // check no messages are outgoing let query_response = query(deps.as_ref(), mock_env(), query_msg.clone()); @@ -281,9 +281,46 @@ fn route_duplicate_ids_should_fail() { } } +#[test] +fn reject_reroute_outgoing_message_with_different_contents() { + let mut msgs = generate_msgs(VerificationStatus::SucceededOnSourceChain, 10); + + let mut deps = mock_dependencies(); + + let router = "router"; + instantiate_contract(deps.as_mut(), "verifier", router); + + let response = execute( + deps.as_mut(), + mock_env(), + mock_info(router, &[]), + ExecuteMsg::RouteMessages(msgs.clone()), + ); + assert!(response.is_ok()); + + // re-route with different payload + msgs.iter_mut().for_each(|msg| { + let mut rng = thread_rng(); + msg.payload_hash.iter_mut().for_each(|byte| { + *byte = rng.gen(); + }); + }); + let response = execute( + deps.as_mut(), + mock_env(), + mock_info(router, &[]), + ExecuteMsg::RouteMessages(msgs.clone()), + ); + assert!(response.is_err_and(|err| err_contains!( + err.report, + Error, + Error::RouteOutgoingMessages + ))); +} + fn test_cases_for_correct_verifier() -> ( Vec>, - impl Fn(voting_verifier::msg::QueryMsg) -> Result, ContractError> + Clone + Sized, + impl Fn(voting_verifier::msg::QueryMsg) -> Result, ContractError> + Clone, ) { let all_messages = generate_msgs_with_all_statuses(10); let status_by_msg = map_status_by_msg(all_messages.clone()); @@ -308,7 +345,7 @@ fn test_cases_for_correct_verifier() -> ( fn test_cases_for_duplicate_msgs() -> ( Vec>, - impl Fn(voting_verifier::msg::QueryMsg) -> Result, ContractError> + Clone + Sized, + impl Fn(voting_verifier::msg::QueryMsg) -> Result, ContractError> + Clone, ) { let all_messages = generate_msgs_with_all_statuses(10); let status_by_msg = map_status_by_msg(all_messages.clone()); @@ -334,7 +371,7 @@ fn test_cases_for_duplicate_msgs() -> ( } fn generate_msgs_with_all_statuses( - count_per_status: i32, + count_per_status: u8, ) -> HashMap> { all_statuses() .into_iter() @@ -342,17 +379,14 @@ fn generate_msgs_with_all_statuses( .collect::>>() } -fn generate_msgs(namespace: impl Debug, count: i32) -> Vec { +fn generate_msgs(namespace: impl Debug, count: u8) -> Vec { (0..count) .map(|i| Message { - cc_id: CrossChainId { - chain: "mock-chain".parse().unwrap(), - id: format!("{:?}{}", namespace, i).parse().unwrap(), - }, + cc_id: CrossChainId::new("mock-chain", format!("{:?}{}", namespace, i)).unwrap(), destination_address: "idc".parse().unwrap(), destination_chain: "mock-chain-2".parse().unwrap(), source_address: "idc".parse().unwrap(), - payload_hash: [i as u8; 32], + payload_hash: [i; 32], }) .collect() } @@ -401,7 +435,7 @@ fn correctly_working_verifier_handler( { move |msg: voting_verifier::msg::QueryMsg| -> Result, ContractError> { match msg { - voting_verifier::msg::QueryMsg::GetMessagesStatus { messages } => Ok(messages + voting_verifier::msg::QueryMsg::MessagesStatus(messages) => Ok(messages .into_iter() .map(|msg| { MessageStatus::new( diff --git a/contracts/gateway/tests/test_route_incoming.json b/contracts/gateway/tests/test_route_incoming.json index f69f36d33..f0b261d23 100644 --- a/contracts/gateway/tests/test_route_incoming.json +++ b/contracts/gateway/tests/test_route_incoming.json @@ -13,7 +13,7 @@ "wasm": { "execute": { "contract_addr": "router", - "msg": "eyJyb3V0ZV9tZXNzYWdlcyI6W3siY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPblNvdXJjZUNoYWluMCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifV19", + "msg": "eyJyb3V0ZV9tZXNzYWdlcyI6W3siY2NfaWQiOnsic291cmNlX2NoYWluIjoibW9jay1jaGFpbiIsIm1lc3NhZ2VfaWQiOiJTdWNjZWVkZWRPblNvdXJjZUNoYWluMCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifV19", "funds": [] } } @@ -28,7 +28,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain0" }, { @@ -36,7 +36,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -44,7 +44,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -64,7 +64,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain0" }, { @@ -72,7 +72,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -80,7 +80,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -100,7 +100,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain0" }, { @@ -108,7 +108,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -116,7 +116,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -136,7 +136,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify0" }, { @@ -144,7 +144,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -152,7 +152,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -172,7 +172,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress0" }, { @@ -180,7 +180,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -188,7 +188,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -208,7 +208,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown0" }, { @@ -216,7 +216,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -224,7 +224,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -244,7 +244,7 @@ "wasm": { "execute": { "contract_addr": "router", - "msg": "eyJyb3V0ZV9tZXNzYWdlcyI6W3siY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPblNvdXJjZUNoYWluMCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiU3VjY2VlZGVkT25Tb3VyY2VDaGFpbjEifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxIn0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IlN1Y2NlZWRlZE9uU291cmNlQ2hhaW4yIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMiJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPblNvdXJjZUNoYWluMyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiU3VjY2VlZGVkT25Tb3VyY2VDaGFpbjQifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IlN1Y2NlZWRlZE9uU291cmNlQ2hhaW41In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNSJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPblNvdXJjZUNoYWluNiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiU3VjY2VlZGVkT25Tb3VyY2VDaGFpbjcifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IlN1Y2NlZWRlZE9uU291cmNlQ2hhaW44In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwOCJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPblNvdXJjZUNoYWluOSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkifV19", + "msg": "eyJyb3V0ZV9tZXNzYWdlcyI6W3siY2NfaWQiOnsic291cmNlX2NoYWluIjoibW9jay1jaGFpbiIsIm1lc3NhZ2VfaWQiOiJTdWNjZWVkZWRPblNvdXJjZUNoYWluMCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiU3VjY2VlZGVkT25Tb3VyY2VDaGFpbjEifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxIn0seyJjY19pZCI6eyJzb3VyY2VfY2hhaW4iOiJtb2NrLWNoYWluIiwibWVzc2FnZV9pZCI6IlN1Y2NlZWRlZE9uU291cmNlQ2hhaW4yIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMiJ9LHsiY2NfaWQiOnsic291cmNlX2NoYWluIjoibW9jay1jaGFpbiIsIm1lc3NhZ2VfaWQiOiJTdWNjZWVkZWRPblNvdXJjZUNoYWluMyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiU3VjY2VlZGVkT25Tb3VyY2VDaGFpbjQifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0In0seyJjY19pZCI6eyJzb3VyY2VfY2hhaW4iOiJtb2NrLWNoYWluIiwibWVzc2FnZV9pZCI6IlN1Y2NlZWRlZE9uU291cmNlQ2hhaW41In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNSJ9LHsiY2NfaWQiOnsic291cmNlX2NoYWluIjoibW9jay1jaGFpbiIsIm1lc3NhZ2VfaWQiOiJTdWNjZWVkZWRPblNvdXJjZUNoYWluNiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiU3VjY2VlZGVkT25Tb3VyY2VDaGFpbjcifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3In0seyJjY19pZCI6eyJzb3VyY2VfY2hhaW4iOiJtb2NrLWNoYWluIiwibWVzc2FnZV9pZCI6IlN1Y2NlZWRlZE9uU291cmNlQ2hhaW44In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwOCJ9LHsiY2NfaWQiOnsic291cmNlX2NoYWluIjoibW9jay1jaGFpbiIsIm1lc3NhZ2VfaWQiOiJTdWNjZWVkZWRPblNvdXJjZUNoYWluOSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkifV19", "funds": [] } } @@ -259,7 +259,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain0" }, { @@ -267,7 +267,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -275,7 +275,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -288,7 +288,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain1" }, { @@ -296,7 +296,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -304,7 +304,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -317,7 +317,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain2" }, { @@ -325,7 +325,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -333,7 +333,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -346,7 +346,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain3" }, { @@ -354,7 +354,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -362,7 +362,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -375,7 +375,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain4" }, { @@ -383,7 +383,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -391,7 +391,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -404,7 +404,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain5" }, { @@ -412,7 +412,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -420,7 +420,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -433,7 +433,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain6" }, { @@ -441,7 +441,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -449,7 +449,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -462,7 +462,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain7" }, { @@ -470,7 +470,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -478,7 +478,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -491,7 +491,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain8" }, { @@ -499,7 +499,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -507,7 +507,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -520,7 +520,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain9" }, { @@ -528,7 +528,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -536,7 +536,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -556,7 +556,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain0" }, { @@ -564,7 +564,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -572,7 +572,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -585,7 +585,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain1" }, { @@ -593,7 +593,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -601,7 +601,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -614,7 +614,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain2" }, { @@ -622,7 +622,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -630,7 +630,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -643,7 +643,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain3" }, { @@ -651,7 +651,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -659,7 +659,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -672,7 +672,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain4" }, { @@ -680,7 +680,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -688,7 +688,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -701,7 +701,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain5" }, { @@ -709,7 +709,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -717,7 +717,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -730,7 +730,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain6" }, { @@ -738,7 +738,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -746,7 +746,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -759,7 +759,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain7" }, { @@ -767,7 +767,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -775,7 +775,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -788,7 +788,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain8" }, { @@ -796,7 +796,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -804,7 +804,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -817,7 +817,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain9" }, { @@ -825,7 +825,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -833,7 +833,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -853,7 +853,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain0" }, { @@ -861,7 +861,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -869,7 +869,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -882,7 +882,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain1" }, { @@ -890,7 +890,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -898,7 +898,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -911,7 +911,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain2" }, { @@ -919,7 +919,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -927,7 +927,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -940,7 +940,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain3" }, { @@ -948,7 +948,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -956,7 +956,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -969,7 +969,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain4" }, { @@ -977,7 +977,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -985,7 +985,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -998,7 +998,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain5" }, { @@ -1006,7 +1006,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1014,7 +1014,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1027,7 +1027,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain6" }, { @@ -1035,7 +1035,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1043,7 +1043,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1056,7 +1056,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain7" }, { @@ -1064,7 +1064,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1072,7 +1072,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1085,7 +1085,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain8" }, { @@ -1093,7 +1093,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1101,7 +1101,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1114,7 +1114,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain9" }, { @@ -1122,7 +1122,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1130,7 +1130,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1150,7 +1150,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify0" }, { @@ -1158,7 +1158,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1166,7 +1166,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1179,7 +1179,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify1" }, { @@ -1187,7 +1187,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1195,7 +1195,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1208,7 +1208,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify2" }, { @@ -1216,7 +1216,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1224,7 +1224,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1237,7 +1237,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify3" }, { @@ -1245,7 +1245,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1253,7 +1253,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1266,7 +1266,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify4" }, { @@ -1274,7 +1274,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1282,7 +1282,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1295,7 +1295,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify5" }, { @@ -1303,7 +1303,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1311,7 +1311,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1324,7 +1324,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify6" }, { @@ -1332,7 +1332,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1340,7 +1340,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1353,7 +1353,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify7" }, { @@ -1361,7 +1361,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1369,7 +1369,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1382,7 +1382,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify8" }, { @@ -1390,7 +1390,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1398,7 +1398,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1411,7 +1411,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify9" }, { @@ -1419,7 +1419,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1427,7 +1427,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1447,7 +1447,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress0" }, { @@ -1455,7 +1455,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1463,7 +1463,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1476,7 +1476,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress1" }, { @@ -1484,7 +1484,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1492,7 +1492,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1505,7 +1505,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress2" }, { @@ -1513,7 +1513,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1521,7 +1521,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1534,7 +1534,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress3" }, { @@ -1542,7 +1542,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1550,7 +1550,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1563,7 +1563,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress4" }, { @@ -1571,7 +1571,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1579,7 +1579,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1592,7 +1592,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress5" }, { @@ -1600,7 +1600,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1608,7 +1608,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1621,7 +1621,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress6" }, { @@ -1629,7 +1629,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1637,7 +1637,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1650,7 +1650,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress7" }, { @@ -1658,7 +1658,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1666,7 +1666,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1679,7 +1679,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress8" }, { @@ -1687,7 +1687,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1695,7 +1695,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1708,7 +1708,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress9" }, { @@ -1716,7 +1716,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1724,7 +1724,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1744,7 +1744,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown0" }, { @@ -1752,7 +1752,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1760,7 +1760,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1773,7 +1773,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown1" }, { @@ -1781,7 +1781,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1789,7 +1789,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1802,7 +1802,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown2" }, { @@ -1810,7 +1810,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1818,7 +1818,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1831,7 +1831,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown3" }, { @@ -1839,7 +1839,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1847,7 +1847,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1860,7 +1860,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown4" }, { @@ -1868,7 +1868,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1876,7 +1876,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1889,7 +1889,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown5" }, { @@ -1897,7 +1897,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1905,7 +1905,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1918,7 +1918,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown6" }, { @@ -1926,7 +1926,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1934,7 +1934,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1947,7 +1947,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown7" }, { @@ -1955,7 +1955,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1963,7 +1963,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1976,7 +1976,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown8" }, { @@ -1984,7 +1984,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1992,7 +1992,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2005,7 +2005,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown9" }, { @@ -2013,7 +2013,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2021,7 +2021,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2041,7 +2041,7 @@ "wasm": { "execute": { "contract_addr": "router", - "msg": "eyJyb3V0ZV9tZXNzYWdlcyI6W3siY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPblNvdXJjZUNoYWluMCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiU3VjY2VlZGVkT25Tb3VyY2VDaGFpbjEifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxIn0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IlN1Y2NlZWRlZE9uU291cmNlQ2hhaW4yIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMiJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPblNvdXJjZUNoYWluMyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiU3VjY2VlZGVkT25Tb3VyY2VDaGFpbjQifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IlN1Y2NlZWRlZE9uU291cmNlQ2hhaW41In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNSJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPblNvdXJjZUNoYWluNiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiU3VjY2VlZGVkT25Tb3VyY2VDaGFpbjcifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IlN1Y2NlZWRlZE9uU291cmNlQ2hhaW44In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwOCJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJTdWNjZWVkZWRPblNvdXJjZUNoYWluOSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkifV19", + "msg": "eyJyb3V0ZV9tZXNzYWdlcyI6W3siY2NfaWQiOnsic291cmNlX2NoYWluIjoibW9jay1jaGFpbiIsIm1lc3NhZ2VfaWQiOiJTdWNjZWVkZWRPblNvdXJjZUNoYWluMCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiU3VjY2VlZGVkT25Tb3VyY2VDaGFpbjEifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxIn0seyJjY19pZCI6eyJzb3VyY2VfY2hhaW4iOiJtb2NrLWNoYWluIiwibWVzc2FnZV9pZCI6IlN1Y2NlZWRlZE9uU291cmNlQ2hhaW4yIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMiJ9LHsiY2NfaWQiOnsic291cmNlX2NoYWluIjoibW9jay1jaGFpbiIsIm1lc3NhZ2VfaWQiOiJTdWNjZWVkZWRPblNvdXJjZUNoYWluMyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiU3VjY2VlZGVkT25Tb3VyY2VDaGFpbjQifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0In0seyJjY19pZCI6eyJzb3VyY2VfY2hhaW4iOiJtb2NrLWNoYWluIiwibWVzc2FnZV9pZCI6IlN1Y2NlZWRlZE9uU291cmNlQ2hhaW41In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNSJ9LHsiY2NfaWQiOnsic291cmNlX2NoYWluIjoibW9jay1jaGFpbiIsIm1lc3NhZ2VfaWQiOiJTdWNjZWVkZWRPblNvdXJjZUNoYWluNiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiU3VjY2VlZGVkT25Tb3VyY2VDaGFpbjcifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3In0seyJjY19pZCI6eyJzb3VyY2VfY2hhaW4iOiJtb2NrLWNoYWluIiwibWVzc2FnZV9pZCI6IlN1Y2NlZWRlZE9uU291cmNlQ2hhaW44In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwOCJ9LHsiY2NfaWQiOnsic291cmNlX2NoYWluIjoibW9jay1jaGFpbiIsIm1lc3NhZ2VfaWQiOiJTdWNjZWVkZWRPblNvdXJjZUNoYWluOSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkifV19", "funds": [] } } @@ -2056,7 +2056,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain0" }, { @@ -2064,7 +2064,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2072,7 +2072,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2085,7 +2085,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain1" }, { @@ -2093,7 +2093,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2101,7 +2101,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2114,7 +2114,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain2" }, { @@ -2122,7 +2122,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2130,7 +2130,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2143,7 +2143,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain3" }, { @@ -2151,7 +2151,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2159,7 +2159,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2172,7 +2172,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain4" }, { @@ -2180,7 +2180,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2188,7 +2188,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2201,7 +2201,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain5" }, { @@ -2209,7 +2209,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2217,7 +2217,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2230,7 +2230,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain6" }, { @@ -2238,7 +2238,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2246,7 +2246,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2259,7 +2259,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain7" }, { @@ -2267,7 +2267,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2275,7 +2275,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2288,7 +2288,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain8" }, { @@ -2296,7 +2296,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2304,7 +2304,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2317,7 +2317,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain9" }, { @@ -2325,7 +2325,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2333,7 +2333,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2346,7 +2346,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain0" }, { @@ -2354,7 +2354,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2362,7 +2362,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2375,7 +2375,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain1" }, { @@ -2383,7 +2383,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2391,7 +2391,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2404,7 +2404,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain2" }, { @@ -2412,7 +2412,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2420,7 +2420,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2433,7 +2433,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain3" }, { @@ -2441,7 +2441,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2449,7 +2449,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2462,7 +2462,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain4" }, { @@ -2470,7 +2470,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2478,7 +2478,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2491,7 +2491,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain5" }, { @@ -2499,7 +2499,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2507,7 +2507,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2520,7 +2520,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain6" }, { @@ -2528,7 +2528,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2536,7 +2536,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2549,7 +2549,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain7" }, { @@ -2557,7 +2557,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2565,7 +2565,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2578,7 +2578,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain8" }, { @@ -2586,7 +2586,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2594,7 +2594,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2607,7 +2607,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain9" }, { @@ -2615,7 +2615,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2623,7 +2623,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2636,7 +2636,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain0" }, { @@ -2644,7 +2644,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2652,7 +2652,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2665,7 +2665,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain1" }, { @@ -2673,7 +2673,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2681,7 +2681,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2694,7 +2694,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain2" }, { @@ -2702,7 +2702,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2710,7 +2710,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2723,7 +2723,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain3" }, { @@ -2731,7 +2731,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2739,7 +2739,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2752,7 +2752,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain4" }, { @@ -2760,7 +2760,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2768,7 +2768,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2781,7 +2781,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain5" }, { @@ -2789,7 +2789,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2797,7 +2797,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2810,7 +2810,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain6" }, { @@ -2818,7 +2818,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2826,7 +2826,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2839,7 +2839,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain7" }, { @@ -2847,7 +2847,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2855,7 +2855,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2868,7 +2868,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain8" }, { @@ -2876,7 +2876,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2884,7 +2884,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2897,7 +2897,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain9" }, { @@ -2905,7 +2905,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2913,7 +2913,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2926,7 +2926,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify0" }, { @@ -2934,7 +2934,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2942,7 +2942,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2955,7 +2955,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify1" }, { @@ -2963,7 +2963,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2971,7 +2971,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2984,7 +2984,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify2" }, { @@ -2992,7 +2992,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3000,7 +3000,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3013,7 +3013,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify3" }, { @@ -3021,7 +3021,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3029,7 +3029,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3042,7 +3042,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify4" }, { @@ -3050,7 +3050,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3058,7 +3058,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3071,7 +3071,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify5" }, { @@ -3079,7 +3079,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3087,7 +3087,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3100,7 +3100,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify6" }, { @@ -3108,7 +3108,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3116,7 +3116,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3129,7 +3129,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify7" }, { @@ -3137,7 +3137,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3145,7 +3145,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3158,7 +3158,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify8" }, { @@ -3166,7 +3166,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3174,7 +3174,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3187,7 +3187,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify9" }, { @@ -3195,7 +3195,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3203,7 +3203,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3216,7 +3216,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress0" }, { @@ -3224,7 +3224,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3232,7 +3232,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3245,7 +3245,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress1" }, { @@ -3253,7 +3253,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3261,7 +3261,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3274,7 +3274,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress2" }, { @@ -3282,7 +3282,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3290,7 +3290,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3303,7 +3303,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress3" }, { @@ -3311,7 +3311,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3319,7 +3319,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3332,7 +3332,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress4" }, { @@ -3340,7 +3340,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3348,7 +3348,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3361,7 +3361,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress5" }, { @@ -3369,7 +3369,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3377,7 +3377,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3390,7 +3390,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress6" }, { @@ -3398,7 +3398,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3406,7 +3406,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3419,7 +3419,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress7" }, { @@ -3427,7 +3427,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3435,7 +3435,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3448,7 +3448,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress8" }, { @@ -3456,7 +3456,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3464,7 +3464,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3477,7 +3477,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress9" }, { @@ -3485,7 +3485,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3493,7 +3493,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3506,7 +3506,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown0" }, { @@ -3514,7 +3514,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3522,7 +3522,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3535,7 +3535,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown1" }, { @@ -3543,7 +3543,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3551,7 +3551,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3564,7 +3564,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown2" }, { @@ -3572,7 +3572,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3580,7 +3580,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3593,7 +3593,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown3" }, { @@ -3601,7 +3601,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3609,7 +3609,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3622,7 +3622,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown4" }, { @@ -3630,7 +3630,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3638,7 +3638,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3651,7 +3651,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown5" }, { @@ -3659,7 +3659,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3667,7 +3667,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3680,7 +3680,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown6" }, { @@ -3688,7 +3688,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3696,7 +3696,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3709,7 +3709,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown7" }, { @@ -3717,7 +3717,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3725,7 +3725,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3738,7 +3738,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown8" }, { @@ -3746,7 +3746,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3754,7 +3754,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3767,7 +3767,7 @@ "type": "unfit_for_routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown9" }, { @@ -3775,7 +3775,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3783,7 +3783,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { diff --git a/contracts/gateway/tests/test_route_outgoing.json b/contracts/gateway/tests/test_route_outgoing.json index 030d0a533..6590430c4 100644 --- a/contracts/gateway/tests/test_route_outgoing.json +++ b/contracts/gateway/tests/test_route_outgoing.json @@ -13,7 +13,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain0" }, { @@ -21,7 +21,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -29,7 +29,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -49,7 +49,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain0" }, { @@ -57,7 +57,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -65,7 +65,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -85,7 +85,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain0" }, { @@ -93,7 +93,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -101,7 +101,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -121,7 +121,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify0" }, { @@ -129,7 +129,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -137,7 +137,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -157,7 +157,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress0" }, { @@ -165,7 +165,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -173,7 +173,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -193,7 +193,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown0" }, { @@ -201,7 +201,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -209,7 +209,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -229,7 +229,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain0" }, { @@ -237,7 +237,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -245,7 +245,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -258,7 +258,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain1" }, { @@ -266,7 +266,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -274,7 +274,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -287,7 +287,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain2" }, { @@ -295,7 +295,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -303,7 +303,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -316,7 +316,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain3" }, { @@ -324,7 +324,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -332,7 +332,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -345,7 +345,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain4" }, { @@ -353,7 +353,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -361,7 +361,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -374,7 +374,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain5" }, { @@ -382,7 +382,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -390,7 +390,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -403,7 +403,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain6" }, { @@ -411,7 +411,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -419,7 +419,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -432,7 +432,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain7" }, { @@ -440,7 +440,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -448,7 +448,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -461,7 +461,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain8" }, { @@ -469,7 +469,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -477,7 +477,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -490,7 +490,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain9" }, { @@ -498,7 +498,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -506,7 +506,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -526,7 +526,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain0" }, { @@ -534,7 +534,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -542,7 +542,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -555,7 +555,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain1" }, { @@ -563,7 +563,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -571,7 +571,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -584,7 +584,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain2" }, { @@ -592,7 +592,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -600,7 +600,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -613,7 +613,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain3" }, { @@ -621,7 +621,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -629,7 +629,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -642,7 +642,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain4" }, { @@ -650,7 +650,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -658,7 +658,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -671,7 +671,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain5" }, { @@ -679,7 +679,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -687,7 +687,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -700,7 +700,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain6" }, { @@ -708,7 +708,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -716,7 +716,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -729,7 +729,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain7" }, { @@ -737,7 +737,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -745,7 +745,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -758,7 +758,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain8" }, { @@ -766,7 +766,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -774,7 +774,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -787,7 +787,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain9" }, { @@ -795,7 +795,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -803,7 +803,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -823,7 +823,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain0" }, { @@ -831,7 +831,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -839,7 +839,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -852,7 +852,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain1" }, { @@ -860,7 +860,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -868,7 +868,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -881,7 +881,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain2" }, { @@ -889,7 +889,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -897,7 +897,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -910,7 +910,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain3" }, { @@ -918,7 +918,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -926,7 +926,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -939,7 +939,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain4" }, { @@ -947,7 +947,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -955,7 +955,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -968,7 +968,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain5" }, { @@ -976,7 +976,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -984,7 +984,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -997,7 +997,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain6" }, { @@ -1005,7 +1005,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1013,7 +1013,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1026,7 +1026,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain7" }, { @@ -1034,7 +1034,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1042,7 +1042,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1055,7 +1055,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain8" }, { @@ -1063,7 +1063,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1071,7 +1071,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1084,7 +1084,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain9" }, { @@ -1092,7 +1092,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1100,7 +1100,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1120,7 +1120,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify0" }, { @@ -1128,7 +1128,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1136,7 +1136,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1149,7 +1149,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify1" }, { @@ -1157,7 +1157,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1165,7 +1165,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1178,7 +1178,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify2" }, { @@ -1186,7 +1186,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1194,7 +1194,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1207,7 +1207,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify3" }, { @@ -1215,7 +1215,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1223,7 +1223,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1236,7 +1236,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify4" }, { @@ -1244,7 +1244,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1252,7 +1252,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1265,7 +1265,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify5" }, { @@ -1273,7 +1273,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1281,7 +1281,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1294,7 +1294,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify6" }, { @@ -1302,7 +1302,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1310,7 +1310,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1323,7 +1323,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify7" }, { @@ -1331,7 +1331,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1339,7 +1339,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1352,7 +1352,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify8" }, { @@ -1360,7 +1360,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1368,7 +1368,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1381,7 +1381,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify9" }, { @@ -1389,7 +1389,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1397,7 +1397,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1417,7 +1417,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress0" }, { @@ -1425,7 +1425,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1433,7 +1433,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1446,7 +1446,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress1" }, { @@ -1454,7 +1454,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1462,7 +1462,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1475,7 +1475,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress2" }, { @@ -1483,7 +1483,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1491,7 +1491,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1504,7 +1504,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress3" }, { @@ -1512,7 +1512,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1520,7 +1520,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1533,7 +1533,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress4" }, { @@ -1541,7 +1541,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1549,7 +1549,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1562,7 +1562,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress5" }, { @@ -1570,7 +1570,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1578,7 +1578,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1591,7 +1591,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress6" }, { @@ -1599,7 +1599,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1607,7 +1607,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1620,7 +1620,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress7" }, { @@ -1628,7 +1628,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1636,7 +1636,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1649,7 +1649,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress8" }, { @@ -1657,7 +1657,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1665,7 +1665,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1678,7 +1678,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress9" }, { @@ -1686,7 +1686,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1694,7 +1694,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1714,7 +1714,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown0" }, { @@ -1722,7 +1722,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1730,7 +1730,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1743,7 +1743,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown1" }, { @@ -1751,7 +1751,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1759,7 +1759,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1772,7 +1772,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown2" }, { @@ -1780,7 +1780,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1788,7 +1788,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1801,7 +1801,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown3" }, { @@ -1809,7 +1809,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1817,7 +1817,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1830,7 +1830,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown4" }, { @@ -1838,7 +1838,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1846,7 +1846,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1859,7 +1859,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown5" }, { @@ -1867,7 +1867,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1875,7 +1875,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1888,7 +1888,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown6" }, { @@ -1896,7 +1896,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1904,7 +1904,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1917,7 +1917,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown7" }, { @@ -1925,7 +1925,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1933,7 +1933,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1946,7 +1946,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown8" }, { @@ -1954,7 +1954,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1962,7 +1962,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1975,7 +1975,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown9" }, { @@ -1983,7 +1983,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1991,7 +1991,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2011,7 +2011,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain0" }, { @@ -2019,7 +2019,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2027,7 +2027,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2040,7 +2040,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain1" }, { @@ -2048,7 +2048,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2056,7 +2056,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2069,7 +2069,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain2" }, { @@ -2077,7 +2077,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2085,7 +2085,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2098,7 +2098,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain3" }, { @@ -2106,7 +2106,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2114,7 +2114,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2127,7 +2127,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain4" }, { @@ -2135,7 +2135,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2143,7 +2143,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2156,7 +2156,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain5" }, { @@ -2164,7 +2164,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2172,7 +2172,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2185,7 +2185,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain6" }, { @@ -2193,7 +2193,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2201,7 +2201,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2214,7 +2214,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain7" }, { @@ -2222,7 +2222,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2230,7 +2230,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2243,7 +2243,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain8" }, { @@ -2251,7 +2251,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2259,7 +2259,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2272,7 +2272,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain9" }, { @@ -2280,7 +2280,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2288,7 +2288,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2301,7 +2301,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain0" }, { @@ -2309,7 +2309,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2317,7 +2317,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2330,7 +2330,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain1" }, { @@ -2338,7 +2338,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2346,7 +2346,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2359,7 +2359,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain2" }, { @@ -2367,7 +2367,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2375,7 +2375,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2388,7 +2388,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain3" }, { @@ -2396,7 +2396,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2404,7 +2404,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2417,7 +2417,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain4" }, { @@ -2425,7 +2425,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2433,7 +2433,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2446,7 +2446,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain5" }, { @@ -2454,7 +2454,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2462,7 +2462,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2475,7 +2475,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain6" }, { @@ -2483,7 +2483,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2491,7 +2491,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2504,7 +2504,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain7" }, { @@ -2512,7 +2512,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2520,7 +2520,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2533,7 +2533,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain8" }, { @@ -2541,7 +2541,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2549,7 +2549,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2562,7 +2562,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain9" }, { @@ -2570,7 +2570,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2578,7 +2578,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2591,7 +2591,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain0" }, { @@ -2599,7 +2599,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2607,7 +2607,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2620,7 +2620,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain1" }, { @@ -2628,7 +2628,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2636,7 +2636,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2649,7 +2649,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain2" }, { @@ -2657,7 +2657,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2665,7 +2665,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2678,7 +2678,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain3" }, { @@ -2686,7 +2686,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2694,7 +2694,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2707,7 +2707,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain4" }, { @@ -2715,7 +2715,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2723,7 +2723,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2736,7 +2736,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain5" }, { @@ -2744,7 +2744,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2752,7 +2752,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2765,7 +2765,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain6" }, { @@ -2773,7 +2773,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2781,7 +2781,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2794,7 +2794,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain7" }, { @@ -2802,7 +2802,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2810,7 +2810,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2823,7 +2823,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain8" }, { @@ -2831,7 +2831,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2839,7 +2839,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2852,7 +2852,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain9" }, { @@ -2860,7 +2860,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2868,7 +2868,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2881,7 +2881,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify0" }, { @@ -2889,7 +2889,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2897,7 +2897,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2910,7 +2910,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify1" }, { @@ -2918,7 +2918,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2926,7 +2926,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2939,7 +2939,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify2" }, { @@ -2947,7 +2947,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2955,7 +2955,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2968,7 +2968,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify3" }, { @@ -2976,7 +2976,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2984,7 +2984,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2997,7 +2997,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify4" }, { @@ -3005,7 +3005,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3013,7 +3013,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3026,7 +3026,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify5" }, { @@ -3034,7 +3034,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3042,7 +3042,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3055,7 +3055,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify6" }, { @@ -3063,7 +3063,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3071,7 +3071,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3084,7 +3084,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify7" }, { @@ -3092,7 +3092,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3100,7 +3100,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3113,7 +3113,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify8" }, { @@ -3121,7 +3121,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3129,7 +3129,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3142,7 +3142,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify9" }, { @@ -3150,7 +3150,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3158,7 +3158,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3171,7 +3171,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress0" }, { @@ -3179,7 +3179,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3187,7 +3187,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3200,7 +3200,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress1" }, { @@ -3208,7 +3208,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3216,7 +3216,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3229,7 +3229,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress2" }, { @@ -3237,7 +3237,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3245,7 +3245,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3258,7 +3258,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress3" }, { @@ -3266,7 +3266,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3274,7 +3274,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3287,7 +3287,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress4" }, { @@ -3295,7 +3295,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3303,7 +3303,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3316,7 +3316,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress5" }, { @@ -3324,7 +3324,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3332,7 +3332,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3345,7 +3345,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress6" }, { @@ -3353,7 +3353,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3361,7 +3361,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3374,7 +3374,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress7" }, { @@ -3382,7 +3382,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3390,7 +3390,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3403,7 +3403,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress8" }, { @@ -3411,7 +3411,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3419,7 +3419,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3432,7 +3432,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress9" }, { @@ -3440,7 +3440,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3448,7 +3448,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3461,7 +3461,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown0" }, { @@ -3469,7 +3469,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3477,7 +3477,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3490,7 +3490,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown1" }, { @@ -3498,7 +3498,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3506,7 +3506,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3519,7 +3519,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown2" }, { @@ -3527,7 +3527,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3535,7 +3535,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3548,7 +3548,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown3" }, { @@ -3556,7 +3556,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3564,7 +3564,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3577,7 +3577,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown4" }, { @@ -3585,7 +3585,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3593,7 +3593,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3606,7 +3606,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown5" }, { @@ -3614,7 +3614,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3622,7 +3622,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3635,7 +3635,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown6" }, { @@ -3643,7 +3643,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3651,7 +3651,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3664,7 +3664,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown7" }, { @@ -3672,7 +3672,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3680,7 +3680,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3693,7 +3693,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown8" }, { @@ -3701,7 +3701,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3709,7 +3709,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3722,7 +3722,7 @@ "type": "routing", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown9" }, { @@ -3730,7 +3730,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3738,7 +3738,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { diff --git a/contracts/gateway/tests/test_verify.json b/contracts/gateway/tests/test_verify.json index 0b37c440e..1cf376c00 100644 --- a/contracts/gateway/tests/test_verify.json +++ b/contracts/gateway/tests/test_verify.json @@ -13,7 +13,7 @@ "type": "already_verified", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain0" }, { @@ -21,7 +21,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -29,7 +29,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -49,7 +49,7 @@ "type": "already_rejected", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain0" }, { @@ -57,7 +57,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -65,7 +65,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -85,7 +85,7 @@ "wasm": { "execute": { "contract_addr": "verifier", - "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOnsibWVzc2FnZXMiOlt7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluMCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifV19fQ==", + "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOlt7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluMCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifV19", "funds": [] } } @@ -100,7 +100,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain0" }, { @@ -108,7 +108,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -116,7 +116,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -136,7 +136,7 @@ "wasm": { "execute": { "contract_addr": "verifier", - "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOnsibWVzc2FnZXMiOlt7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnkwIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCJ9XX19", + "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOlt7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiRmFpbGVkVG9WZXJpZnkwIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCJ9XX0=", "funds": [] } } @@ -151,7 +151,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify0" }, { @@ -159,7 +159,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -167,7 +167,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -187,7 +187,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress0" }, { @@ -195,7 +195,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -203,7 +203,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -223,7 +223,7 @@ "wasm": { "execute": { "contract_addr": "verifier", - "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOnsibWVzc2FnZXMiOlt7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiVW5rbm93bjAifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn1dfX0=", + "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOlt7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiVW5rbm93bjAifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn1dfQ==", "funds": [] } } @@ -238,7 +238,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown0" }, { @@ -246,7 +246,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -254,7 +254,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -274,7 +274,7 @@ "type": "already_verified", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain0" }, { @@ -282,7 +282,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -290,7 +290,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -303,7 +303,7 @@ "type": "already_verified", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain1" }, { @@ -311,7 +311,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -319,7 +319,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -332,7 +332,7 @@ "type": "already_verified", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain2" }, { @@ -340,7 +340,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -348,7 +348,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -361,7 +361,7 @@ "type": "already_verified", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain3" }, { @@ -369,7 +369,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -377,7 +377,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -390,7 +390,7 @@ "type": "already_verified", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain4" }, { @@ -398,7 +398,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -406,7 +406,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -419,7 +419,7 @@ "type": "already_verified", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain5" }, { @@ -427,7 +427,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -435,7 +435,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -448,7 +448,7 @@ "type": "already_verified", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain6" }, { @@ -456,7 +456,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -464,7 +464,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -477,7 +477,7 @@ "type": "already_verified", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain7" }, { @@ -485,7 +485,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -493,7 +493,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -506,7 +506,7 @@ "type": "already_verified", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain8" }, { @@ -514,7 +514,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -522,7 +522,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -535,7 +535,7 @@ "type": "already_verified", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain9" }, { @@ -543,7 +543,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -551,7 +551,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -571,7 +571,7 @@ "type": "already_rejected", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain0" }, { @@ -579,7 +579,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -587,7 +587,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -600,7 +600,7 @@ "type": "already_rejected", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain1" }, { @@ -608,7 +608,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -616,7 +616,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -629,7 +629,7 @@ "type": "already_rejected", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain2" }, { @@ -637,7 +637,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -645,7 +645,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -658,7 +658,7 @@ "type": "already_rejected", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain3" }, { @@ -666,7 +666,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -674,7 +674,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -687,7 +687,7 @@ "type": "already_rejected", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain4" }, { @@ -695,7 +695,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -703,7 +703,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -716,7 +716,7 @@ "type": "already_rejected", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain5" }, { @@ -724,7 +724,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -732,7 +732,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -745,7 +745,7 @@ "type": "already_rejected", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain6" }, { @@ -753,7 +753,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -761,7 +761,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -774,7 +774,7 @@ "type": "already_rejected", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain7" }, { @@ -782,7 +782,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -790,7 +790,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -803,7 +803,7 @@ "type": "already_rejected", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain8" }, { @@ -811,7 +811,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -819,7 +819,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -832,7 +832,7 @@ "type": "already_rejected", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain9" }, { @@ -840,7 +840,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -848,7 +848,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -868,7 +868,7 @@ "wasm": { "execute": { "contract_addr": "verifier", - "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOnsibWVzc2FnZXMiOlt7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluMCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluMSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluMiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluMyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluNCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluNSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluNiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluNyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluOCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluOSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkifV19fQ==", + "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOlt7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluMCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluMSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluMiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluMyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluNCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluNSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluNiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluNyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluOCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluOSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkifV19", "funds": [] } } @@ -883,7 +883,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain0" }, { @@ -891,7 +891,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -899,7 +899,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -912,7 +912,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain1" }, { @@ -920,7 +920,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -928,7 +928,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -941,7 +941,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain2" }, { @@ -949,7 +949,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -957,7 +957,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -970,7 +970,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain3" }, { @@ -978,7 +978,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -986,7 +986,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -999,7 +999,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain4" }, { @@ -1007,7 +1007,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1015,7 +1015,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1028,7 +1028,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain5" }, { @@ -1036,7 +1036,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1044,7 +1044,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1057,7 +1057,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain6" }, { @@ -1065,7 +1065,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1073,7 +1073,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1086,7 +1086,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain7" }, { @@ -1094,7 +1094,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1102,7 +1102,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1115,7 +1115,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain8" }, { @@ -1123,7 +1123,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1131,7 +1131,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1144,7 +1144,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain9" }, { @@ -1152,7 +1152,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1160,7 +1160,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1180,7 +1180,7 @@ "wasm": { "execute": { "contract_addr": "verifier", - "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOnsibWVzc2FnZXMiOlt7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnkwIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJGYWlsZWRUb1ZlcmlmeTEifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxIn0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IkZhaWxlZFRvVmVyaWZ5MiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnkzIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMyJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJGYWlsZWRUb1ZlcmlmeTQifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IkZhaWxlZFRvVmVyaWZ5NSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk2In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNiJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJGYWlsZWRUb1ZlcmlmeTcifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IkZhaWxlZFRvVmVyaWZ5OCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk5In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOSJ9XX19", + "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOlt7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiRmFpbGVkVG9WZXJpZnkwIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCJ9LHsiY2NfaWQiOnsic291cmNlX2NoYWluIjoibW9jay1jaGFpbiIsIm1lc3NhZ2VfaWQiOiJGYWlsZWRUb1ZlcmlmeTEifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxIn0seyJjY19pZCI6eyJzb3VyY2VfY2hhaW4iOiJtb2NrLWNoYWluIiwibWVzc2FnZV9pZCI6IkZhaWxlZFRvVmVyaWZ5MiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiRmFpbGVkVG9WZXJpZnkzIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMyJ9LHsiY2NfaWQiOnsic291cmNlX2NoYWluIjoibW9jay1jaGFpbiIsIm1lc3NhZ2VfaWQiOiJGYWlsZWRUb1ZlcmlmeTQifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0In0seyJjY19pZCI6eyJzb3VyY2VfY2hhaW4iOiJtb2NrLWNoYWluIiwibWVzc2FnZV9pZCI6IkZhaWxlZFRvVmVyaWZ5NSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiRmFpbGVkVG9WZXJpZnk2In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNiJ9LHsiY2NfaWQiOnsic291cmNlX2NoYWluIjoibW9jay1jaGFpbiIsIm1lc3NhZ2VfaWQiOiJGYWlsZWRUb1ZlcmlmeTcifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3In0seyJjY19pZCI6eyJzb3VyY2VfY2hhaW4iOiJtb2NrLWNoYWluIiwibWVzc2FnZV9pZCI6IkZhaWxlZFRvVmVyaWZ5OCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiRmFpbGVkVG9WZXJpZnk5In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOSJ9XX0=", "funds": [] } } @@ -1195,7 +1195,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify0" }, { @@ -1203,7 +1203,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1211,7 +1211,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1224,7 +1224,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify1" }, { @@ -1232,7 +1232,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1240,7 +1240,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1253,7 +1253,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify2" }, { @@ -1261,7 +1261,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1269,7 +1269,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1282,7 +1282,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify3" }, { @@ -1290,7 +1290,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1298,7 +1298,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1311,7 +1311,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify4" }, { @@ -1319,7 +1319,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1327,7 +1327,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1340,7 +1340,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify5" }, { @@ -1348,7 +1348,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1356,7 +1356,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1369,7 +1369,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify6" }, { @@ -1377,7 +1377,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1385,7 +1385,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1398,7 +1398,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify7" }, { @@ -1406,7 +1406,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1414,7 +1414,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1427,7 +1427,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify8" }, { @@ -1435,7 +1435,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1443,7 +1443,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1456,7 +1456,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify9" }, { @@ -1464,7 +1464,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1472,7 +1472,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1492,7 +1492,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress0" }, { @@ -1500,7 +1500,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1508,7 +1508,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1521,7 +1521,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress1" }, { @@ -1529,7 +1529,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1537,7 +1537,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1550,7 +1550,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress2" }, { @@ -1558,7 +1558,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1566,7 +1566,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1579,7 +1579,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress3" }, { @@ -1587,7 +1587,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1595,7 +1595,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1608,7 +1608,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress4" }, { @@ -1616,7 +1616,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1624,7 +1624,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1637,7 +1637,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress5" }, { @@ -1645,7 +1645,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1653,7 +1653,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1666,7 +1666,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress6" }, { @@ -1674,7 +1674,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1682,7 +1682,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1695,7 +1695,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress7" }, { @@ -1703,7 +1703,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1711,7 +1711,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1724,7 +1724,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress8" }, { @@ -1732,7 +1732,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1740,7 +1740,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1753,7 +1753,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress9" }, { @@ -1761,7 +1761,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1769,7 +1769,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1789,7 +1789,7 @@ "wasm": { "execute": { "contract_addr": "verifier", - "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOnsibWVzc2FnZXMiOlt7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiVW5rbm93bjAifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IlVua25vd24xIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMSJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJVbmtub3duMiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiVW5rbm93bjMifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzIn0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IlVua25vd240In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNCJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJVbmtub3duNSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiVW5rbm93bjYifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IlVua25vd243In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNyJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJVbmtub3duOCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiVW5rbm93bjkifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5In1dfX0=", + "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOlt7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiVW5rbm93bjAifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn0seyJjY19pZCI6eyJzb3VyY2VfY2hhaW4iOiJtb2NrLWNoYWluIiwibWVzc2FnZV9pZCI6IlVua25vd24xIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMSJ9LHsiY2NfaWQiOnsic291cmNlX2NoYWluIjoibW9jay1jaGFpbiIsIm1lc3NhZ2VfaWQiOiJVbmtub3duMiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiVW5rbm93bjMifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzIn0seyJjY19pZCI6eyJzb3VyY2VfY2hhaW4iOiJtb2NrLWNoYWluIiwibWVzc2FnZV9pZCI6IlVua25vd240In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNCJ9LHsiY2NfaWQiOnsic291cmNlX2NoYWluIjoibW9jay1jaGFpbiIsIm1lc3NhZ2VfaWQiOiJVbmtub3duNSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiVW5rbm93bjYifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2In0seyJjY19pZCI6eyJzb3VyY2VfY2hhaW4iOiJtb2NrLWNoYWluIiwibWVzc2FnZV9pZCI6IlVua25vd243In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNyJ9LHsiY2NfaWQiOnsic291cmNlX2NoYWluIjoibW9jay1jaGFpbiIsIm1lc3NhZ2VfaWQiOiJVbmtub3duOCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiVW5rbm93bjkifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5In1dfQ==", "funds": [] } } @@ -1804,7 +1804,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown0" }, { @@ -1812,7 +1812,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1820,7 +1820,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1833,7 +1833,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown1" }, { @@ -1841,7 +1841,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1849,7 +1849,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1862,7 +1862,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown2" }, { @@ -1870,7 +1870,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1878,7 +1878,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1891,7 +1891,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown3" }, { @@ -1899,7 +1899,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1907,7 +1907,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1920,7 +1920,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown4" }, { @@ -1928,7 +1928,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1936,7 +1936,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1949,7 +1949,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown5" }, { @@ -1957,7 +1957,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1965,7 +1965,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -1978,7 +1978,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown6" }, { @@ -1986,7 +1986,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -1994,7 +1994,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2007,7 +2007,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown7" }, { @@ -2015,7 +2015,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2023,7 +2023,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2036,7 +2036,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown8" }, { @@ -2044,7 +2044,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2052,7 +2052,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2065,7 +2065,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown9" }, { @@ -2073,7 +2073,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2081,7 +2081,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2101,7 +2101,7 @@ "wasm": { "execute": { "contract_addr": "verifier", - "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOnsibWVzc2FnZXMiOlt7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluMCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluMSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluMiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluMyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluNCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluNSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluNiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluNyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluOCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluOSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnkwIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJGYWlsZWRUb1ZlcmlmeTEifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxIn0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IkZhaWxlZFRvVmVyaWZ5MiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnkzIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMyJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJGYWlsZWRUb1ZlcmlmeTQifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IkZhaWxlZFRvVmVyaWZ5NSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk2In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNiJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJGYWlsZWRUb1ZlcmlmeTcifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IkZhaWxlZFRvVmVyaWZ5OCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiRmFpbGVkVG9WZXJpZnk5In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOSJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJVbmtub3duMCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiVW5rbm93bjEifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxIn0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IlVua25vd24yIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMiJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJVbmtub3duMyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiVW5rbm93bjQifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IlVua25vd241In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNSJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJVbmtub3duNiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYifSx7ImNjX2lkIjp7ImNoYWluIjoibW9jay1jaGFpbiIsImlkIjoiVW5rbm93bjcifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3In0seyJjY19pZCI6eyJjaGFpbiI6Im1vY2stY2hhaW4iLCJpZCI6IlVua25vd244In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwOCJ9LHsiY2NfaWQiOnsiY2hhaW4iOiJtb2NrLWNoYWluIiwiaWQiOiJVbmtub3duOSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkifV19fQ==", + "msg": "eyJ2ZXJpZnlfbWVzc2FnZXMiOlt7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluMCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluMSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluMiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluMyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluNCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluNSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluNiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluNyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluOCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiTm90Rm91bmRPblNvdXJjZUNoYWluOSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiRmFpbGVkVG9WZXJpZnkwIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCJ9LHsiY2NfaWQiOnsic291cmNlX2NoYWluIjoibW9jay1jaGFpbiIsIm1lc3NhZ2VfaWQiOiJGYWlsZWRUb1ZlcmlmeTEifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxIn0seyJjY19pZCI6eyJzb3VyY2VfY2hhaW4iOiJtb2NrLWNoYWluIiwibWVzc2FnZV9pZCI6IkZhaWxlZFRvVmVyaWZ5MiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiRmFpbGVkVG9WZXJpZnkzIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMyJ9LHsiY2NfaWQiOnsic291cmNlX2NoYWluIjoibW9jay1jaGFpbiIsIm1lc3NhZ2VfaWQiOiJGYWlsZWRUb1ZlcmlmeTQifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0In0seyJjY19pZCI6eyJzb3VyY2VfY2hhaW4iOiJtb2NrLWNoYWluIiwibWVzc2FnZV9pZCI6IkZhaWxlZFRvVmVyaWZ5NSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiRmFpbGVkVG9WZXJpZnk2In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNiJ9LHsiY2NfaWQiOnsic291cmNlX2NoYWluIjoibW9jay1jaGFpbiIsIm1lc3NhZ2VfaWQiOiJGYWlsZWRUb1ZlcmlmeTcifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3In0seyJjY19pZCI6eyJzb3VyY2VfY2hhaW4iOiJtb2NrLWNoYWluIiwibWVzc2FnZV9pZCI6IkZhaWxlZFRvVmVyaWZ5OCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiRmFpbGVkVG9WZXJpZnk5In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOSJ9LHsiY2NfaWQiOnsic291cmNlX2NoYWluIjoibW9jay1jaGFpbiIsIm1lc3NhZ2VfaWQiOiJVbmtub3duMCJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiVW5rbm93bjEifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxIn0seyJjY19pZCI6eyJzb3VyY2VfY2hhaW4iOiJtb2NrLWNoYWluIiwibWVzc2FnZV9pZCI6IlVua25vd24yIn0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMiJ9LHsiY2NfaWQiOnsic291cmNlX2NoYWluIjoibW9jay1jaGFpbiIsIm1lc3NhZ2VfaWQiOiJVbmtub3duMyJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiVW5rbm93bjQifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0MDQwNDA0In0seyJjY19pZCI6eyJzb3VyY2VfY2hhaW4iOiJtb2NrLWNoYWluIiwibWVzc2FnZV9pZCI6IlVua25vd241In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNTA1MDUwNSJ9LHsiY2NfaWQiOnsic291cmNlX2NoYWluIjoibW9jay1jaGFpbiIsIm1lc3NhZ2VfaWQiOiJVbmtub3duNiJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYwNjA2MDYifSx7ImNjX2lkIjp7InNvdXJjZV9jaGFpbiI6Im1vY2stY2hhaW4iLCJtZXNzYWdlX2lkIjoiVW5rbm93bjcifSwic291cmNlX2FkZHJlc3MiOiJpZGMiLCJkZXN0aW5hdGlvbl9jaGFpbiI6Im1vY2stY2hhaW4tMiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJpZGMiLCJwYXlsb2FkX2hhc2giOiIwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3MDcwNzA3In0seyJjY19pZCI6eyJzb3VyY2VfY2hhaW4iOiJtb2NrLWNoYWluIiwibWVzc2FnZV9pZCI6IlVua25vd244In0sInNvdXJjZV9hZGRyZXNzIjoiaWRjIiwiZGVzdGluYXRpb25fY2hhaW4iOiJtb2NrLWNoYWluLTIiLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiaWRjIiwicGF5bG9hZF9oYXNoIjoiMDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwODA4MDgwOCJ9LHsiY2NfaWQiOnsic291cmNlX2NoYWluIjoibW9jay1jaGFpbiIsIm1lc3NhZ2VfaWQiOiJVbmtub3duOSJ9LCJzb3VyY2VfYWRkcmVzcyI6ImlkYyIsImRlc3RpbmF0aW9uX2NoYWluIjoibW9jay1jaGFpbi0yIiwiZGVzdGluYXRpb25fYWRkcmVzcyI6ImlkYyIsInBheWxvYWRfaGFzaCI6IjA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkwOTA5MDkifV19", "funds": [] } } @@ -2116,7 +2116,7 @@ "type": "already_verified", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain0" }, { @@ -2124,7 +2124,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2132,7 +2132,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2145,7 +2145,7 @@ "type": "already_verified", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain1" }, { @@ -2153,7 +2153,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2161,7 +2161,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2174,7 +2174,7 @@ "type": "already_verified", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain2" }, { @@ -2182,7 +2182,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2190,7 +2190,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2203,7 +2203,7 @@ "type": "already_verified", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain3" }, { @@ -2211,7 +2211,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2219,7 +2219,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2232,7 +2232,7 @@ "type": "already_verified", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain4" }, { @@ -2240,7 +2240,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2248,7 +2248,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2261,7 +2261,7 @@ "type": "already_verified", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain5" }, { @@ -2269,7 +2269,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2277,7 +2277,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2290,7 +2290,7 @@ "type": "already_verified", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain6" }, { @@ -2298,7 +2298,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2306,7 +2306,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2319,7 +2319,7 @@ "type": "already_verified", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain7" }, { @@ -2327,7 +2327,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2335,7 +2335,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2348,7 +2348,7 @@ "type": "already_verified", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain8" }, { @@ -2356,7 +2356,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2364,7 +2364,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2377,7 +2377,7 @@ "type": "already_verified", "attributes": [ { - "key": "id", + "key": "message_id", "value": "SucceededOnSourceChain9" }, { @@ -2385,7 +2385,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2393,7 +2393,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2406,7 +2406,7 @@ "type": "already_rejected", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain0" }, { @@ -2414,7 +2414,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2422,7 +2422,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2435,7 +2435,7 @@ "type": "already_rejected", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain1" }, { @@ -2443,7 +2443,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2451,7 +2451,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2464,7 +2464,7 @@ "type": "already_rejected", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain2" }, { @@ -2472,7 +2472,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2480,7 +2480,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2493,7 +2493,7 @@ "type": "already_rejected", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain3" }, { @@ -2501,7 +2501,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2509,7 +2509,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2522,7 +2522,7 @@ "type": "already_rejected", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain4" }, { @@ -2530,7 +2530,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2538,7 +2538,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2551,7 +2551,7 @@ "type": "already_rejected", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain5" }, { @@ -2559,7 +2559,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2567,7 +2567,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2580,7 +2580,7 @@ "type": "already_rejected", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain6" }, { @@ -2588,7 +2588,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2596,7 +2596,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2609,7 +2609,7 @@ "type": "already_rejected", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain7" }, { @@ -2617,7 +2617,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2625,7 +2625,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2638,7 +2638,7 @@ "type": "already_rejected", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain8" }, { @@ -2646,7 +2646,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2654,7 +2654,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2667,7 +2667,7 @@ "type": "already_rejected", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedOnSourceChain9" }, { @@ -2675,7 +2675,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2683,7 +2683,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2696,7 +2696,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain0" }, { @@ -2704,7 +2704,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2712,7 +2712,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2725,7 +2725,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain1" }, { @@ -2733,7 +2733,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2741,7 +2741,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2754,7 +2754,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain2" }, { @@ -2762,7 +2762,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2770,7 +2770,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2783,7 +2783,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain3" }, { @@ -2791,7 +2791,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2799,7 +2799,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2812,7 +2812,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain4" }, { @@ -2820,7 +2820,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2828,7 +2828,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2841,7 +2841,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain5" }, { @@ -2849,7 +2849,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2857,7 +2857,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2870,7 +2870,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain6" }, { @@ -2878,7 +2878,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2886,7 +2886,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2899,7 +2899,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain7" }, { @@ -2907,7 +2907,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2915,7 +2915,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2928,7 +2928,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain8" }, { @@ -2936,7 +2936,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2944,7 +2944,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2957,7 +2957,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "NotFoundOnSourceChain9" }, { @@ -2965,7 +2965,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -2973,7 +2973,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -2986,7 +2986,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify0" }, { @@ -2994,7 +2994,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3002,7 +3002,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3015,7 +3015,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify1" }, { @@ -3023,7 +3023,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3031,7 +3031,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3044,7 +3044,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify2" }, { @@ -3052,7 +3052,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3060,7 +3060,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3073,7 +3073,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify3" }, { @@ -3081,7 +3081,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3089,7 +3089,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3102,7 +3102,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify4" }, { @@ -3110,7 +3110,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3118,7 +3118,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3131,7 +3131,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify5" }, { @@ -3139,7 +3139,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3147,7 +3147,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3160,7 +3160,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify6" }, { @@ -3168,7 +3168,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3176,7 +3176,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3189,7 +3189,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify7" }, { @@ -3197,7 +3197,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3205,7 +3205,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3218,7 +3218,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify8" }, { @@ -3226,7 +3226,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3234,7 +3234,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3247,7 +3247,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "FailedToVerify9" }, { @@ -3255,7 +3255,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3263,7 +3263,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3276,7 +3276,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress0" }, { @@ -3284,7 +3284,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3292,7 +3292,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3305,7 +3305,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress1" }, { @@ -3313,7 +3313,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3321,7 +3321,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3334,7 +3334,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress2" }, { @@ -3342,7 +3342,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3350,7 +3350,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3363,7 +3363,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress3" }, { @@ -3371,7 +3371,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3379,7 +3379,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3392,7 +3392,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress4" }, { @@ -3400,7 +3400,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3408,7 +3408,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3421,7 +3421,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress5" }, { @@ -3429,7 +3429,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3437,7 +3437,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3450,7 +3450,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress6" }, { @@ -3458,7 +3458,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3466,7 +3466,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3479,7 +3479,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress7" }, { @@ -3487,7 +3487,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3495,7 +3495,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3508,7 +3508,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress8" }, { @@ -3516,7 +3516,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3524,7 +3524,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3537,7 +3537,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "InProgress9" }, { @@ -3545,7 +3545,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3553,7 +3553,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3566,7 +3566,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown0" }, { @@ -3574,7 +3574,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3582,7 +3582,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3595,7 +3595,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown1" }, { @@ -3603,7 +3603,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3611,7 +3611,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3624,7 +3624,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown2" }, { @@ -3632,7 +3632,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3640,7 +3640,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3653,7 +3653,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown3" }, { @@ -3661,7 +3661,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3669,7 +3669,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3682,7 +3682,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown4" }, { @@ -3690,7 +3690,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3698,7 +3698,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3711,7 +3711,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown5" }, { @@ -3719,7 +3719,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3727,7 +3727,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3740,7 +3740,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown6" }, { @@ -3748,7 +3748,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3756,7 +3756,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3769,7 +3769,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown7" }, { @@ -3777,7 +3777,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3785,7 +3785,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3798,7 +3798,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown8" }, { @@ -3806,7 +3806,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3814,7 +3814,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { @@ -3827,7 +3827,7 @@ "type": "verifying", "attributes": [ { - "key": "id", + "key": "message_id", "value": "Unknown9" }, { @@ -3835,7 +3835,7 @@ "value": "mock-chain" }, { - "key": "source_addresses", + "key": "source_address", "value": "idc" }, { @@ -3843,7 +3843,7 @@ "value": "mock-chain-2" }, { - "key": "destination_addresses", + "key": "destination_address", "value": "idc" }, { diff --git a/contracts/rewards/.cargo/config b/contracts/multisig-prover/.cargo/config.toml similarity index 100% rename from contracts/rewards/.cargo/config rename to contracts/multisig-prover/.cargo/config.toml diff --git a/contracts/multisig-prover/Cargo.toml b/contracts/multisig-prover/Cargo.toml index e50323090..396f28f74 100644 --- a/contracts/multisig-prover/Cargo.toml +++ b/contracts/multisig-prover/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "multisig-prover" -version = "0.5.0" +version = "1.0.0" rust-version = { workspace = true } -edition = "2021" +edition = { workspace = true } description = "Multisig prover contract" exclude = [ - # Those files are rust-optimizer artifacts. You might want to commit them for convenience, but they should not be part of the source code publication. - "contract.wasm", - "hash.txt", + # Those files are rust-optimizer artifacts. You might want to commit them for convenience, but they should not be part of the source code publication. + "contract.wasm", + "hash.txt", ] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -29,14 +29,11 @@ library = [] optimize = """docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/rust-optimizer:0.12.6 + cosmwasm/optimizer:0.16.0 """ [dependencies] -alloy-primitives = { version = "0.7.0", default-features = false } -alloy-sol-types = { version = "0.7.0", default-features = false, features = ["json"] } -axelar-wasm-std = { workspace = true } -axelar-wasm-std-derive = { workspace = true } +axelar-wasm-std = { workspace = true, features = ["derive"] } bcs = "0.1.5" coordinator = { workspace = true } cosmwasm-schema = { workspace = true } @@ -45,18 +42,23 @@ cw-storage-plus = { workspace = true } cw-utils = "1.0.1" cw2 = { workspace = true } error-stack = { workspace = true } -ethabi = { version = "18.0.0", default-features = false, features = [] } +ethers-contract = { workspace = true } +ethers-core = { workspace = true } +evm-gateway = { workspace = true } gateway = { workspace = true } gateway-api = { workspace = true } hex = { version = "0.4.3", default-features = false, features = [] } itertools = "0.11.0" -k256 = { version = "0.13.1", features = ["ecdsa"] } +k256 = { workspace = true } +msgs-derive = { workspace = true } multisig = { workspace = true, features = ["library"] } report = { workspace = true } router-api = { workspace = true } serde_json = "1.0.89" service-registry = { workspace = true } sha3 = { workspace = true } +stellar = { workspace = true } +sui-gateway = { workspace = true } thiserror = { workspace = true } voting-verifier = { workspace = true, features = ["library"] } @@ -64,8 +66,8 @@ voting-verifier = { workspace = true, features = ["library"] } anyhow = "1.0" cw-multi-test = "0.15.1" elliptic-curve = "0.13.5" -ethers = "2.0.8" generic-array = "0.14.7" +goldie = { workspace = true } prost = "0.12.4" [lints] diff --git a/contracts/multisig-prover/src/bin/schema.rs b/contracts/multisig-prover/src/bin/schema.rs index 8a58b5f6e..0dc35b5f9 100644 --- a/contracts/multisig-prover/src/bin/schema.rs +++ b/contracts/multisig-prover/src/bin/schema.rs @@ -1,5 +1,4 @@ use cosmwasm_schema::write_api; - use multisig_prover::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; fn main() { diff --git a/contracts/multisig-prover/src/contract.rs b/contracts/multisig-prover/src/contract.rs index 2bfbd92a8..81952e655 100644 --- a/contracts/multisig-prover/src/contract.rs +++ b/contracts/multisig-prover/src/contract.rs @@ -1,3 +1,4 @@ +use axelar_wasm_std::{address, permission_control}; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ @@ -5,13 +6,14 @@ use cosmwasm_std::{ }; use error_stack::ResultExt; -use crate::{ - error::ContractError, - execute, migrations, - msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, - query, reply, - state::{Config, CONFIG}, -}; +use crate::error::ContractError; +use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use crate::state::{Config, CONFIG}; + +mod execute; +mod migrations; +mod query; +mod reply; pub const START_MULTISIG_REPLY_ID: u64 = 1; @@ -24,46 +26,41 @@ pub fn instantiate( _env: Env, _info: MessageInfo, msg: InstantiateMsg, -) -> Result { +) -> Result { cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - let config = make_config(&deps, msg)?; - CONFIG.save(deps.storage, &config)?; - - Ok(Response::default()) -} - -fn make_config( - deps: &DepsMut, - msg: InstantiateMsg, -) -> Result { - let admin = deps.api.addr_validate(&msg.admin_address)?; - let governance = deps.api.addr_validate(&msg.governance_address)?; - let gateway = deps.api.addr_validate(&msg.gateway_address)?; - let multisig = deps.api.addr_validate(&msg.multisig_address)?; - let coordinator = deps.api.addr_validate(&msg.coordinator_address)?; - let service_registry = deps.api.addr_validate(&msg.service_registry_address)?; - let voting_verifier = deps.api.addr_validate(&msg.voting_verifier_address)?; - - Ok(Config { - admin, - governance, - gateway, - multisig, - coordinator, - service_registry, - voting_verifier, + let config = Config { + gateway: address::validate_cosmwasm_address(deps.api, &msg.gateway_address)?, + multisig: address::validate_cosmwasm_address(deps.api, &msg.multisig_address)?, + coordinator: address::validate_cosmwasm_address(deps.api, &msg.coordinator_address)?, + service_registry: address::validate_cosmwasm_address( + deps.api, + &msg.service_registry_address, + )?, + voting_verifier: address::validate_cosmwasm_address( + deps.api, + &msg.voting_verifier_address, + )?, signing_threshold: msg.signing_threshold, service_name: msg.service_name, - chain_name: msg - .chain_name - .parse() - .map_err(|_| ContractError::InvalidChainName)?, + chain_name: msg.chain_name.parse()?, verifier_set_diff_threshold: msg.verifier_set_diff_threshold, encoder: msg.encoder, key_type: msg.key_type, domain_separator: msg.domain_separator, - }) + }; + CONFIG.save(deps.storage, &config)?; + + permission_control::set_admin( + deps.storage, + &address::validate_cosmwasm_address(deps.api, &msg.admin_address)?, + )?; + permission_control::set_governance( + deps.storage, + &address::validate_cosmwasm_address(deps.api, &msg.governance_address)?, + )?; + + Ok(Response::default()) } #[cfg_attr(not(feature = "library"), entry_point)] @@ -72,27 +69,21 @@ pub fn execute( env: Env, info: MessageInfo, msg: ExecuteMsg, -) -> Result { - match msg { - ExecuteMsg::ConstructProof { message_ids } => execute::construct_proof(deps, message_ids), - ExecuteMsg::UpdateVerifierSet {} => { - execute::require_admin(&deps, info.clone()) - .or_else(|_| execute::require_governance(&deps, info))?; - execute::update_verifier_set(deps, env) - } - ExecuteMsg::ConfirmVerifierSet {} => execute::confirm_verifier_set(deps, info.sender), +) -> Result { + match msg.ensure_permissions(deps.storage, &info.sender)? { + ExecuteMsg::ConstructProof(message_ids) => Ok(execute::construct_proof(deps, message_ids)?), + ExecuteMsg::UpdateVerifierSet {} => Ok(execute::update_verifier_set(deps, env)?), + ExecuteMsg::ConfirmVerifierSet {} => Ok(execute::confirm_verifier_set(deps, info.sender)?), ExecuteMsg::UpdateSigningThreshold { new_signing_threshold, - } => { - execute::require_governance(&deps, info)?; - execute::update_signing_threshold(deps, new_signing_threshold) - } + } => Ok(execute::update_signing_threshold( + deps, + new_signing_threshold, + )?), ExecuteMsg::UpdateAdmin { new_admin_address } => { - execute::require_governance(&deps, info)?; - execute::update_admin(deps, new_admin_address) + Ok(execute::update_admin(deps, new_admin_address)?) } } - .map_err(axelar_wasm_std::ContractError::from) } #[cfg_attr(not(feature = "library"), entry_point)] @@ -100,12 +91,12 @@ pub fn reply( deps: DepsMut, _env: Env, reply: Reply, -) -> Result { +) -> Result { match reply.id { START_MULTISIG_REPLY_ID => reply::start_multisig_reply(deps, reply), _ => unreachable!("unknown reply ID"), } - .map_err(axelar_wasm_std::ContractError::from) + .map_err(axelar_wasm_std::error::ContractError::from) } #[cfg_attr(not(feature = "library"), entry_point)] @@ -113,15 +104,16 @@ pub fn query( deps: Deps, _env: Env, msg: QueryMsg, -) -> Result { +) -> Result { match msg { - QueryMsg::GetProof { + QueryMsg::Proof { multisig_session_id, - } => to_json_binary(&query::get_proof(deps, multisig_session_id)?), - QueryMsg::GetVerifierSet {} => to_json_binary(&query::get_verifier_set(deps)?), + } => to_json_binary(&query::proof(deps, multisig_session_id)?), + QueryMsg::CurrentVerifierSet {} => to_json_binary(&query::current_verifier_set(deps)?), + QueryMsg::NextVerifierSet {} => to_json_binary(&query::next_verifier_set(deps)?), } .change_context(ContractError::SerializeResponse) - .map_err(axelar_wasm_std::ContractError::from) + .map_err(axelar_wasm_std::error::ContractError::from) } #[cfg_attr(not(feature = "library"), entry_point)] @@ -129,39 +121,36 @@ pub fn migrate( deps: DepsMut, _env: Env, _msg: Empty, -) -> Result { - cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; +) -> Result { + migrations::v0_6_0::migrate(deps.storage)?; - migrations::v_0_5::migrate_verifier_sets(deps) + Ok(Response::default()) } #[cfg(test)] mod tests { + use axelar_wasm_std::permission_control::Permission; + use axelar_wasm_std::{permission_control, MajorityThreshold, Threshold, VerificationStatus}; + use cosmwasm_std::testing::{ + mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage, + }; use cosmwasm_std::{ - from_json, - testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, - Addr, Empty, Fraction, OwnedDeps, SubMsgResponse, SubMsgResult, Uint128, Uint64, + from_json, Addr, Empty, Fraction, OwnedDeps, SubMsgResponse, SubMsgResult, Uint128, Uint64, }; - - use axelar_wasm_std::{MajorityThreshold, Threshold, VerificationStatus}; - use multisig::{msg::Signer, verifier_set::VerifierSet}; + use multisig::msg::Signer; + use multisig::verifier_set::VerifierSet; use prost::Message; use router_api::CrossChainId; - use crate::{ - contract::execute::should_update_verifier_set, - test::test_utils::{ - mock_querier_handler, ADMIN, COORDINATOR_ADDRESS, GATEWAY_ADDRESS, GOVERNANCE, - MULTISIG_ADDRESS, SERVICE_NAME, SERVICE_REGISTRY_ADDRESS, VOTING_VERIFIER_ADDRESS, - }, - }; - use crate::{ - encoding::Encoder, - msg::{GetProofResponse, ProofStatus}, - test::test_data::{self, TestOperator}, - }; - use super::*; + use crate::contract::execute::should_update_verifier_set; + use crate::encoding::Encoder; + use crate::msg::{ProofResponse, ProofStatus, VerifierSetResponse}; + use crate::test::test_data::{self, TestOperator}; + use crate::test::test_utils::{ + mock_querier_handler, ADMIN, COORDINATOR_ADDRESS, GATEWAY_ADDRESS, GOVERNANCE, + MULTISIG_ADDRESS, SERVICE_NAME, SERVICE_REGISTRY_ADDRESS, VOTING_VERIFIER_ADDRESS, + }; const RELAYER: &str = "relayer"; const MULTISIG_SESSION_ID: Uint64 = Uint64::one(); @@ -190,7 +179,7 @@ mod tests { service_name: SERVICE_NAME.to_string(), chain_name: "ganache-0".to_string(), verifier_set_diff_threshold: 0, - encoder: crate::encoding::Encoder::Abi, + encoder: Encoder::Abi, key_type: multisig::key::KeyType::Ecdsa, domain_separator: [0; 32], }, @@ -202,7 +191,7 @@ mod tests { fn execute_update_verifier_set( deps: DepsMut, - ) -> Result { + ) -> Result { let msg = ExecuteMsg::UpdateVerifierSet {}; execute(deps, mock_env(), mock_info(ADMIN, &[]), msg) } @@ -210,7 +199,7 @@ mod tests { fn confirm_verifier_set( deps: DepsMut, sender: Addr, - ) -> Result { + ) -> Result { let msg = ExecuteMsg::ConfirmVerifierSet {}; execute(deps, mock_env(), mock_info(sender.as_str(), &[]), msg) } @@ -219,7 +208,7 @@ mod tests { deps: DepsMut, sender: Addr, new_signing_threshold: MajorityThreshold, - ) -> Result { + ) -> Result { let msg = ExecuteMsg::UpdateSigningThreshold { new_signing_threshold, }; @@ -230,7 +219,7 @@ mod tests { deps: DepsMut, sender: &str, new_admin_address: String, - ) -> Result { + ) -> Result { let msg = ExecuteMsg::UpdateAdmin { new_admin_address }; execute(deps, mock_env(), mock_info(sender, &[]), msg) } @@ -238,20 +227,21 @@ mod tests { fn execute_construct_proof( deps: DepsMut, message_ids: Option>, - ) -> Result { - let message_ids = match message_ids { - Some(ids) => ids, - None => test_data::messages() + ) -> Result { + let message_ids = message_ids.unwrap_or_else(|| { + test_data::messages() .into_iter() .map(|msg| msg.cc_id) - .collect::>(), - }; + .collect::>() + }); - let msg = ExecuteMsg::ConstructProof { message_ids }; + let msg = ExecuteMsg::ConstructProof(message_ids); execute(deps, mock_env(), mock_info(RELAYER, &[]), msg) } - fn reply_construct_proof(deps: DepsMut) -> Result { + fn reply_construct_proof( + deps: DepsMut, + ) -> Result { let session_id = to_json_binary(&MULTISIG_SESSION_ID).unwrap(); let response = SubMsgResponse { @@ -274,10 +264,10 @@ mod tests { ) } - fn query_get_proof( + fn query_proof( deps: Deps, multisig_session_id: Option, - ) -> Result { + ) -> Result { let multisig_session_id = match multisig_session_id { Some(id) => id, None => MULTISIG_SESSION_ID, @@ -286,17 +276,17 @@ mod tests { query( deps, mock_env(), - QueryMsg::GetProof { + QueryMsg::Proof { multisig_session_id, }, ) .map(|res| from_json(res).unwrap()) } - fn query_get_verifier_set( + fn query_verifier_set( deps: Deps, - ) -> Result, axelar_wasm_std::ContractError> { - query(deps, mock_env(), QueryMsg::GetVerifierSet {}).map(|res| from_json(res).unwrap()) + ) -> Result, axelar_wasm_std::error::ContractError> { + query(deps, mock_env(), QueryMsg::CurrentVerifierSet {}).map(|res| from_json(res).unwrap()) } #[test] @@ -348,13 +338,30 @@ mod tests { assert_eq!(res.messages.len(), 0); let config = CONFIG.load(deps.as_ref().storage).unwrap(); - assert_eq!(config.admin, admin); assert_eq!(config.gateway, gateway_address); assert_eq!(config.multisig, multisig_address); assert_eq!(config.service_registry, service_registry_address); assert_eq!(config.signing_threshold, signing_threshold); assert_eq!(config.service_name, service_name); - assert_eq!(config.encoder, encoding) + assert_eq!(config.encoder, encoding); + + assert_eq!( + permission_control::sender_role( + deps.as_ref().storage, + &address::validate_cosmwasm_address(&deps.api, admin).unwrap() + ) + .unwrap(), + Permission::Admin.into() + ); + + assert_eq!( + permission_control::sender_role( + deps.as_ref().storage, + &address::validate_cosmwasm_address(&deps.api, governance).unwrap() + ) + .unwrap(), + Permission::Governance.into() + ); } } @@ -386,14 +393,14 @@ mod tests { #[test] fn test_update_verifier_set_fresh() { let mut deps = setup_test_case(); - let verifier_set = query_get_verifier_set(deps.as_ref()); + let verifier_set = query_verifier_set(deps.as_ref()); assert!(verifier_set.is_ok()); assert!(verifier_set.unwrap().is_none()); let res = execute_update_verifier_set(deps.as_mut()); assert!(res.is_ok()); - let verifier_set = query_get_verifier_set(deps.as_ref()); + let verifier_set = query_verifier_set(deps.as_ref()); assert!(verifier_set.is_ok()); let verifier_set = verifier_set.unwrap().unwrap(); @@ -401,7 +408,7 @@ mod tests { let expected_verifier_set = test_operators_to_verifier_set(test_data::operators(), mock_env().block.height); - assert_eq!(verifier_set, expected_verifier_set); + assert_eq!(verifier_set, expected_verifier_set.into()); } #[test] @@ -416,7 +423,13 @@ mod tests { assert!(res.is_err()); assert_eq!( res.unwrap_err().to_string(), - axelar_wasm_std::ContractError::from(ContractError::Unauthorized).to_string() + axelar_wasm_std::error::ContractError::from( + permission_control::Error::PermissionDenied { + expected: Permission::Elevated.into(), + actual: Permission::NoPrivilege.into() + } + ) + .to_string() ); } @@ -463,7 +476,7 @@ mod tests { assert!(res.is_ok()); - let verifier_set = query_get_verifier_set(deps.as_ref()); + let verifier_set = query_verifier_set(deps.as_ref()); assert!(verifier_set.is_ok()); let verifier_set = verifier_set.unwrap().unwrap(); @@ -471,7 +484,7 @@ mod tests { let expected_verifier_set = test_operators_to_verifier_set(test_data::operators(), mock_env().block.height); - assert_eq!(verifier_set, expected_verifier_set); + assert_eq!(verifier_set, expected_verifier_set.into()); } #[test] @@ -497,7 +510,7 @@ mod tests { let res = execute_update_verifier_set(deps.as_mut()); assert!(res.is_ok()); - let verifier_set = query_get_verifier_set(deps.as_ref()); + let verifier_set = query_verifier_set(deps.as_ref()); assert!(verifier_set.is_ok()); let verifier_set = verifier_set.unwrap().unwrap(); @@ -505,7 +518,7 @@ mod tests { let expected_verifier_set = test_operators_to_verifier_set(new_verifier_set, mock_env().block.height); - assert_eq!(verifier_set, expected_verifier_set); + assert_eq!(verifier_set, expected_verifier_set.into()); } #[test] @@ -531,7 +544,7 @@ mod tests { assert!(res.is_ok()); - let verifier_set = query_get_verifier_set(deps.as_ref()); + let verifier_set = query_verifier_set(deps.as_ref()); assert!(verifier_set.is_ok()); let verifier_set = verifier_set.unwrap().unwrap(); @@ -539,7 +552,7 @@ mod tests { let expected_verifier_set = test_operators_to_verifier_set(test_data::operators(), mock_env().block.height); - assert_eq!(verifier_set, expected_verifier_set); + assert_eq!(verifier_set, expected_verifier_set.into()); } #[test] @@ -554,7 +567,8 @@ mod tests { assert!(res.is_err()); assert_eq!( res.unwrap_err().to_string(), - axelar_wasm_std::ContractError::from(ContractError::VerifierSetUnchanged).to_string() + axelar_wasm_std::error::ContractError::from(ContractError::VerifierSetUnchanged) + .to_string() ); } @@ -579,7 +593,7 @@ mod tests { assert!(res.is_err()); assert_eq!( res.unwrap_err().to_string(), - axelar_wasm_std::ContractError::from(ContractError::VerifierSetNotConfirmed) + axelar_wasm_std::error::ContractError::from(ContractError::VerifierSetNotConfirmed) .to_string() ); } @@ -609,7 +623,20 @@ mod tests { assert!(res.is_err()); assert_eq!( res.unwrap_err().to_string(), - axelar_wasm_std::ContractError::from(ContractError::VerifierSetNotConfirmed) + axelar_wasm_std::error::ContractError::from(ContractError::VerifierSetNotConfirmed) + .to_string() + ); + } + + #[test] + fn confirm_verifier_no_update_in_progress_should_fail() { + let mut deps = setup_test_case(); + + let res = confirm_verifier_set(deps.as_mut(), Addr::unchecked("relayer")); + assert!(res.is_err()); + assert_eq!( + res.unwrap_err().to_string(), + axelar_wasm_std::error::ContractError::from(ContractError::NoVerifierSetToConfirm) .to_string() ); } @@ -647,7 +674,7 @@ mod tests { execute_construct_proof(deps.as_mut(), None).unwrap(); reply_construct_proof(deps.as_mut()).unwrap(); // simulate reply from multisig - let res = query_get_proof(deps.as_ref(), None).unwrap(); + let res = query_proof(deps.as_ref(), None).unwrap(); assert_eq!(res.multisig_session_id, MULTISIG_SESSION_ID); assert_eq!(res.message_ids.len(), 1); @@ -666,7 +693,7 @@ mod tests { assert!(res.is_err()); assert_eq!( res.unwrap_err().to_string(), - axelar_wasm_std::ContractError::from(ContractError::NoVerifierSet).to_string() + axelar_wasm_std::error::ContractError::from(ContractError::NoVerifierSet).to_string() ); } @@ -696,7 +723,10 @@ mod tests { /// Calls update_signing_threshold, increasing the threshold by one. /// Returns (initial threshold, new threshold) fn update_signing_threshold_increase_by_one(deps: DepsMut) -> (Uint128, Uint128) { - let verifier_set = query_get_verifier_set(deps.as_ref()).unwrap().unwrap(); + let verifier_set = query_verifier_set(deps.as_ref()) + .unwrap() + .unwrap() + .verifier_set; let initial_threshold = verifier_set.threshold; let total_weight = verifier_set .signers @@ -731,7 +761,10 @@ mod tests { update_signing_threshold_increase_by_one(deps.as_mut()); assert_ne!(initial_threshold, new_threshold); - let verifier_set = query_get_verifier_set(deps.as_ref()).unwrap().unwrap(); + let verifier_set = query_verifier_set(deps.as_ref()) + .unwrap() + .unwrap() + .verifier_set; assert_eq!(verifier_set.threshold, initial_threshold); } @@ -749,7 +782,10 @@ mod tests { let governance = Addr::unchecked(GOVERNANCE); confirm_verifier_set(deps.as_mut(), governance).unwrap(); - let verifier_set = query_get_verifier_set(deps.as_ref()).unwrap().unwrap(); + let verifier_set = query_verifier_set(deps.as_ref()) + .unwrap() + .unwrap() + .verifier_set; assert_eq!(verifier_set.threshold, new_threshold); } @@ -767,7 +803,10 @@ mod tests { let res = confirm_verifier_set(deps.as_mut(), Addr::unchecked("relayer")); assert!(res.is_ok()); - let verifier_set = query_get_verifier_set(deps.as_ref()).unwrap().unwrap(); + let verifier_set = query_verifier_set(deps.as_ref()) + .unwrap() + .unwrap() + .verifier_set; assert_eq!(verifier_set.threshold, new_threshold); } @@ -855,7 +894,16 @@ mod tests { let res = execute_update_admin(deps.as_mut(), GOVERNANCE, new_admin.to_string()); assert!(res.is_ok(), "{:?}", res); - let config = CONFIG.load(deps.as_ref().storage).unwrap(); - assert_eq!(config.admin, Addr::unchecked(new_admin)); + assert_eq!( + permission_control::sender_role(deps.as_ref().storage, &Addr::unchecked(new_admin)) + .unwrap(), + Permission::Admin.into() + ); + + assert_eq!( + permission_control::sender_role(deps.as_ref().storage, &Addr::unchecked(ADMIN)) + .unwrap(), + Permission::NoPrivilege.into() + ); } } diff --git a/contracts/multisig-prover/src/execute.rs b/contracts/multisig-prover/src/contract/execute.rs similarity index 59% rename from contracts/multisig-prover/src/execute.rs rename to contracts/multisig-prover/src/contract/execute.rs index 04d913e26..197d78c5a 100644 --- a/contracts/multisig-prover/src/execute.rs +++ b/contracts/multisig-prover/src/contract/execute.rs @@ -1,71 +1,72 @@ -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashSet}; +use axelar_wasm_std::permission_control::Permission; +use axelar_wasm_std::snapshot::{Participant, Snapshot}; +use axelar_wasm_std::{address, permission_control, FnExt, MajorityThreshold, VerificationStatus}; use cosmwasm_std::{ - to_json_binary, wasm_execute, Addr, DepsMut, Env, MessageInfo, QuerierWrapper, QueryRequest, - Response, Storage, SubMsg, WasmQuery, + to_json_binary, wasm_execute, Addr, DepsMut, Env, QuerierWrapper, QueryRequest, Response, + Storage, SubMsg, WasmQuery, }; - +use error_stack::{report, ResultExt}; use itertools::Itertools; -use multisig::{key::PublicKey, msg::Signer, verifier_set::VerifierSet}; - -use axelar_wasm_std::{snapshot, MajorityThreshold, VerificationStatus}; +use multisig::msg::Signer; +use multisig::verifier_set::VerifierSet; use router_api::{ChainName, CrossChainId, Message}; -use service_registry::state::WeightedVerifier; - -use crate::{ - contract::START_MULTISIG_REPLY_ID, - error::ContractError, - payload::Payload, - state::{Config, CONFIG, CURRENT_VERIFIER_SET, NEXT_VERIFIER_SET, PAYLOAD, REPLY_TRACKER}, - types::VerifiersInfo, -}; - -pub fn require_admin(deps: &DepsMut, info: MessageInfo) -> Result<(), ContractError> { - match CONFIG.load(deps.storage)?.admin { - admin if admin == info.sender => Ok(()), - _ => Err(ContractError::Unauthorized), - } -} +use service_registry::{Service, WeightedVerifier}; -pub fn require_governance(deps: &DepsMut, info: MessageInfo) -> Result<(), ContractError> { - match CONFIG.load(deps.storage)?.governance { - governance if governance == info.sender => Ok(()), - _ => Err(ContractError::Unauthorized), - } -} +use crate::contract::START_MULTISIG_REPLY_ID; +use crate::error::ContractError; +use crate::payload::Payload; +use crate::state::{ + Config, CONFIG, CURRENT_VERIFIER_SET, NEXT_VERIFIER_SET, PAYLOAD, REPLY_TRACKER, +}; pub fn construct_proof( deps: DepsMut, message_ids: Vec, -) -> Result { - let config = CONFIG.load(deps.storage)?; - let payload_id = (&message_ids).into(); +) -> error_stack::Result { + let config = CONFIG.load(deps.storage).map_err(ContractError::from)?; - let messages = get_messages( + let messages = messages( deps.querier, message_ids, config.gateway.clone(), config.chain_name.clone(), )?; - let payload = match PAYLOAD.may_load(deps.storage, &payload_id)? { - Some(payload) => payload, - None => { - let payload = Payload::Messages(messages); - PAYLOAD.save(deps.storage, &payload_id, &payload)?; + let payload = Payload::Messages(messages); + let payload_id = payload.id(); - payload + match PAYLOAD + .may_load(deps.storage, &payload_id) + .map_err(ContractError::from)? + { + Some(stored_payload) => { + if stored_payload != payload { + return Err(report!(ContractError::PayloadMismatch)) + .attach_printable_lazy(|| format!("{:?}", stored_payload)); + } + } + None => { + PAYLOAD + .save(deps.storage, &payload_id, &payload) + .map_err(ContractError::from)?; } }; // keep track of the payload id to use during submessage reply - REPLY_TRACKER.save(deps.storage, &payload_id)?; + REPLY_TRACKER + .save(deps.storage, &payload_id) + .map_err(ContractError::from)?; let verifier_set = CURRENT_VERIFIER_SET - .may_load(deps.storage)? + .may_load(deps.storage) + .map_err(ContractError::from)? .ok_or(ContractError::NoVerifierSet)?; - let digest = payload.digest(config.encoder, &config.domain_separator, &verifier_set)?; + let digest = config + .encoder + .digest(&config.domain_separator, &verifier_set, &payload)?; let start_sig_msg = multisig::msg::ExecuteMsg::StartSigningSession { verifier_set_id: verifier_set.id(), @@ -74,12 +75,13 @@ pub fn construct_proof( sig_verifier: None, }; - let wasm_msg = wasm_execute(config.multisig, &start_sig_msg, vec![])?; + let wasm_msg = + wasm_execute(config.multisig, &start_sig_msg, vec![]).map_err(ContractError::from)?; Ok(Response::new().add_submessage(SubMsg::reply_on_success(wasm_msg, START_MULTISIG_REPLY_ID))) } -fn get_messages( +fn messages( querier: QuerierWrapper, message_ids: Vec, gateway: Addr, @@ -87,7 +89,7 @@ fn get_messages( ) -> Result, ContractError> { let length = message_ids.len(); - let query = gateway_api::msg::QueryMsg::GetOutgoingMessages { message_ids }; + let query = gateway_api::msg::QueryMsg::OutgoingMessages(message_ids); let messages: Vec = querier.query(&QueryRequest::Wasm(WasmQuery::Smart { contract_addr: gateway.into(), msg: to_json_binary(&query)?, @@ -99,18 +101,25 @@ fn get_messages( "violated invariant: returned gateway messages count mismatch" ); - if messages + if let Some(wrong_destination) = messages .iter() - .any(|msg| msg.destination_chain != chain_name) + .find(|msg| msg.destination_chain != chain_name) { - panic!("violated invariant: messages from different chain found"); + Err(ContractError::InvalidDestinationChain { + expected: chain_name, + actual: wrong_destination.destination_chain.clone(), + }) + } else { + Ok(messages) } - - Ok(messages) } -fn get_verifiers_info(deps: &DepsMut, config: &Config) -> Result { - let active_verifiers_query = service_registry::msg::QueryMsg::GetActiveVerifiers { +fn make_verifier_set( + deps: &DepsMut, + env: &Env, + config: &Config, +) -> Result { + let active_verifiers_query = service_registry::msg::QueryMsg::ActiveVerifiers { service_name: config.service_name.clone(), chain_name: config.chain_name.clone(), }; @@ -121,48 +130,55 @@ fn get_verifiers_info(deps: &DepsMut, config: &Config) -> Result(&QueryRequest::Wasm(WasmQuery::Smart { + contract_addr: config.service_registry.to_string(), + msg: to_json_binary(&service_registry::msg::QueryMsg::Service { + service_name: config.service_name.clone(), + })?, + }))? + .min_num_verifiers; + + let participants_with_pubkeys = verifiers .into_iter() - .map(WeightedVerifier::into) - .collect::>(); - - let snapshot = - snapshot::Snapshot::new(config.signing_threshold, participants.clone().try_into()?); - - let mut pub_keys = vec![]; - for participant in &participants { - let pub_key_query = multisig::msg::QueryMsg::GetPublicKey { - verifier_address: participant.address.to_string(), - key_type: config.key_type, - }; - let pub_key: PublicKey = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: config.multisig.to_string(), - msg: to_json_binary(&pub_key_query)?, - }))?; - pub_keys.push(pub_key); + .filter_map(|verifier| { + let pub_key_query = multisig::msg::QueryMsg::PublicKey { + verifier_address: verifier.verifier_info.address.to_string(), + key_type: config.key_type, + }; + + match deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { + contract_addr: config.multisig.to_string(), + msg: to_json_binary(&pub_key_query).ok()?, + })) { + Ok(pub_key) => Some((Participant::from(verifier), pub_key)), + Err(_) => None, + } + }) + .collect::>(); + + if participants_with_pubkeys.len() < min_num_verifiers as usize { + return Err(ContractError::NotEnoughVerifiers); } - Ok(VerifiersInfo { - snapshot, - pubkeys_by_participant: participants.into_iter().zip(pub_keys).collect(), - }) -} + let snapshot = Snapshot::new( + config.signing_threshold, + participants_with_pubkeys + .iter() + .map(|(participant, _)| participant.clone()) + .collect::>() + .try_into()?, + ); -fn make_verifier_set( - deps: &DepsMut, - env: &Env, - config: &Config, -) -> Result { - let verifiers_info = get_verifiers_info(deps, config)?; Ok(VerifierSet::new( - verifiers_info.pubkeys_by_participant, - verifiers_info.snapshot.quorum.into(), + participants_with_pubkeys, + snapshot.quorum.into(), env.block.height, )) } -fn get_next_verifier_set( +fn next_verifier_set( deps: &DepsMut, env: &Env, config: &Config, @@ -202,37 +218,53 @@ fn save_next_verifier_set( Ok(()) } -pub fn update_verifier_set(deps: DepsMut, env: Env) -> Result { - let config = CONFIG.load(deps.storage)?; - let cur_verifier_set = CURRENT_VERIFIER_SET.may_load(deps.storage)?; +pub fn update_verifier_set( + deps: DepsMut, + env: Env, +) -> error_stack::Result { + let config = CONFIG.load(deps.storage).map_err(ContractError::from)?; + let cur_verifier_set = CURRENT_VERIFIER_SET + .may_load(deps.storage) + .map_err(ContractError::from)?; match cur_verifier_set { None => { // if no verifier set, just store it and return let new_verifier_set = make_verifier_set(&deps, &env, &config)?; - CURRENT_VERIFIER_SET.save(deps.storage, &new_verifier_set)?; - - Ok(Response::new().add_message(wasm_execute( - config.multisig, - &multisig::msg::ExecuteMsg::RegisterVerifierSet { - verifier_set: new_verifier_set, - }, - vec![], - )?)) + CURRENT_VERIFIER_SET + .save(deps.storage, &new_verifier_set) + .map_err(ContractError::from)?; + + Ok(Response::new().add_message( + wasm_execute( + config.multisig, + &multisig::msg::ExecuteMsg::RegisterVerifierSet { + verifier_set: new_verifier_set, + }, + vec![], + ) + .map_err(ContractError::from)?, + )) } Some(cur_verifier_set) => { - let new_verifier_set = get_next_verifier_set(&deps, &env, &config)? + let new_verifier_set = next_verifier_set(&deps, &env, &config)? .ok_or(ContractError::VerifierSetUnchanged)?; save_next_verifier_set(deps.storage, &new_verifier_set)?; - let payload = Payload::VerifierSet(new_verifier_set); + let payload = Payload::VerifierSet(new_verifier_set.clone()); let payload_id = payload.id(); - PAYLOAD.save(deps.storage, &payload_id, &payload)?; - REPLY_TRACKER.save(deps.storage, &payload_id)?; + PAYLOAD + .save(deps.storage, &payload_id, &payload) + .map_err(ContractError::from)?; + REPLY_TRACKER + .save(deps.storage, &payload_id) + .map_err(ContractError::from)?; let digest = - payload.digest(config.encoder, &config.domain_separator, &cur_verifier_set)?; + config + .encoder + .digest(&config.domain_separator, &cur_verifier_set, &payload)?; let start_sig_msg = multisig::msg::ExecuteMsg::StartSigningSession { verifier_set_id: cur_verifier_set.id(), @@ -241,10 +273,24 @@ pub fn update_verifier_set(deps: DepsMut, env: Env) -> Result Result<(), ContractError> { - let query = voting_verifier::msg::QueryMsg::GetVerifierSetStatus { - new_verifier_set: verifier_set.clone(), - }; + let query = voting_verifier::msg::QueryMsg::VerifierSetStatus(verifier_set.clone()); let status: VerificationStatus = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { contract_addr: config.voting_verifier.to_string(), @@ -273,15 +317,20 @@ fn ensure_verifier_set_verification( pub fn confirm_verifier_set(deps: DepsMut, sender: Addr) -> Result { let config = CONFIG.load(deps.storage)?; - let verifier_set = NEXT_VERIFIER_SET.load(deps.storage)?; + let verifier_set = NEXT_VERIFIER_SET + .may_load(deps.storage)? + .ok_or(ContractError::NoVerifierSetToConfirm)?; - if sender != config.governance { + let sender_role = permission_control::sender_role(deps.storage, &sender)?; + if !sender_role.contains(Permission::Governance) { ensure_verifier_set_verification(&verifier_set, &config, &deps)?; } CURRENT_VERIFIER_SET.save(deps.storage, &verifier_set)?; NEXT_VERIFIER_SET.remove(deps.storage); + let verifier_union_set = all_active_verifiers(&deps)?; + Ok(Response::new() .add_message(wasm_execute( config.multisig, @@ -293,12 +342,31 @@ pub fn confirm_verifier_set(deps: DepsMut, sender: Addr) -> Result Result, ContractError> { + let current_signers = CURRENT_VERIFIER_SET + .may_load(deps.storage)? + .map(|verifier_set| verifier_set.signers) + .unwrap_or_default(); + + let next_signers = NEXT_VERIFIER_SET + .may_load(deps.storage)? + .map(|verifier_set| verifier_set.signers) + .unwrap_or_default(); + + current_signers + .values() + .chain(next_signers.values()) + .map(|signer| signer.address.clone()) + .collect::>() + .then(Ok) +} + pub fn should_update_verifier_set( new_verifiers: &VerifierSet, cur_verifiers: &VerifierSet, @@ -344,34 +412,27 @@ pub fn update_signing_threshold( Ok(Response::new()) } -pub fn update_admin(deps: DepsMut, new_admin_address: String) -> Result { - CONFIG.update( - deps.storage, - |mut config| -> Result { - config.admin = deps.api.addr_validate(&new_admin_address)?; - Ok(config) - }, - )?; +pub fn update_admin( + deps: DepsMut, + new_admin_address: String, +) -> Result { + let new_admin = address::validate_cosmwasm_address(deps.api, &new_admin_address)?; + permission_control::set_admin(deps.storage, &new_admin)?; Ok(Response::new()) } #[cfg(test)] mod tests { + use std::collections::BTreeMap; + use axelar_wasm_std::Threshold; - use cosmwasm_std::{ - testing::{mock_dependencies, mock_env}, - Addr, - }; + use cosmwasm_std::testing::{mock_dependencies, mock_env}; + use cosmwasm_std::Addr; use router_api::ChainName; - use crate::{ - execute::should_update_verifier_set, - state::{Config, NEXT_VERIFIER_SET}, - test::test_data, - }; - use std::collections::BTreeMap; - - use super::{different_set_in_progress, get_next_verifier_set}; + use super::{different_set_in_progress, next_verifier_set, should_update_verifier_set}; + use crate::state::{Config, NEXT_VERIFIER_SET}; + use crate::test::test_data; #[test] fn should_update_verifier_set_no_change() { @@ -477,21 +538,19 @@ mod tests { } #[test] - fn get_next_verifier_set_should_return_pending() { + fn next_verifier_set_should_return_pending() { let mut deps = mock_dependencies(); let env = mock_env(); let new_verifier_set = test_data::new_verifier_set(); NEXT_VERIFIER_SET .save(deps.as_mut().storage, &new_verifier_set) .unwrap(); - let ret_verifier_set = get_next_verifier_set(&deps.as_mut(), &env, &mock_config()); + let ret_verifier_set = next_verifier_set(&deps.as_mut(), &env, &mock_config()); assert_eq!(ret_verifier_set.unwrap().unwrap(), new_verifier_set); } fn mock_config() -> Config { Config { - admin: Addr::unchecked("doesn't matter"), - governance: Addr::unchecked("doesn't matter"), gateway: Addr::unchecked("doesn't matter"), multisig: Addr::unchecked("doesn't matter"), coordinator: Addr::unchecked("doesn't matter"), diff --git a/contracts/multisig-prover/src/contract/migrations/mod.rs b/contracts/multisig-prover/src/contract/migrations/mod.rs new file mode 100644 index 000000000..b9dfdddab --- /dev/null +++ b/contracts/multisig-prover/src/contract/migrations/mod.rs @@ -0,0 +1 @@ +pub mod v0_6_0; diff --git a/contracts/multisig-prover/src/contract/migrations/v0_6_0.rs b/contracts/multisig-prover/src/contract/migrations/v0_6_0.rs new file mode 100644 index 000000000..dbf741166 --- /dev/null +++ b/contracts/multisig-prover/src/contract/migrations/v0_6_0.rs @@ -0,0 +1,290 @@ +#![allow(deprecated)] + +use axelar_wasm_std::error::ContractError; +use axelar_wasm_std::hash::Hash; +use axelar_wasm_std::{permission_control, MajorityThreshold}; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, Storage}; +use cw_storage_plus::Item; +use multisig::key::KeyType; +use router_api::ChainName; + +use crate::contract::{CONTRACT_NAME, CONTRACT_VERSION}; +use crate::encoding::Encoder; +use crate::state; + +const BASE_VERSION: &str = "0.6.0"; + +pub fn migrate(storage: &mut dyn Storage) -> Result<(), ContractError> { + cw2::assert_contract_version(storage, CONTRACT_NAME, BASE_VERSION)?; + + let config = CONFIG.load(storage)?; + + migrate_permission_control(storage, &config)?; + migrate_config(storage, config)?; + delete_payloads(storage); + + cw2::set_contract_version(storage, CONTRACT_NAME, CONTRACT_VERSION)?; + Ok(()) +} + +fn delete_payloads(storage: &mut dyn Storage) { + state::PAYLOAD.clear(storage); + state::MULTISIG_SESSION_PAYLOAD.clear(storage); + state::REPLY_TRACKER.remove(storage); +} + +fn migrate_permission_control( + storage: &mut dyn Storage, + config: &Config, +) -> Result<(), ContractError> { + permission_control::set_governance(storage, &config.governance)?; + permission_control::set_admin(storage, &config.admin)?; + Ok(()) +} + +fn migrate_config(storage: &mut dyn Storage, config: Config) -> Result<(), ContractError> { + CONFIG.remove(storage); + + let config = state::Config { + gateway: config.gateway, + multisig: config.multisig, + coordinator: config.coordinator, + service_registry: config.service_registry, + voting_verifier: config.voting_verifier, + signing_threshold: config.signing_threshold, + service_name: config.service_name, + chain_name: config.chain_name, + verifier_set_diff_threshold: config.verifier_set_diff_threshold, + encoder: config.encoder, + key_type: config.key_type, + domain_separator: config.domain_separator, + }; + state::CONFIG.save(storage, &config)?; + Ok(()) +} + +#[cw_serde] +#[deprecated(since = "0.6.0", note = "only used during migration")] +struct Config { + pub admin: Addr, + pub governance: Addr, + pub gateway: Addr, + pub multisig: Addr, + pub coordinator: Addr, + pub service_registry: Addr, + pub voting_verifier: Addr, + pub signing_threshold: MajorityThreshold, + pub service_name: String, + pub chain_name: ChainName, + pub verifier_set_diff_threshold: u32, + pub encoder: Encoder, + pub key_type: KeyType, + pub domain_separator: Hash, +} +#[deprecated(since = "0.6.0", note = "only used during migration")] +const CONFIG: Item = Item::new("config"); + +#[cfg(test)] +mod tests { + use axelar_wasm_std::permission_control::Permission; + use axelar_wasm_std::{permission_control, MajorityThreshold, Threshold}; + use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; + use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, Response}; + use multisig::key::KeyType; + use router_api::{CrossChainId, Message}; + + use crate::contract::migrations::v0_6_0; + use crate::contract::{CONTRACT_NAME, CONTRACT_VERSION}; + use crate::encoding::Encoder; + use crate::error::ContractError; + use crate::msg::InstantiateMsg; + use crate::{payload, state}; + + #[test] + fn migrate_checks_contract_version() { + let mut deps = mock_dependencies(); + instantiate_contract(deps.as_mut()); + cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, "something wrong").unwrap(); + + assert!(v0_6_0::migrate(deps.as_mut().storage).is_err()); + + cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, v0_6_0::BASE_VERSION) + .unwrap(); + + assert!(v0_6_0::migrate(deps.as_mut().storage).is_ok()); + } + + #[test] + fn migrate_sets_contract_version() { + let mut deps = mock_dependencies(); + instantiate_contract(deps.as_mut()); + + v0_6_0::migrate(deps.as_mut().storage).unwrap(); + + let contract_version = cw2::get_contract_version(deps.as_mut().storage).unwrap(); + assert_eq!(contract_version.contract, CONTRACT_NAME); + assert_eq!(contract_version.version, CONTRACT_VERSION); + } + + #[test] + fn migrate_payload() { + let mut deps = mock_dependencies(); + + instantiate_contract(deps.as_mut()); + + let msgs = vec![ + Message { + cc_id: CrossChainId { + message_id: "id1".try_into().unwrap(), + source_chain: "chain1".try_into().unwrap(), + }, + source_address: "source-address".parse().unwrap(), + destination_chain: "destination".parse().unwrap(), + destination_address: "destination-address".parse().unwrap(), + payload_hash: [1; 32], + }, + Message { + cc_id: CrossChainId { + message_id: "id2".try_into().unwrap(), + source_chain: "chain2".try_into().unwrap(), + }, + source_address: "source-address2".parse().unwrap(), + destination_chain: "destination2".parse().unwrap(), + destination_address: "destination-address2".parse().unwrap(), + payload_hash: [2; 32], + }, + Message { + cc_id: CrossChainId { + message_id: "id3".try_into().unwrap(), + source_chain: "chain3".try_into().unwrap(), + }, + source_address: "source-address3".parse().unwrap(), + destination_chain: "destination3".parse().unwrap(), + destination_address: "destination-address3".parse().unwrap(), + payload_hash: [3; 32], + }, + ]; + + let payload = payload::Payload::Messages(msgs); + + state::PAYLOAD + .save(deps.as_mut().storage, &payload.id(), &payload) + .unwrap(); + + assert!(v0_6_0::migrate(deps.as_mut().storage).is_ok()); + + assert!(state::PAYLOAD.is_empty(deps.as_ref().storage)); + } + + #[test] + fn migrate_permission_control() { + let mut deps = mock_dependencies(); + + instantiate_contract(deps.as_mut()); + + assert!(v0_6_0::migrate(deps.as_mut().storage).is_ok()); + + assert_eq!( + permission_control::sender_role(deps.as_ref().storage, &Addr::unchecked("admin")) + .unwrap(), + Permission::Admin.into() + ); + + assert_eq!( + permission_control::sender_role(deps.as_ref().storage, &Addr::unchecked("governance")) + .unwrap(), + Permission::Governance.into() + ); + } + + #[test] + fn migrate_config() { + let mut deps = mock_dependencies(); + + instantiate_contract(deps.as_mut()); + + assert!(v0_6_0::CONFIG.load(deps.as_ref().storage).is_ok()); + assert!(state::CONFIG.load(deps.as_ref().storage).is_err()); + + assert!(v0_6_0::migrate(deps.as_mut().storage).is_ok()); + + assert!(v0_6_0::CONFIG.load(deps.as_ref().storage).is_err()); + assert!(state::CONFIG.load(deps.as_ref().storage).is_ok()); + } + + fn instantiate_contract(deps: DepsMut) { + instantiate( + deps, + mock_env(), + mock_info("admin", &[]), + InstantiateMsg { + admin_address: "admin".to_string(), + governance_address: "governance".to_string(), + gateway_address: "gateway".to_string(), + multisig_address: "multisig".to_string(), + coordinator_address: "coordinator".to_string(), + service_registry_address: "service_registry".to_string(), + voting_verifier_address: "voting_verifier".to_string(), + signing_threshold: Threshold::try_from((2u64, 3u64)) + .and_then(MajorityThreshold::try_from) + .unwrap(), + service_name: "service".to_string(), + chain_name: "chain".to_string(), + verifier_set_diff_threshold: 1, + encoder: Encoder::Abi, + key_type: KeyType::Ecdsa, + domain_separator: [0; 32], + }, + ) + .unwrap(); + } + + #[deprecated(since = "0.6.0", note = "only used to test the migration")] + pub fn instantiate( + deps: DepsMut, + _env: Env, + _info: MessageInfo, + msg: InstantiateMsg, + ) -> Result { + cw2::set_contract_version(deps.storage, CONTRACT_NAME, v0_6_0::BASE_VERSION)?; + + let config = make_config(&deps, msg)?; + v0_6_0::CONFIG.save(deps.storage, &config)?; + + Ok(Response::default()) + } + + fn make_config( + deps: &DepsMut, + msg: InstantiateMsg, + ) -> Result { + let admin = deps.api.addr_validate(&msg.admin_address)?; + let governance = deps.api.addr_validate(&msg.governance_address)?; + let gateway = deps.api.addr_validate(&msg.gateway_address)?; + let multisig = deps.api.addr_validate(&msg.multisig_address)?; + let coordinator = deps.api.addr_validate(&msg.coordinator_address)?; + let service_registry = deps.api.addr_validate(&msg.service_registry_address)?; + let voting_verifier = deps.api.addr_validate(&msg.voting_verifier_address)?; + + Ok(v0_6_0::Config { + admin, + governance, + gateway, + multisig, + coordinator, + service_registry, + voting_verifier, + signing_threshold: msg.signing_threshold, + service_name: msg.service_name, + chain_name: msg + .chain_name + .parse() + .map_err(|_| ContractError::InvalidChainName)?, + verifier_set_diff_threshold: msg.verifier_set_diff_threshold, + encoder: msg.encoder, + key_type: msg.key_type, + domain_separator: msg.domain_separator, + }) + } +} diff --git a/contracts/multisig-prover/src/contract/query.rs b/contracts/multisig-prover/src/contract/query.rs new file mode 100644 index 000000000..998b572c1 --- /dev/null +++ b/contracts/multisig-prover/src/contract/query.rs @@ -0,0 +1,90 @@ +use cosmwasm_std::{to_json_binary, Deps, QueryRequest, StdResult, Uint64, WasmQuery}; +use error_stack::Result; +use multisig::multisig::Multisig; +use multisig::types::MultisigState; + +use crate::error::ContractError; +use crate::msg::{ProofResponse, ProofStatus, VerifierSetResponse}; +use crate::state::{ + CONFIG, CURRENT_VERIFIER_SET, MULTISIG_SESSION_PAYLOAD, NEXT_VERIFIER_SET, PAYLOAD, +}; + +pub fn proof(deps: Deps, multisig_session_id: Uint64) -> Result { + let config = CONFIG.load(deps.storage).map_err(ContractError::from)?; + + let payload_id = MULTISIG_SESSION_PAYLOAD + .load(deps.storage, multisig_session_id.u64()) + .map_err(ContractError::from)?; + + let query_msg = multisig::msg::QueryMsg::Multisig { + session_id: multisig_session_id, + }; + + let multisig: Multisig = deps + .querier + .query(&QueryRequest::Wasm(WasmQuery::Smart { + contract_addr: config.multisig.to_string(), + msg: to_json_binary(&query_msg).map_err(ContractError::from)?, + })) + .map_err(ContractError::from)?; + + let payload = PAYLOAD + .load(deps.storage, &payload_id) + .map_err(ContractError::from)?; + + let status = match multisig.state { + MultisigState::Pending => ProofStatus::Pending, + MultisigState::Completed { .. } => { + let execute_data = config.encoder.execute_data( + &config.domain_separator, + &multisig.verifier_set, + multisig.optimize_signatures(), + &payload, + )?; + ProofStatus::Completed { execute_data } + } + }; + + Ok(ProofResponse { + multisig_session_id, + message_ids: payload.message_ids().unwrap_or_default(), + payload, + status, + }) +} + +pub fn current_verifier_set(deps: Deps) -> StdResult> { + CURRENT_VERIFIER_SET + .may_load(deps.storage) + .map(|op| op.map(|set| set.into())) +} + +pub fn next_verifier_set(deps: Deps) -> StdResult> { + NEXT_VERIFIER_SET + .may_load(deps.storage) + .map(|op| op.map(|set| set.into())) +} + +#[cfg(test)] +mod test { + use cosmwasm_std::testing::mock_dependencies; + + use crate::state; + use crate::test::test_data::new_verifier_set; + + #[test] + fn next_verifier_set() { + let mut deps = mock_dependencies(); + + assert_eq!(None, super::next_verifier_set(deps.as_ref()).unwrap()); + + state::NEXT_VERIFIER_SET + .save(deps.as_mut().storage, &new_verifier_set()) + .unwrap(); + + assert_eq!( + Some(new_verifier_set().into()), + super::next_verifier_set(deps.as_ref()).unwrap() + ); + } +} diff --git a/contracts/multisig-prover/src/reply.rs b/contracts/multisig-prover/src/contract/reply.rs similarity index 90% rename from contracts/multisig-prover/src/reply.rs rename to contracts/multisig-prover/src/contract/reply.rs index ddb6951f2..d3fc7253b 100644 --- a/contracts/multisig-prover/src/reply.rs +++ b/contracts/multisig-prover/src/contract/reply.rs @@ -1,12 +1,9 @@ use cosmwasm_std::{from_json, DepsMut, Reply, Response, Uint64}; use cw_utils::{parse_reply_execute_data, MsgExecuteContractResponse}; -use crate::state::{CONFIG, PAYLOAD}; -use crate::{ - error::ContractError, - events::Event, - state::{MULTISIG_SESSION_PAYLOAD, REPLY_TRACKER}, -}; +use crate::error::ContractError; +use crate::events::Event; +use crate::state::{CONFIG, MULTISIG_SESSION_PAYLOAD, PAYLOAD, REPLY_TRACKER}; pub fn start_multisig_reply(deps: DepsMut, reply: Reply) -> Result { let config = CONFIG.load(deps.storage)?; diff --git a/contracts/multisig-prover/src/encoding/abi/execute_data.rs b/contracts/multisig-prover/src/encoding/abi/execute_data.rs index 792589315..199986969 100644 --- a/contracts/multisig-prover/src/encoding/abi/execute_data.rs +++ b/contracts/multisig-prover/src/encoding/abi/execute_data.rs @@ -1,162 +1,80 @@ -use alloy_primitives::Bytes; -use alloy_sol_types::{sol, SolCall}; -use cosmwasm_std::HexBinary; - use axelar_wasm_std::hash::Hash; -use multisig::{key::Signature, msg::SignerWithSig, verifier_set::VerifierSet}; - -use crate::{ - encoding::abi::{evm_address, Message, Proof, WeightedSigners}, - error::ContractError, - payload::Payload, -}; - -sol!( - IAxelarAmplifierGateway, - "src/encoding/abi/solidity/IAxelarAmplifierGateway.json" -); - -impl From for IAxelarAmplifierGateway::WeightedSigners { - fn from(signers: WeightedSigners) -> Self { - IAxelarAmplifierGateway::WeightedSigners { - signers: signers - .signers - .iter() - .map(|signer| IAxelarAmplifierGateway::WeightedSigner { - signer: signer.signer, - weight: signer.weight, - }) - .collect(), - threshold: signers.threshold, - nonce: signers.nonce, - } - } -} - -impl From for IAxelarAmplifierGateway::Proof { - fn from(proof: Proof) -> Self { - IAxelarAmplifierGateway::Proof { - signers: proof.signers.into(), - signatures: proof.signatures, - } - } -} - -impl From for IAxelarAmplifierGateway::Message { - fn from(message: Message) -> Self { - IAxelarAmplifierGateway::Message { - messageId: message.messageId, - sourceChain: message.sourceChain, - sourceAddress: message.sourceAddress, - contractAddress: message.contractAddress, - payloadHash: message.payloadHash, - } - } -} +use cosmwasm_std::HexBinary; +use error_stack::ResultExt; +use ethers_contract::contract::EthCall; +use ethers_core::abi::{self, Tokenize}; +use evm_gateway::{ApproveMessagesCall, Message, Proof, RotateSignersCall, WeightedSigners}; +use multisig::msg::SignerWithSig; +use multisig::verifier_set::VerifierSet; -impl Proof { - /// Proof contains the entire verifier set and optimized signatures. Signatures are sorted in ascending order based on the signer's address. - pub fn new(verifier_set: &VerifierSet, mut signers_with_sigs: Vec) -> Self { - signers_with_sigs.sort_by_key(|signer| { - evm_address(&signer.signer.pub_key).expect("failed to convert pub key to evm address") - }); - - let signatures = signers_with_sigs - .into_iter() - .map(|signer| Bytes::copy_from_slice(signer.signature.as_ref())) - .collect(); - - Proof { - signers: WeightedSigners::from(verifier_set), - signatures, - } - } -} +use crate::encoding::{to_recoverable, Encoder}; +use crate::error::ContractError; +use crate::payload::Payload; pub fn encode( verifier_set: &VerifierSet, signers: Vec, payload_digest: &Hash, payload: &Payload, -) -> Result { - let signers = to_recoverable(payload_digest.as_slice(), signers); +) -> error_stack::Result { + let signers = to_recoverable(Encoder::Abi, payload_digest, signers); - let proof = Proof::new(verifier_set, signers); + let proof = Proof::new(verifier_set, signers).change_context(ContractError::Proof)?; - let data = match payload { + let (selector, encoded) = match payload { Payload::Messages(messages) => { let messages: Vec<_> = messages .iter() - .map(|msg| Message::try_from(msg).map(IAxelarAmplifierGateway::Message::from)) - .collect::, _>>()?; - - IAxelarAmplifierGateway::approveMessagesCall::new((messages, proof.into())) - .abi_encode() - .into() + .map(Message::try_from) + .collect::>() + .change_context(ContractError::InvalidMessage)?; + + ( + ApproveMessagesCall::selector(), + abi::encode(&ApproveMessagesCall { messages, proof }.into_tokens()), + ) } Payload::VerifierSet(new_verifier_set) => { - let new_verifier_set = WeightedSigners::from(new_verifier_set); + let new_signers = WeightedSigners::try_from(new_verifier_set) + .change_context(ContractError::InvalidVerifierSet)?; - IAxelarAmplifierGateway::rotateSignersCall::new((new_verifier_set.into(), proof.into())) - .abi_encode() - .into() + ( + RotateSignersCall::selector(), + abi::encode(&RotateSignersCall { new_signers, proof }.into_tokens()), + ) } }; - Ok(data) -} - -// Convert non-recoverable ECDSA signatures to recoverable ones. -fn to_recoverable(msg: &[u8], signers: Vec) -> Vec { - signers + Ok(selector .into_iter() - .map(|mut signer| { - if let Signature::Ecdsa(nonrecoverable) = signer.signature { - signer.signature = nonrecoverable - .to_recoverable(msg, &signer.signer.pub_key, add27) - .map(Signature::EcdsaRecoverable) - .expect("failed to convert non-recoverable signature to recoverable"); - } - - signer - }) - .collect() -} - -pub fn add27(recovery_byte: k256::ecdsa::RecoveryId) -> u8 { - recovery_byte - .to_byte() - .checked_add(27) - .expect("overflow when adding 27 to recovery byte") + .chain(encoded) + .collect::>() + .into()) } #[cfg(test)] mod tests { use std::str::FromStr; - use alloy_primitives::Signature as EcdsaSignature; + use axelar_wasm_std::hash::Hash; use cosmwasm_std::HexBinary; use elliptic_curve::consts::U32; + use ethers_core::types::Signature as EthersSignature; + use evm_gateway::evm_address; use generic_array::GenericArray; use hex::FromHex; use itertools::Itertools; use k256::ecdsa::Signature as K256Signature; - use sha3::{Digest, Keccak256}; - - use axelar_wasm_std::hash::Hash; use multisig::key::{KeyType, KeyTyped, Signature}; use multisig::msg::{Signer, SignerWithSig}; + use sha3::{Digest, Keccak256}; - use crate::{ - encoding::abi::{ - evm_address, - execute_data::{add27, encode}, - payload_hash_to_sign, - }, - payload::Payload, - test::test_data::{ - curr_verifier_set, domain_separator, messages, verifier_set_from_pub_keys, - }, + use crate::encoding::abi::execute_data::encode; + use crate::encoding::abi::payload_digest; + use crate::encoding::add_27; + use crate::payload::Payload; + use crate::test::test_data::{ + curr_verifier_set, domain_separator, messages, verifier_set_from_pub_keys, }; #[test] @@ -189,7 +107,7 @@ mod tests { let signers_with_sigs = signers_with_sigs(verifier_set.signers.values(), sigs); let payload = Payload::VerifierSet(new_verifier_set); - let payload_hash: Hash = payload_hash_to_sign(&domain_separator, &verifier_set, &payload) + let payload_hash: Hash = payload_digest(&domain_separator, &verifier_set, &payload) .unwrap() .as_slice() .try_into() @@ -204,7 +122,7 @@ mod tests { #[test] fn should_convert_signature_to_recoverable() { - let ecdsa_signature = EcdsaSignature::from_str("74ab5ec395cdafd861dec309c30f6cf8884fc9905eb861171e636d9797478adb60b2bfceb7db0a08769ed7a60006096d3e0f6d3783d125600ac6306180ecbc6f1b").unwrap(); + let ecdsa_signature = EthersSignature::from_str("74ab5ec395cdafd861dec309c30f6cf8884fc9905eb861171e636d9797478adb60b2bfceb7db0a08769ed7a60006096d3e0f6d3783d125600ac6306180ecbc6f1b").unwrap(); let pub_key = Vec::from_hex("03571a2dcec96eecc7950c9f36367fd459b8d334bac01ac153b7ed3dcf4025fc22") .unwrap(); @@ -212,8 +130,10 @@ mod tests { let digest = "6ac52b00f4256d98d53c256949288135c14242a39001d5fdfa564ea003ccaf92"; let signature = { - let r_bytes: [u8; 32] = ecdsa_signature.r().to_be_bytes(); - let s_bytes: [u8; 32] = ecdsa_signature.s().to_be_bytes(); + let mut r_bytes = [0u8; 32]; + let mut s_bytes = [0u8; 32]; + ecdsa_signature.r.to_big_endian(&mut r_bytes); + ecdsa_signature.s.to_big_endian(&mut s_bytes); let gar: &GenericArray = GenericArray::from_slice(&r_bytes); let gas: &GenericArray = GenericArray::from_slice(&s_bytes); @@ -229,11 +149,11 @@ mod tests { .to_recoverable( HexBinary::from_hex(digest).unwrap().as_slice(), &multisig::key::PublicKey::Ecdsa(HexBinary::from(pub_key.to_vec())), - add27, + add_27, ) .unwrap(); - assert_eq!(recoverable.as_ref(), ecdsa_signature.as_bytes().as_slice()); + assert_eq!(recoverable.as_ref(), ecdsa_signature.to_vec().as_slice()); } else { panic!("Invalid signature type") } @@ -259,7 +179,7 @@ mod tests { let signers_with_sigs = signers_with_sigs(verifier_set.signers.values(), sigs); let payload = Payload::Messages(messages()); - let payload_hash: Hash = payload_hash_to_sign(&domain_separator, &verifier_set, &payload) + let payload_hash: Hash = payload_digest(&domain_separator, &verifier_set, &payload) .unwrap() .as_slice() .try_into() diff --git a/contracts/multisig-prover/src/encoding/abi/mod.rs b/contracts/multisig-prover/src/encoding/abi/mod.rs index c07eb223e..2a0439b6d 100644 --- a/contracts/multisig-prover/src/encoding/abi/mod.rs +++ b/contracts/multisig-prover/src/encoding/abi/mod.rs @@ -1,23 +1,15 @@ -// TODO: remove this, use evm-gateway package for solidity type conversion and encoding - pub mod execute_data; -use std::str::FromStr; - -use alloy_primitives::{Address, FixedBytes}; -use alloy_sol_types::{sol, SolValue}; -use cosmwasm_std::Uint256; -use k256::{elliptic_curve::sec1::ToEncodedPoint, PublicKey}; -use sha3::{Digest, Keccak256}; - use axelar_wasm_std::hash::Hash; -use multisig::{key::PublicKey as MultisigPublicKey, msg::Signer, verifier_set::VerifierSet}; -use router_api::Message as RouterMessage; - -use crate::{error::ContractError, payload::Payload}; +use error_stack::{Result, ResultExt}; +use ethers_core::abi::{encode as abi_encode, Token, Tokenize}; +use evm_gateway::{CommandType, Message, WeightedSigners}; +use itertools::Itertools; +use multisig::verifier_set::VerifierSet; +use sha3::{Digest, Keccak256}; -sol!("src/encoding/abi/solidity/AmplifierGatewayTypes.sol"); -sol!("src/encoding/abi/solidity/WeightedMultisigTypes.sol"); +use crate::error::ContractError; +use crate::payload::Payload; impl From<&Payload> for CommandType { fn from(payload: &Payload) -> Self { @@ -28,68 +20,15 @@ impl From<&Payload> for CommandType { } } -impl WeightedSigners { - pub fn hash(&self) -> Hash { - Keccak256::digest(self.abi_encode()).into() - } -} - -impl From<&Signer> for WeightedSigner { - fn from(signer: &Signer) -> Self { - WeightedSigner { - signer: evm_address(&signer.pub_key).expect("failed to convert pub key to evm address"), - weight: signer.weight.u128(), - } - } -} - -impl From<&VerifierSet> for WeightedSigners { - fn from(verifier_set: &VerifierSet) -> Self { - let mut signers = verifier_set - .signers - .values() - .map(WeightedSigner::from) - .collect::>(); - - signers.sort_by_key(|weighted_signer| weighted_signer.signer); - - WeightedSigners { - signers, - threshold: verifier_set.threshold.u128(), - nonce: Uint256::from(verifier_set.created_at).to_be_bytes().into(), - } - } -} - -impl TryFrom<&RouterMessage> for Message { - type Error = ContractError; - - fn try_from(msg: &RouterMessage) -> Result { - let contract_address = - Address::from_str(msg.destination_address.as_str()).map_err(|err| { - ContractError::InvalidMessage { - reason: format!("destination_address is not a valid EVM address: {}", err), - } - })?; - - let payload_hash = FixedBytes::<32>::from_slice(msg.payload_hash.as_slice()); - - Ok(Message { - sourceChain: msg.cc_id.chain.to_string(), - messageId: msg.cc_id.id.to_string(), - sourceAddress: msg.source_address.to_string(), - contractAddress: contract_address, - payloadHash: payload_hash, - }) - } -} - -pub fn payload_hash_to_sign( +pub fn payload_digest( domain_separator: &Hash, signer: &VerifierSet, payload: &Payload, ) -> Result { - let signer_hash = WeightedSigners::from(signer).hash(); + let signer_hash = WeightedSigners::try_from(signer) + .map(|signers| signers.hash()) + .change_context(ContractError::InvalidVerifierSet)?; + let data_hash = Keccak256::digest(encode(payload)?); // Prefix for standard EVM signed data https://eips.ethereum.org/EIPS/eip-191 @@ -105,34 +44,27 @@ pub fn payload_hash_to_sign( } pub fn encode(payload: &Payload) -> Result, ContractError> { - let command_type = CommandType::from(payload); + let command_type = CommandType::from(payload).into(); match payload { Payload::Messages(messages) => { - let messages: Vec<_> = messages + let messages = messages .iter() .map(Message::try_from) - .collect::>()?; + .map_ok(|m| Token::Tuple(m.into_tokens())) + .collect::, _>>() + .change_context(ContractError::InvalidMessage)?; - Ok((command_type, messages).abi_encode_sequence()) - } - Payload::VerifierSet(verifier_set) => { - Ok((command_type, WeightedSigners::from(verifier_set)).abi_encode_sequence()) + Ok(abi_encode(&[command_type, Token::Array(messages)])) } - } -} - -fn evm_address(pub_key: &MultisigPublicKey) -> Result { - match pub_key { - MultisigPublicKey::Ecdsa(pub_key) => PublicKey::from_sec1_bytes(pub_key) - .map(|pub_key| pub_key.to_encoded_point(false)) - .map(|pub_key| Address::from_raw_public_key(&pub_key.as_bytes()[1..])) - .map_err(|err| ContractError::InvalidPublicKey { - reason: err.to_string(), - }), - _ => Err(ContractError::InvalidPublicKey { - reason: "expect ECDSA public key".to_string(), - }), + Payload::VerifierSet(verifier_set) => Ok(abi_encode(&[ + command_type, + Token::Tuple( + WeightedSigners::try_from(verifier_set) + .change_context(ContractError::InvalidVerifierSet)? + .into_tokens(), + ), + ])), } } @@ -140,40 +72,19 @@ fn evm_address(pub_key: &MultisigPublicKey) -> Result { mod tests { use cosmwasm_std::HexBinary; - use router_api::{CrossChainId, Message as RouterMessage}; - - use crate::{ - encoding::abi::{payload_hash_to_sign, CommandType, Message, WeightedSigners}, - payload::Payload, - test::test_data::{ - curr_verifier_set, domain_separator, messages, new_verifier_set, - verifier_set_from_pub_keys, - }, + use crate::encoding::abi::{payload_digest, CommandType}; + use crate::payload::Payload; + use crate::test::test_data::{ + curr_verifier_set, domain_separator, messages, new_verifier_set, verifier_set_from_pub_keys, }; #[test] fn command_type_from_payload() { let payload = Payload::Messages(vec![]); - assert_eq!( - CommandType::from(&payload).as_u8(), - CommandType::ApproveMessages.as_u8() - ); + assert_eq!(CommandType::from(&payload), CommandType::ApproveMessages); let payload = Payload::VerifierSet(new_verifier_set()); - assert_eq!( - CommandType::from(&payload).as_u8(), - CommandType::RotateSigners.as_u8() - ); - } - - #[test] - fn weight_signers_hash() { - let expected_hash = - HexBinary::from_hex("e490c7e55a46b0e1e39a3034973b676eed044fed387f80f4e6377305313f8762") - .unwrap(); - let verifier_set = curr_verifier_set(); - - assert_eq!(WeightedSigners::from(&verifier_set).hash(), expected_hash); + assert_eq!(CommandType::from(&payload), CommandType::RotateSigners); } #[test] @@ -193,7 +104,7 @@ mod tests { ]; let new_verifier_set = verifier_set_from_pub_keys(new_pub_keys); - let msg_to_sign = payload_hash_to_sign( + let msg_to_sign = payload_digest( &domain_separator, &curr_verifier_set(), &Payload::VerifierSet(new_verifier_set), @@ -202,43 +113,6 @@ mod tests { assert_eq!(msg_to_sign, expected_hash); } - #[test] - fn router_message_to_gateway_message() { - let source_chain = "chain0"; - let message_id = "0xff822c88807859ff226b58e24f24974a70f04b9442501ae38fd665b3c68f3834-0"; - let source_address = "0x52444f1835Adc02086c37Cb226561605e2E1699b"; - let destination_chain = "chain1"; - let destination_address = "0xA4f10f76B86E01B98daF66A3d02a65e14adb0767"; - let payload_hash = "8c3685dc41c2eca11426f8035742fb97ea9f14931152670a5703f18fe8b392f0"; - - let router_messages = RouterMessage { - cc_id: CrossChainId { - chain: source_chain.parse().unwrap(), - id: message_id.parse().unwrap(), - }, - source_address: source_address.parse().unwrap(), - destination_address: destination_address.parse().unwrap(), - destination_chain: destination_chain.parse().unwrap(), - payload_hash: HexBinary::from_hex(payload_hash) - .unwrap() - .to_array::<32>() - .unwrap(), - }; - - let gateway_message = Message::try_from(&router_messages).unwrap(); - assert_eq!(gateway_message.sourceChain, source_chain); - assert_eq!(gateway_message.messageId, message_id); - assert_eq!(gateway_message.sourceAddress, source_address); - assert_eq!( - gateway_message.contractAddress.to_string(), - destination_address - ); - assert_eq!( - gateway_message.payloadHash.to_string()[2..], - payload_hash.to_string() - ); - } - #[test] fn approve_messages_hash() { // generated by axelar-gmp-sdk-solidity unit tests @@ -248,7 +122,7 @@ mod tests { let domain_separator = domain_separator(); - let digest = payload_hash_to_sign( + let digest = payload_digest( &domain_separator, &curr_verifier_set(), &Payload::Messages(messages()), diff --git a/contracts/multisig-prover/src/encoding/abi/solidity/AmplifierGatewayTypes.sol b/contracts/multisig-prover/src/encoding/abi/solidity/AmplifierGatewayTypes.sol deleted file mode 100644 index 6fd019e09..000000000 --- a/contracts/multisig-prover/src/encoding/abi/solidity/AmplifierGatewayTypes.sol +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -/** - * @notice This enum represents the different types of commands that can be processed by the Axelar Amplifier Gateway - */ -enum CommandType { - ApproveMessages, - RotateSigners -} - -/** - * @notice This struct represents a message that is to be processed by the Amplifier Gateway - * @param sourceChain The chain from which the message originated - * @param messageId The unique identifier for the message - * @param sourceAddress The address from which the message originated - * @param contractAddress The address of the contract that the message is intended for - * @param payloadHash The hash of the payload that is to be processed - */ -struct Message { - string sourceChain; - string messageId; - string sourceAddress; - address contractAddress; - bytes32 payloadHash; -} diff --git a/contracts/multisig-prover/src/encoding/abi/solidity/WeightedMultisigTypes.sol b/contracts/multisig-prover/src/encoding/abi/solidity/WeightedMultisigTypes.sol deleted file mode 100644 index 63578e177..000000000 --- a/contracts/multisig-prover/src/encoding/abi/solidity/WeightedMultisigTypes.sol +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -/** - * @notice This struct represents the weighted signers payload - * @param signers The list of signers - * @param weights The list of weights - * @param threshold The threshold for the signers - */ -struct WeightedSigner { - address signer; - uint128 weight; -} - -/** - * @notice This struct represents the weighted signers payload - * @param signers The list of weighted signers - * @param threshold The threshold for the weighted signers - * @param nonce The nonce to distinguish different weighted signer sets - */ -struct WeightedSigners { - WeightedSigner[] signers; - uint128 threshold; - bytes32 nonce; -} - -/** - * @notice This struct represents a proof for a message from the weighted signers - * @param signers The weighted signers - * @param signatures The list of signatures - */ -struct Proof { - WeightedSigners signers; - bytes[] signatures; -} diff --git a/contracts/multisig-prover/src/encoding/bcs.rs b/contracts/multisig-prover/src/encoding/bcs.rs new file mode 100644 index 000000000..b56c35746 --- /dev/null +++ b/contracts/multisig-prover/src/encoding/bcs.rs @@ -0,0 +1,288 @@ +use std::iter; + +use axelar_wasm_std::hash::Hash; +use cosmwasm_std::HexBinary; +use error_stack::{Result, ResultExt}; +use multisig::msg::SignerWithSig; +use multisig::verifier_set::VerifierSet; +use sha3::{Digest, Keccak256}; +use sui_gateway::{CommandType, ExecuteData, Message, MessageToSign, Proof, WeightedSigners}; + +use crate::encoding::{to_recoverable, Encoder}; +use crate::error::ContractError; +use crate::payload::Payload; + +fn encode_payload(payload: &Payload) -> Result, ContractError> { + let encoded: Vec = match payload { + Payload::Messages(messages) => bcs::to_bytes( + &messages + .iter() + .map(Message::try_from) + .collect::, _>>() + .change_context(ContractError::InvalidMessage)?, + ) + .expect("failed to serialize messages"), + Payload::VerifierSet(verifier_set) => bcs::to_bytes( + &WeightedSigners::try_from(verifier_set.clone()) + .change_context(ContractError::InvalidVerifierSet)?, + ) + .expect("failed to weighted signers"), + }; + + Ok(encoded) +} + +pub fn payload_digest( + domain_separator: &Hash, + verifier_set: &VerifierSet, + payload: &Payload, +) -> Result { + let command_type = match payload { + Payload::Messages(_) => CommandType::ApproveMessages, + Payload::VerifierSet(_) => CommandType::RotateSigners, + }; + let data = iter::once(command_type as u8) + .chain(encode_payload(payload)?) + .collect::>(); + let msg = MessageToSign { + domain_separator: (*domain_separator).into(), + signers_hash: WeightedSigners::try_from(verifier_set.clone()) + .change_context(ContractError::InvalidVerifierSet)? + .hash() + .into(), + data_hash: <[u8; 32]>::from(Keccak256::digest(data)).into(), + }; + + Ok(msg.hash()) +} + +/// `encode_execute_data` returns the BCS encoded execute data that contains the payload and the proof. +/// The relayer will use this data to submit the payload to the contract. +pub fn encode_execute_data( + verifier_set: &VerifierSet, + signatures: Vec, + payload_digest: &Hash, + payload: &Payload, +) -> Result { + let signatures = to_recoverable(Encoder::Bcs, payload_digest, signatures); + + let encoded_payload = encode_payload(payload)?; + let encoded_proof = bcs::to_bytes( + &Proof::try_from((verifier_set.clone(), signatures)) + .change_context(ContractError::Proof)?, + ) + .expect("failed to serialize proof"); + let execute_data = ExecuteData::new(encoded_payload, encoded_proof); + + Ok(bcs::to_bytes(&execute_data) + .expect("failed to serialize execute data") + .into()) +} + +#[cfg(test)] +mod tests { + use axelar_wasm_std::hash::Hash; + use cosmwasm_std::{Addr, HexBinary, Uint128}; + use multisig::key::KeyType; + use multisig::msg::Signer; + use multisig::verifier_set::VerifierSet; + use router_api::{CrossChainId, Message}; + + use super::payload_digest; + use crate::payload::Payload; + + #[test] + fn payload_digest_should_encode_correctly_for_verifier_set() { + let verifier_set = VerifierSet { + signers: vec![ + ( + "addr_1".to_string(), + Signer { + address: Addr::unchecked("addr_1"), + pub_key: ( + KeyType::Ecdsa, + HexBinary::from_hex("02a7ecca982c2d9ac150c629699c4c601032b42429b418799d6c08ce7d966f518b").unwrap(), + ) + .try_into() + .unwrap(), + weight: Uint128::one(), + }, + ), + ( + "addr_2".to_string(), + Signer { + address: Addr::unchecked("addr_2"), + pub_key: ( + KeyType::Ecdsa, + HexBinary::from_hex("023e44013597b8a49df193265ae443e1a9970626d4df92e4ebad677ab2aca5c13a").unwrap(), + ) + .try_into() + .unwrap(), + weight: Uint128::one(), + }, + ), + ( + "addr_3".to_string(), + Signer { + address: Addr::unchecked("addr_3"), + pub_key: ( + KeyType::Ecdsa, + HexBinary::from_hex("024fa6a34ec85dc2618d730ad68ab914ccc492e54b573172083c0fa44465f54dcc").unwrap(), + ) + .try_into() + .unwrap(), + weight: Uint128::one(), + }, + ), + ] + .into_iter() + .collect(), + threshold: 2u128.into(), + created_at: 2024, + }; + let payload = Payload::VerifierSet(VerifierSet { + signers: vec![ + ( + "addr_1".to_string(), + Signer { + address: Addr::unchecked("addr_1"), + pub_key: ( + KeyType::Ecdsa, + HexBinary::from_hex("0309613c4ae8b9ac87bdb3c4ff240a7e5f905f59754f377ccf54fbd8ce0e8ba636").unwrap(), + ) + .try_into() + .unwrap(), + weight: Uint128::one(), + }, + ), + ( + "addr_2".to_string(), + Signer { + address: Addr::unchecked("addr_2"), + pub_key: ( + KeyType::Ecdsa, + HexBinary::from_hex("032b14344bda89d1a0f1a976af94648f1b4a5df5397d008f3b2032267c11eda7c9").unwrap(), + ) + .try_into() + .unwrap(), + weight: Uint128::one(), + }, + ), + ( + "addr_3".to_string(), + Signer { + address: Addr::unchecked("addr_3"), + pub_key: ( + KeyType::Ecdsa, + HexBinary::from_hex("031fb4e7844794a28bc49e8a702c984031d8627befea844932a02e5e918f59f610").unwrap(), + ) + .try_into() + .unwrap(), + weight: Uint128::one(), + }, + ), + ] + .into_iter() + .collect(), + threshold: 2u128.into(), + created_at: 2025, + }); + + goldie::assert!(hex::encode( + payload_digest(&Hash::from([1; 32]), &verifier_set, &payload).unwrap() + )); + } + + #[test] + fn payload_digest_should_encode_correctly_for_messages() { + let verifier_set = VerifierSet { + signers: vec![ + ( + "addr_1".to_string(), + Signer { + address: Addr::unchecked("addr_1"), + pub_key: ( + KeyType::Ecdsa, + HexBinary::from_hex("02a7ecca982c2d9ac150c629699c4c601032b42429b418799d6c08ce7d966f518b").unwrap(), + ) + .try_into() + .unwrap(), + weight: Uint128::one(), + }, + ), + ( + "addr_2".to_string(), + Signer { + address: Addr::unchecked("addr_2"), + pub_key: ( + KeyType::Ecdsa, + HexBinary::from_hex("023e44013597b8a49df193265ae443e1a9970626d4df92e4ebad677ab2aca5c13a").unwrap(), + ) + .try_into() + .unwrap(), + weight: Uint128::one(), + }, + ), + ( + "addr_3".to_string(), + Signer { + address: Addr::unchecked("addr_3"), + pub_key: ( + KeyType::Ecdsa, + HexBinary::from_hex("024fa6a34ec85dc2618d730ad68ab914ccc492e54b573172083c0fa44465f54dcc").unwrap(), + ) + .try_into() + .unwrap(), + weight: Uint128::one(), + }, + ), + ] + .into_iter() + .collect(), + threshold: 2u128.into(), + created_at: 2024, + }; + let payload = Payload::Messages(vec![ + Message { + cc_id: CrossChainId { + source_chain: "ethereum".parse().unwrap(), + message_id: + "0xbb9b5566c2f4876863333e481f4698350154259ffe6226e283b16ce18a64bcf1:0" + .parse() + .unwrap(), + }, + source_address: "0x1a68E002efa42CF3bDEF81d66bB41f9d677420bE" + .parse() + .unwrap(), + destination_chain: "sui".parse().unwrap(), + destination_address: + "0xdf4dd40feff3c09bb5c559d0cfd7d1c5025fa802bba275453e48af7d2b437727" + .parse() + .unwrap(), + payload_hash: [2; 32], + }, + Message { + cc_id: CrossChainId { + source_chain: "ethereum".parse().unwrap(), + message_id: + "0xd695e1ee9d73aeee677d4cec13d17351c1e86a0ce49b7fd3de94350e9cd0b3a9:1" + .parse() + .unwrap(), + }, + source_address: "0x876EabF441B2EE5B5b0554Fd502a8E0600950cFa" + .parse() + .unwrap(), + destination_chain: "sui".parse().unwrap(), + destination_address: + "0x7bcef829e138fb8fff88671514597313153b9f5501a282bee68a2d9b66aa66e8" + .parse() + .unwrap(), + payload_hash: [3; 32], + }, + ]); + + goldie::assert!(hex::encode( + payload_digest(&Hash::from([1; 32]), &verifier_set, &payload).unwrap() + )); + } +} diff --git a/contracts/multisig-prover/src/encoding/mod.rs b/contracts/multisig-prover/src/encoding/mod.rs index 3ab989f42..25941cd67 100644 --- a/contracts/multisig-prover/src/encoding/mod.rs +++ b/contracts/multisig-prover/src/encoding/mod.rs @@ -1,10 +1,99 @@ -pub mod abi; +mod abi; +mod bcs; +mod stellar_xdr; +use axelar_wasm_std::hash::Hash; use cosmwasm_schema::cw_serde; +use cosmwasm_std::HexBinary; +use error_stack::Result; +use multisig::key::Signature; +use multisig::msg::SignerWithSig; +use multisig::verifier_set::VerifierSet; + +use crate::error::ContractError; +use crate::payload::Payload; #[cw_serde] #[derive(Copy)] pub enum Encoder { Abi, Bcs, + StellarXdr, +} + +impl Encoder { + pub fn digest( + &self, + domain_separator: &Hash, + verifier_set: &VerifierSet, + payload: &Payload, + ) -> Result { + match self { + Encoder::Abi => abi::payload_digest(domain_separator, verifier_set, payload), + Encoder::Bcs => bcs::payload_digest(domain_separator, verifier_set, payload), + Encoder::StellarXdr => { + stellar_xdr::payload_digest(domain_separator, verifier_set, payload) + } + } + } + + pub fn execute_data( + &self, + domain_separator: &Hash, + verifier_set: &VerifierSet, + sigs: Vec, + payload: &Payload, + ) -> Result { + match self { + Encoder::Abi => abi::execute_data::encode( + verifier_set, + sigs, + &self.digest(domain_separator, verifier_set, payload)?, + payload, + ), + Encoder::Bcs => bcs::encode_execute_data( + verifier_set, + sigs, + &self.digest(domain_separator, verifier_set, payload)?, + payload, + ), + Encoder::StellarXdr => todo!(), + } + } +} + +// Convert non-recoverable ECDSA signatures to recoverable ones. +fn to_recoverable(encoder: Encoder, msg: M, signers: Vec) -> Vec +where + M: AsRef<[u8]>, +{ + let recovery_transform = match encoder { + Encoder::Abi => add_27, + Encoder::Bcs => no_op, + Encoder::StellarXdr => no_op, + }; + signers + .into_iter() + .map(|mut signer| { + if let Signature::Ecdsa(nonrecoverable) = signer.signature { + signer.signature = nonrecoverable + .to_recoverable(msg.as_ref(), &signer.signer.pub_key, recovery_transform) + .map(Signature::EcdsaRecoverable) + .expect("failed to convert non-recoverable signature to recoverable"); + } + + signer + }) + .collect() +} + +fn add_27(recovery_byte: k256::ecdsa::RecoveryId) -> u8 { + recovery_byte + .to_byte() + .checked_add(27) + .expect("overflow when adding 27 to recovery byte") +} + +fn no_op(recovery_byte: k256::ecdsa::RecoveryId) -> u8 { + recovery_byte.to_byte() } diff --git a/contracts/multisig-prover/src/encoding/stellar_xdr.rs b/contracts/multisig-prover/src/encoding/stellar_xdr.rs new file mode 100644 index 000000000..ffb0c55f0 --- /dev/null +++ b/contracts/multisig-prover/src/encoding/stellar_xdr.rs @@ -0,0 +1,196 @@ +use axelar_wasm_std::hash::Hash; +use axelar_wasm_std::FnExt; +use error_stack::{Result, ResultExt}; +use multisig::verifier_set::VerifierSet; +use sha3::{Digest, Keccak256}; +use stellar::{Message, Messages, WeightedSigners}; + +use crate::error::ContractError; +use crate::payload::Payload; + +pub fn payload_digest( + domain_separator: &Hash, + verifier_set: &VerifierSet, + payload: &Payload, +) -> Result { + let data_hash = match payload { + Payload::Messages(messages) => messages + .iter() + .map(Message::try_from) + .collect::, _>>() + .change_context(ContractError::InvalidMessage)? + .then(Messages::from) + .messages_approval_hash(), + Payload::VerifierSet(verifier_set) => WeightedSigners::try_from(verifier_set) + .change_context(ContractError::InvalidVerifierSet)? + .signers_rotation_hash(), + } + .change_context(ContractError::SerializeData)?; + + let signers_hash = WeightedSigners::try_from(verifier_set) + .change_context(ContractError::InvalidVerifierSet)? + .hash() + .change_context(ContractError::SerializeData)?; + + let unsigned = [ + domain_separator, + signers_hash.as_slice(), + data_hash.as_slice(), + ] + .concat(); + + Ok(Keccak256::digest(unsigned).into()) +} + +#[cfg(test)] +mod tests { + use cosmwasm_std::{Addr, HexBinary, Uint128}; + use multisig::key::KeyType; + use multisig::msg::Signer; + use multisig::verifier_set::VerifierSet; + use router_api::{CrossChainId, Message}; + + use crate::encoding::stellar_xdr::payload_digest; + use crate::payload::Payload; + + #[test] + fn stellar_messages_payload_digest() { + let signers_data = vec![ + ( + "addr_1", + "508bcac3df50837e0b093aebc549211ba72bd1e7c1830a288b816b677d62a046", + 9u128, + ), + ( + "addr_2", + "5c186341e6392ff06b35b2b80a05f99cdd1dd7d5b436f2eef1a6dd08c07c9463", + 4u128, + ), + ( + "addr_3", + "78c860cbba0b74a728bdc2ae05feef5a14c8903f59d59525ed5bea9b52027d0e", + 3u128, + ), + ( + "addr_4", + "ac1276368dab35ecc413c5008f184df4005e8773ea44ce3c980bc3dbe45f7521", + 3u128, + ), + ( + "addr_4", + "856d2aedc159b543f3150fd9e013ed5cc4d5d32659595e7bedbec279c28ccbe0", + 5u128, + ), + ( + "addr_5", + "e2a6a040c4a31f8131651fb669d514066963e2fde91feb86350d494a6e02f0fa", + 6u128, + ), + ]; + let verifier_set = gen_veifier_set(signers_data, 22, 2024); + + let payload = Payload::Messages(vec![Message { + cc_id: CrossChainId { + source_chain: "source".parse().unwrap(), + message_id: "test".parse().unwrap(), + }, + source_address: "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + .parse() + .unwrap(), + destination_chain: "stellar".parse().unwrap(), + destination_address: "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + .parse() + .unwrap(), + payload_hash: HexBinary::from_hex( + "65ad329dc342a82bd1daedc42e183e6e2c272b8e2e3fd7c8f81d089736d0bc3c", + ) + .unwrap() + .to_array() + .unwrap(), + }]); + let domain_separator: [u8; 32] = + HexBinary::from_hex("2a15376c1277252b1bcce5a6ecd781bfbc2697dfd969ff58d8e2e116018b501e") + .unwrap() + .to_array() + .unwrap(); + goldie::assert!(hex::encode( + payload_digest(&domain_separator, &verifier_set, &payload).unwrap() + )); + } + + #[test] + fn stellar_verifier_set_payload_digest() { + let verifier_set = gen_veifier_set( + vec![( + "addr_1", + "bf95c447eb2e694974ee2cf5f17e7165bc884a0cb676bb4de50c604bb7a6ea77", + 4u128, + )], + 1, + 2024, + ); + let signers_data = vec![ + ( + "addr_1", + "5086d25f94b8c42faf7ef4325516864e179fcb2a1a9321720f0fc2b249105106", + 5u128, + ), + ( + "addr_2", + "57a446f70d8243b7d5e08edcd9c5774f3f0257940df7aa84bca5b1acfc0f3ba3", + 7u128, + ), + ( + "addr_3", + "5a3211139cca5cee83096e8009aadf6405d84f5137706bc1db68f53cbb202054", + 9u128, + ), + ( + "addr_4", + "9d8774a24acce628658dc93e41c56972ded010c07b731306b54282890113d60f", + 7u128, + ), + ( + "addr_5", + "a99083342953620013c9c61f8000a8778915337632ac601458c6c93387d963f5", + 7u128, + ), + ]; + let payload = Payload::VerifierSet(gen_veifier_set(signers_data, 27, 2024)); + let domain_separator: [u8; 32] = + HexBinary::from_hex("6773bd037510492f863cba62a0f3c55ac846883f33cae7266aff8be5eb9681e8") + .unwrap() + .to_array() + .unwrap(); + + goldie::assert!(hex::encode( + payload_digest(&domain_separator, &verifier_set, &payload).unwrap() + )); + } + + fn gen_veifier_set( + signers_data: Vec<(&str, &str, u128)>, + threshold: u128, + created_at: u64, + ) -> VerifierSet { + VerifierSet { + signers: signers_data + .into_iter() + .map(|(addr, pub_key, weight)| { + ( + addr.to_string(), + Signer { + address: Addr::unchecked(addr), + pub_key: (KeyType::Ed25519, HexBinary::from_hex(pub_key).unwrap()) + .try_into() + .unwrap(), + weight: Uint128::from(weight), + }, + ) + }) + .collect(), + threshold: threshold.into(), + created_at, + } + } +} diff --git a/contracts/multisig-prover/src/encoding/testdata/payload_digest_should_encode_correctly_for_messages.golden b/contracts/multisig-prover/src/encoding/testdata/payload_digest_should_encode_correctly_for_messages.golden new file mode 100644 index 000000000..3083f74e5 --- /dev/null +++ b/contracts/multisig-prover/src/encoding/testdata/payload_digest_should_encode_correctly_for_messages.golden @@ -0,0 +1 @@ +26d564a2e0f5336170857e4a3436ac05f3883ed293dbf674aa0787478723a1af \ No newline at end of file diff --git a/contracts/multisig-prover/src/encoding/testdata/payload_digest_should_encode_correctly_for_verifier_set.golden b/contracts/multisig-prover/src/encoding/testdata/payload_digest_should_encode_correctly_for_verifier_set.golden new file mode 100644 index 000000000..5e4e52923 --- /dev/null +++ b/contracts/multisig-prover/src/encoding/testdata/payload_digest_should_encode_correctly_for_verifier_set.golden @@ -0,0 +1 @@ +1775806969d1e2e6bac62484ed8d7be8c6c48c2f6bb4075ee60a45548140aaa9 \ No newline at end of file diff --git a/contracts/multisig-prover/src/encoding/testdata/stellar_messages_payload_digest.golden b/contracts/multisig-prover/src/encoding/testdata/stellar_messages_payload_digest.golden new file mode 100644 index 000000000..82b3f830f --- /dev/null +++ b/contracts/multisig-prover/src/encoding/testdata/stellar_messages_payload_digest.golden @@ -0,0 +1 @@ +0d29098e3f5a7ead5c97c0f8e74051e1de9cb601105551f8d4a898187e0f4f0d \ No newline at end of file diff --git a/contracts/multisig-prover/src/encoding/testdata/stellar_verifier_set_payload_digest.golden b/contracts/multisig-prover/src/encoding/testdata/stellar_verifier_set_payload_digest.golden new file mode 100644 index 000000000..c10388061 --- /dev/null +++ b/contracts/multisig-prover/src/encoding/testdata/stellar_verifier_set_payload_digest.golden @@ -0,0 +1 @@ +80097627445437998d5e4b6689ec9b5dcba64cdd5f4580e490f173cefe78609f \ No newline at end of file diff --git a/contracts/multisig-prover/src/error.rs b/contracts/multisig-prover/src/error.rs index d95d0e1fd..cafef424e 100644 --- a/contracts/multisig-prover/src/error.rs +++ b/contracts/multisig-prover/src/error.rs @@ -1,6 +1,6 @@ -use axelar_wasm_std::nonempty; -use axelar_wasm_std_derive::IntoContractError; +use axelar_wasm_std::{nonempty, IntoContractError}; use cosmwasm_std::StdError; +use router_api::ChainName; use thiserror::Error; #[derive(Error, Debug, PartialEq, IntoContractError)] @@ -8,11 +8,8 @@ pub enum ContractError { #[error(transparent)] Std(#[from] StdError), - #[error("caller is not authorized")] - Unauthorized, - - #[error("message is invalid: {reason}")] - InvalidMessage { reason: String }, + #[error("message is invalid")] + InvalidMessage, #[error("public key is invalid: {reason}")] InvalidPublicKey { reason: String }, @@ -21,6 +18,7 @@ pub enum ContractError { InvalidSignature { reason: String }, #[error("chain name is invalid")] + #[deprecated(since = "0.6.0")] InvalidChainName, #[error("invalid participants: {reason}")] @@ -50,9 +48,33 @@ pub enum ContractError { #[error("a verifier set confirmation already in progress")] VerifierSetConfirmationInProgress, + #[error("no verifier set to confirm")] + NoVerifierSetToConfirm, + #[error("no verifier set stored")] NoVerifierSet, #[error("failed to serialize the response")] SerializeResponse, + + #[error("failed to create proof")] + Proof, + + #[error("invalid verifier set")] + InvalidVerifierSet, + + #[error("not enough verifiers")] + NotEnoughVerifiers, + + #[error("invalid destination chain '{actual}', expected '{expected}'")] + InvalidDestinationChain { + actual: ChainName, + expected: ChainName, + }, + + #[error("payload does not match the stored value")] + PayloadMismatch, + + #[error("failed to serialize data for the external gateway")] + SerializeData, } diff --git a/contracts/multisig-prover/src/events.rs b/contracts/multisig-prover/src/events.rs index 7f17e4a11..0992a7637 100644 --- a/contracts/multisig-prover/src/events.rs +++ b/contracts/multisig-prover/src/events.rs @@ -47,27 +47,36 @@ impl From for cosmwasm_std::Event { #[cfg(test)] mod tests { - use super::*; + use router_api::Message; use serde_json::to_string; + use super::*; + use crate::payload::Payload; + #[test] fn proof_under_construction_is_serializable() { - let msg_ids = vec![ - CrossChainId { - chain: "ethereum".parse().unwrap(), - id: "some_id".try_into().unwrap(), + let payload = Payload::Messages(vec![ + Message { + cc_id: CrossChainId::new("ethereum", "some-id").unwrap(), + source_address: "0x1234".parse().unwrap(), + destination_chain: "avalanche".parse().unwrap(), + destination_address: "0x5678".parse().unwrap(), + payload_hash: [0; 32], }, - CrossChainId { - chain: "fantom".parse().unwrap(), - id: "some_other_id".try_into().unwrap(), + Message { + cc_id: CrossChainId::new("fantom", "some-other-id").unwrap(), + source_address: "0x1234".parse().unwrap(), + destination_chain: "avalanche".parse().unwrap(), + destination_address: "0x5678".parse().unwrap(), + payload_hash: [0; 32], }, - ]; + ]); let event = Event::ProofUnderConstruction { destination_chain: "avalanche".parse().unwrap(), - payload_id: (&msg_ids).into(), + payload_id: payload.id(), multisig_session_id: Uint64::new(2), - msg_ids, + msg_ids: payload.message_ids().unwrap(), }; assert!(to_string(&cosmwasm_std::Event::from(event)).is_ok()); diff --git a/contracts/multisig-prover/src/lib.rs b/contracts/multisig-prover/src/lib.rs index 4ccc635dd..0fea1d56d 100644 --- a/contracts/multisig-prover/src/lib.rs +++ b/contracts/multisig-prover/src/lib.rs @@ -1,15 +1,13 @@ pub mod contract; -pub mod encoding; +mod encoding; pub mod error; pub mod events; -mod execute; -mod migrations; pub mod msg; -pub mod payload; -mod query; -mod reply; -pub mod state; -pub mod types; +mod payload; +mod state; + +pub use encoding::Encoder; +pub use payload::Payload; #[cfg(test)] mod test; diff --git a/contracts/multisig-prover/src/migrations/mod.rs b/contracts/multisig-prover/src/migrations/mod.rs deleted file mode 100644 index 40f174826..000000000 --- a/contracts/multisig-prover/src/migrations/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod v_0_5; diff --git a/contracts/multisig-prover/src/migrations/v_0_5.rs b/contracts/multisig-prover/src/migrations/v_0_5.rs deleted file mode 100644 index cb5cf32d3..000000000 --- a/contracts/multisig-prover/src/migrations/v_0_5.rs +++ /dev/null @@ -1,187 +0,0 @@ -use axelar_wasm_std::{hash::Hash, FnExt, MajorityThreshold}; -use cosmwasm_schema::cw_serde; -use cosmwasm_std::{from_json, Addr, DepsMut, Response}; -use cw_storage_plus::Item; -use multisig::{key::KeyType, verifier_set::VerifierSet}; -use router_api::ChainName; - -use crate::{ - encoding::Encoder, - state::{Config, CONFIG, CURRENT_VERIFIER_SET, NEXT_VERIFIER_SET}, -}; - -const CURRENT_WORKER_SET: Item = Item::new("current_worker_set"); -const NEXT_WORKER_SET: Item = Item::new("next_worker_set"); - -#[cw_serde] -pub struct OldConfig { - pub admin: Addr, - pub governance: Addr, - pub gateway: Addr, - pub multisig: Addr, - pub coordinator: Addr, - pub service_registry: Addr, - pub voting_verifier: Addr, - pub signing_threshold: MajorityThreshold, - pub service_name: String, - pub chain_name: ChainName, - pub worker_set_diff_threshold: u32, - pub encoder: Encoder, - pub key_type: KeyType, - pub domain_separator: Hash, -} -impl OldConfig { - pub fn migrate(self) -> Config { - Config { - domain_separator: self.domain_separator, - admin: self.admin, - governance: self.governance, - gateway: self.gateway, - multisig: self.multisig, - coordinator: self.coordinator, - service_registry: self.service_registry, - voting_verifier: self.voting_verifier, - signing_threshold: self.signing_threshold, - service_name: self.service_name, - chain_name: self.chain_name, - verifier_set_diff_threshold: self.worker_set_diff_threshold, - encoder: self.encoder, - key_type: self.key_type, - } - } -} - -pub fn migrate_verifier_sets(deps: DepsMut) -> Result { - let old_config: OldConfig = deps - .storage - .get(CONFIG.as_slice()) - .expect("config not found") - .then(from_json)?; - - let new_config = old_config.migrate(); - CONFIG.save(deps.storage, &new_config)?; - let current_worker_set = CURRENT_WORKER_SET.may_load(deps.storage)?; - if let Some(current_worker_set) = current_worker_set { - CURRENT_WORKER_SET.remove(deps.storage); - CURRENT_VERIFIER_SET.save(deps.storage, ¤t_worker_set)?; - } - - let next_worker_set = NEXT_WORKER_SET.may_load(deps.storage)?; - if let Some(next_worker_set) = next_worker_set { - NEXT_WORKER_SET.remove(deps.storage); - NEXT_VERIFIER_SET.save(deps.storage, &next_worker_set)?; - } - - Ok(Response::default()) -} - -#[cfg(test)] -mod test { - use crate::{ - migrations::v_0_5::{OldConfig, NEXT_WORKER_SET}, - state::{CONFIG, CURRENT_VERIFIER_SET, NEXT_VERIFIER_SET}, - test::test_data::new_verifier_set, - }; - - use axelar_wasm_std::Threshold; - use cosmwasm_std::{testing::mock_dependencies, to_json_vec, Addr, Uint128}; - - use super::{migrate_verifier_sets, CURRENT_WORKER_SET}; - - #[test] - fn should_be_able_to_migrate_worker_set_to_verifier_set() { - let mut deps = mock_dependencies(); - - let initial_config = OldConfig { - admin: Addr::unchecked("admin"), - governance: Addr::unchecked("governance"), - gateway: Addr::unchecked("gateway"), - multisig: Addr::unchecked("multisig"), - coordinator: Addr::unchecked("coordinator"), - service_registry: Addr::unchecked("service_registry"), - voting_verifier: Addr::unchecked("voting_verifier"), - signing_threshold: Threshold::try_from((2, 3)).unwrap().try_into().unwrap(), - service_name: "validators".to_string(), - chain_name: "ganache-0".parse().unwrap(), - worker_set_diff_threshold: 0, - encoder: crate::encoding::Encoder::Abi, - key_type: multisig::key::KeyType::Ecdsa, - domain_separator: [1; 32], - }; - deps.as_mut() - .storage - .set(CONFIG.as_slice(), &to_json_vec(&initial_config).unwrap()); - - let worker_set = new_verifier_set(); - - CURRENT_WORKER_SET - .save(&mut deps.storage, &worker_set) - .unwrap(); - - let res = migrate_verifier_sets(deps.as_mut()); - assert!(res.is_ok()); - - let verifier_set = CURRENT_VERIFIER_SET.load(&deps.storage).unwrap(); - assert_eq!(verifier_set, worker_set); - - assert!(NEXT_VERIFIER_SET.may_load(&deps.storage).unwrap().is_none()); - - assert!(CURRENT_WORKER_SET - .may_load(&deps.storage) - .unwrap() - .is_none()); - - assert!(NEXT_WORKER_SET.may_load(&deps.storage).unwrap().is_none()); - } - - #[test] - fn should_be_able_to_migrate_worker_set_to_verifier_set_mid_rotation() { - let mut deps = mock_dependencies(); - let initial_config = OldConfig { - admin: Addr::unchecked("admin"), - governance: Addr::unchecked("governance"), - gateway: Addr::unchecked("gateway"), - multisig: Addr::unchecked("multisig"), - coordinator: Addr::unchecked("coordinator"), - service_registry: Addr::unchecked("service_registry"), - voting_verifier: Addr::unchecked("voting_verifier"), - signing_threshold: Threshold::try_from((2, 3)).unwrap().try_into().unwrap(), - service_name: "validators".to_string(), - chain_name: "ganache-0".parse().unwrap(), - worker_set_diff_threshold: 0, - encoder: crate::encoding::Encoder::Abi, - key_type: multisig::key::KeyType::Ecdsa, - domain_separator: [1; 32], - }; - deps.as_mut() - .storage - .set(CONFIG.as_slice(), &to_json_vec(&initial_config).unwrap()); - - let worker_set = new_verifier_set(); - - CURRENT_WORKER_SET - .save(&mut deps.storage, &worker_set) - .unwrap(); - - let mut next_worker_set = worker_set.clone(); - next_worker_set.threshold = worker_set.threshold.checked_add(Uint128::one()).unwrap(); - NEXT_WORKER_SET - .save(&mut deps.storage, &next_worker_set) - .unwrap(); - - let res = migrate_verifier_sets(deps.as_mut()); - assert!(res.is_ok()); - - let verifier_set = CURRENT_VERIFIER_SET.load(&deps.storage).unwrap(); - assert_eq!(verifier_set, worker_set); - - let next_verifier_set = NEXT_VERIFIER_SET.load(&deps.storage).unwrap(); - assert_eq!(next_verifier_set, next_worker_set); - - assert!(CURRENT_WORKER_SET - .may_load(&deps.storage) - .unwrap() - .is_none()); - assert!(NEXT_WORKER_SET.may_load(&deps.storage).unwrap().is_none()); - } -} diff --git a/contracts/multisig-prover/src/msg.rs b/contracts/multisig-prover/src/msg.rs index b80a8d486..69b50beaf 100644 --- a/contracts/multisig-prover/src/msg.rs +++ b/contracts/multisig-prover/src/msg.rs @@ -1,10 +1,13 @@ -use axelar_wasm_std::{hash::Hash, MajorityThreshold}; +use axelar_wasm_std::hash::Hash; +use axelar_wasm_std::MajorityThreshold; use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{HexBinary, Uint64}; +use msgs_derive::EnsurePermissions; use multisig::key::KeyType; use router_api::CrossChainId; -use crate::{encoding::Encoder, payload::Payload}; +use crate::encoding::Encoder; +use crate::payload::Payload; #[cw_serde] pub struct InstantiateMsg { @@ -47,38 +50,48 @@ pub struct InstantiateMsg { /// deployed on the destination chain. The multisig contract supports multiple public keys per verifier (each a different type of key), and this /// parameter controls which registered public key to use for signing for each verifier registered to the destination chain. pub key_type: KeyType, - /// an opaque value created to distinguish distinct chains that the external gateway should be initialized with. + /// An opaque value created to distinguish distinct chains that the external gateway should be initialized with. + /// Value must be a String in hex format without `0x`, e.g. "598ba04d225cec385d1ce3cf3c9a076af803aa5c614bc0e0d176f04ac8d28f55". + #[serde(with = "axelar_wasm_std::hex")] // (de)serialization with hex module + #[schemars(with = "String")] // necessary attribute in conjunction with #[serde(with ...)] pub domain_separator: Hash, } #[cw_serde] +#[derive(EnsurePermissions)] pub enum ExecuteMsg { // Start building a proof that includes specified messages // Queries the gateway for actual message contents - ConstructProof { - message_ids: Vec, - }, + #[permission(Any)] + ConstructProof(Vec), + #[permission(Elevated)] UpdateVerifierSet, + + #[permission(Any)] ConfirmVerifierSet, // Updates the signing threshold. The threshold currently in use does not change. // The verifier set must be updated and confirmed for the change to take effect. - // Callable only by governance. + #[permission(Governance)] UpdateSigningThreshold { new_signing_threshold: MajorityThreshold, }, - UpdateAdmin { - new_admin_address: String, - }, + #[permission(Governance)] + UpdateAdmin { new_admin_address: String }, } #[cw_serde] #[derive(QueryResponses)] pub enum QueryMsg { - #[returns(GetProofResponse)] - GetProof { multisig_session_id: Uint64 }, + #[returns(ProofResponse)] + Proof { multisig_session_id: Uint64 }, - #[returns(Option)] - GetVerifierSet, + /// Returns a `VerifierSetResponse` with the current verifier set id and the verifier set itself. + #[returns(Option)] + CurrentVerifierSet, + + /// Returns a `VerifierSetResponse` with the next verifier set id and the verifier set itself. + #[returns(Option)] + NextVerifierSet, } #[cw_serde] @@ -88,9 +101,24 @@ pub enum ProofStatus { } #[cw_serde] -pub struct GetProofResponse { +pub struct ProofResponse { pub multisig_session_id: Uint64, pub message_ids: Vec, pub payload: Payload, pub status: ProofStatus, } + +#[cw_serde] +pub struct VerifierSetResponse { + pub id: String, + pub verifier_set: multisig::verifier_set::VerifierSet, +} + +impl From for VerifierSetResponse { + fn from(set: multisig::verifier_set::VerifierSet) -> Self { + VerifierSetResponse { + id: set.id(), + verifier_set: set, + } + } +} diff --git a/contracts/multisig-prover/src/payload.rs b/contracts/multisig-prover/src/payload.rs index 14082d89e..1daa2d254 100644 --- a/contracts/multisig-prover/src/payload.rs +++ b/contracts/multisig-prover/src/payload.rs @@ -1,17 +1,9 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{from_json, HexBinary, StdResult}; use cw_storage_plus::{Key, KeyDeserialize, PrimaryKey}; -use sha3::{Digest, Keccak256}; - -use axelar_wasm_std::hash::Hash; -use multisig::msg::SignerWithSig; use multisig::verifier_set::VerifierSet; -use router_api::{CrossChainId, Message}; - -use crate::{ - encoding::{abi, Encoder}, - error::ContractError, -}; +use router_api::{CrossChainId, Message, FIELD_DELIMITER}; +use sha3::{Digest, Keccak256}; #[cw_serde] pub enum Payload { @@ -21,31 +13,29 @@ pub enum Payload { impl Payload { /// id returns the unique identifier for the payload, which can be either - /// - the hash of comma separated sorted message ids - /// - the hash of the verifier set + /// - Hash of 0 followed by '_' separated message ids + /// - Hash of 1 followed by the verifier set hash pub fn id(&self) -> PayloadId { - match self { + let hash = match self { Payload::Messages(msgs) => { - let message_ids = msgs - .iter() - .map(|msg| msg.cc_id.clone()) - .collect::>(); + let message_ids: Vec = + msgs.iter().map(|msg| msg.cc_id.to_string()).collect(); - (&message_ids).into() + message_ids.join(&FIELD_DELIMITER.to_string()).into() } - Payload::VerifierSet(verifier_set) => verifier_set.hash().into(), - } + Payload::VerifierSet(verifier_set) => verifier_set.hash().to_vec(), + }; + + let mut id = vec![self.variant_to_u8()]; + id.extend(hash); + + Keccak256::digest(id).to_vec().into() } - pub fn digest( - &self, - encoder: Encoder, - domain_separator: &Hash, - cur_verifier_set: &VerifierSet, - ) -> Result { - match encoder { - Encoder::Abi => abi::payload_hash_to_sign(domain_separator, cur_verifier_set, self), - Encoder::Bcs => todo!(), + fn variant_to_u8(&self) -> u8 { + match self { + Payload::Messages(_) => 0, + Payload::VerifierSet(_) => 1, } } @@ -55,36 +45,13 @@ impl Payload { Payload::VerifierSet(_) => None, } } - pub fn execute_data( - &self, - encoder: Encoder, - domain_separator: &Hash, - verifier_set: &VerifierSet, - signers_with_sigs: Vec, - payload: &Payload, - ) -> Result { - let payload_hash = payload.digest(encoder, domain_separator, verifier_set)?; - - match encoder { - Encoder::Abi => { - abi::execute_data::encode(verifier_set, signers_with_sigs, &payload_hash, payload) - } - Encoder::Bcs => todo!(), - } - } } #[cw_serde] pub struct PayloadId(HexBinary); -impl From for PayloadId { - fn from(id: HexBinary) -> Self { - Self(id) - } -} - -impl From<&[u8]> for PayloadId { - fn from(id: &[u8]) -> Self { +impl From> for PayloadId { + fn from(id: Vec) -> Self { Self(id.into()) } } @@ -108,32 +75,22 @@ impl KeyDeserialize for PayloadId { } } -impl From<&Vec> for PayloadId { - fn from(ids: &Vec) -> Self { - let mut message_ids = ids.iter().map(|id| id.to_string()).collect::>(); - message_ids.sort(); - - Keccak256::digest(message_ids.join(",")).as_slice().into() - } -} - #[cfg(test)] mod test { - use router_api::CrossChainId; - - use crate::{payload::PayloadId, test::test_data}; + use crate::payload::Payload; + use crate::test::test_data; #[test] - fn test_payload_id() { - let messages = test_data::messages(); - let mut message_ids: Vec = - messages.into_iter().map(|msg| msg.cc_id).collect(); + fn payload_messages_id_unchanged() { + let payload = Payload::Messages(test_data::messages()); - let res: PayloadId = (&message_ids).into(); + goldie::assert_json!(payload.id()); + } - message_ids.reverse(); - let res2: PayloadId = (&message_ids).into(); + #[test] + fn payload_verifier_set_id_unchanged() { + let payload = Payload::VerifierSet(test_data::curr_verifier_set()); - assert_eq!(res, res2); + goldie::assert_json!(payload.id()); } } diff --git a/contracts/multisig-prover/src/query.rs b/contracts/multisig-prover/src/query.rs deleted file mode 100644 index dc4a4e218..000000000 --- a/contracts/multisig-prover/src/query.rs +++ /dev/null @@ -1,54 +0,0 @@ -use cosmwasm_std::{to_json_binary, Deps, QueryRequest, StdResult, Uint64, WasmQuery}; - -use multisig::{multisig::Multisig, types::MultisigState, verifier_set::VerifierSet}; - -use crate::{ - error::ContractError, - msg::{GetProofResponse, ProofStatus}, - state::{CONFIG, CURRENT_VERIFIER_SET, MULTISIG_SESSION_PAYLOAD, PAYLOAD}, -}; - -pub fn get_proof( - deps: Deps, - multisig_session_id: Uint64, -) -> Result { - let config = CONFIG.load(deps.storage)?; - - let payload_id = MULTISIG_SESSION_PAYLOAD.load(deps.storage, multisig_session_id.u64())?; - - let query_msg = multisig::msg::QueryMsg::GetMultisig { - session_id: multisig_session_id, - }; - - let multisig: Multisig = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: config.multisig.to_string(), - msg: to_json_binary(&query_msg)?, - }))?; - - let payload = PAYLOAD.load(deps.storage, &payload_id)?; - - let status = match multisig.state { - MultisigState::Pending => ProofStatus::Pending, - MultisigState::Completed { .. } => { - let execute_data = payload.execute_data( - config.encoder, - &config.domain_separator, - &multisig.verifier_set, - multisig.optimize_signatures(), - &payload, - )?; - ProofStatus::Completed { execute_data } - } - }; - - Ok(GetProofResponse { - multisig_session_id, - message_ids: payload.message_ids().unwrap_or_default(), - payload, - status, - }) -} - -pub fn get_verifier_set(deps: Deps) -> StdResult> { - CURRENT_VERIFIER_SET.may_load(deps.storage) -} diff --git a/contracts/multisig-prover/src/state.rs b/contracts/multisig-prover/src/state.rs index 98dd34fe4..babfc35f5 100644 --- a/contracts/multisig-prover/src/state.rs +++ b/contracts/multisig-prover/src/state.rs @@ -1,21 +1,17 @@ +use axelar_wasm_std::hash::Hash; +use axelar_wasm_std::MajorityThreshold; use cosmwasm_schema::cw_serde; use cosmwasm_std::Addr; use cw_storage_plus::{Item, Map}; - -use axelar_wasm_std::{hash::Hash, MajorityThreshold}; use multisig::key::KeyType; use multisig::verifier_set::VerifierSet; use router_api::ChainName; -use crate::{ - encoding::Encoder, - payload::{Payload, PayloadId}, -}; +use crate::encoding::Encoder; +use crate::payload::{Payload, PayloadId}; #[cw_serde] pub struct Config { - pub admin: Addr, - pub governance: Addr, pub gateway: Addr, pub multisig: Addr, pub coordinator: Addr, diff --git a/contracts/multisig-prover/src/test/test_data.rs b/contracts/multisig-prover/src/test/test_data.rs index f817fe1ea..efd0eb1e6 100644 --- a/contracts/multisig-prover/src/test/test_data.rs +++ b/contracts/multisig-prover/src/test/test_data.rs @@ -3,11 +3,9 @@ use std::collections::BTreeMap; use axelar_wasm_std::{nonempty, MajorityThreshold, Participant, Threshold}; use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, HexBinary, Uint128, Uint64}; -use multisig::{ - key::{KeyType, PublicKey, Signature}, - msg::Signer, - verifier_set::VerifierSet, -}; +use multisig::key::{KeyType, Signature}; +use multisig::msg::Signer; +use multisig::verifier_set::VerifierSet; use router_api::{CrossChainId, Message}; pub fn new_verifier_set() -> VerifierSet { @@ -15,7 +13,7 @@ pub fn new_verifier_set() -> VerifierSet { Signer { address: Addr::unchecked("axelarvaloper1x86a8prx97ekkqej2x636utrdu23y8wupp9gk5"), weight: Uint128::from(10u128), - pub_key: PublicKey::Ecdsa( + pub_key: multisig::key::PublicKey::Ecdsa( HexBinary::from_hex( "03d123ce370b163acd576be0e32e436bb7e63262769881d35fa3573943bf6c6f81", ) @@ -25,7 +23,7 @@ pub fn new_verifier_set() -> VerifierSet { Signer { address: Addr::unchecked("axelarvaloper1ff675m593vve8yh82lzhdnqfpu7m23cxstr6h4"), weight: Uint128::from(10u128), - pub_key: PublicKey::Ecdsa( + pub_key: multisig::key::PublicKey::Ecdsa( HexBinary::from_hex( "03c6ddb0fcee7b528da1ef3c9eed8d51eeacd7cc28a8baa25c33037c5562faa6e4", ) @@ -35,7 +33,7 @@ pub fn new_verifier_set() -> VerifierSet { Signer { address: Addr::unchecked("axelarvaloper12cwre2gdhyytc3p97z9autzg27hmu4gfzz4rxc"), weight: Uint128::from(10u128), - pub_key: PublicKey::Ecdsa( + pub_key: multisig::key::PublicKey::Ecdsa( HexBinary::from_hex( "0274b5d2a4c55d7edbbf9cc210c4d25adbb6194d6b444816235c82984bee518255", ) @@ -45,7 +43,7 @@ pub fn new_verifier_set() -> VerifierSet { Signer { address: Addr::unchecked("axelarvaloper1vs9rdplntrf7ceqdkznjmanrr59qcpjq6le0yw"), weight: Uint128::from(10u128), - pub_key: PublicKey::Ecdsa( + pub_key: multisig::key::PublicKey::Ecdsa( HexBinary::from_hex( "02a670f57de55b8b39b4cb051e178ca8fb3fe3a78cdde7f8238baf5e6ce1893185", ) @@ -55,7 +53,7 @@ pub fn new_verifier_set() -> VerifierSet { Signer { address: Addr::unchecked("axelarvaloper1hz0slkejw96dukw87fztjkvwjdpcu20jewg6mw"), weight: Uint128::from(10u128), - pub_key: PublicKey::Ecdsa( + pub_key: multisig::key::PublicKey::Ecdsa( HexBinary::from_hex( "028584592624e742ba154c02df4c0b06e4e8a957ba081083ea9fe5309492aa6c7b", ) @@ -78,12 +76,11 @@ pub fn new_verifier_set() -> VerifierSet { pub fn messages() -> Vec { vec![Message { - cc_id: CrossChainId { - chain: "ganache-1".parse().unwrap(), - id: "0xff822c88807859ff226b58e24f24974a70f04b9442501ae38fd665b3c68f3834-0" - .parse() - .unwrap(), - }, + cc_id: CrossChainId::new( + "ganache-1", + "0xff822c88807859ff226b58e24f24974a70f04b9442501ae38fd665b3c68f3834-0", + ) + .unwrap(), source_address: "0x52444f1835Adc02086c37Cb226561605e2E1699b" .parse() .unwrap(), @@ -204,7 +201,7 @@ pub fn verifier_set_from_pub_keys(pub_keys: Vec<&str>) -> VerifierSet { address: Addr::unchecked(format!("verifier{i}")), weight: nonempty::Uint128::one(), }, - PublicKey::Ecdsa(HexBinary::from_hex(pub_keys[i]).unwrap()), + multisig::key::PublicKey::Ecdsa(HexBinary::from_hex(pub_keys[i]).unwrap()), ) }) .collect(); diff --git a/contracts/multisig-prover/src/test/test_utils.rs b/contracts/multisig-prover/src/test/test_utils.rs index 2032984ee..a177365dc 100644 --- a/contracts/multisig-prover/src/test/test_utils.rs +++ b/contracts/multisig-prover/src/test/test_utils.rs @@ -1,7 +1,10 @@ use axelar_wasm_std::VerificationStatus; -use cosmwasm_std::{from_json, to_json_binary, QuerierResult, WasmQuery}; -use multisig::{msg::Signer, multisig::Multisig, types::MultisigState, verifier_set::VerifierSet}; -use service_registry::state::{ +use cosmwasm_std::{from_json, to_json_binary, Addr, QuerierResult, Uint128, WasmQuery}; +use multisig::msg::Signer; +use multisig::multisig::Multisig; +use multisig::types::MultisigState; +use multisig::verifier_set::VerifierSet; +use service_registry::{ AuthorizationState, BondingState, Verifier, WeightedVerifier, VERIFIER_WEIGHT, }; @@ -27,8 +30,8 @@ pub fn mock_querier_handler( WasmQuery::Smart { contract_addr, msg } if contract_addr == MULTISIG_ADDRESS => { multisig_mock_querier_handler(from_json(msg).unwrap(), operators.clone()) } - WasmQuery::Smart { contract_addr, .. } if contract_addr == SERVICE_REGISTRY_ADDRESS => { - service_registry_mock_querier_handler(operators.clone()) + WasmQuery::Smart { contract_addr, msg } if contract_addr == SERVICE_REGISTRY_ADDRESS => { + service_registry_mock_querier_handler(from_json(msg).unwrap(), operators.clone()) } WasmQuery::Smart { contract_addr, .. } if contract_addr == VOTING_VERIFIER_ADDRESS => { voting_verifier_mock_querier_handler(verifier_set_status) @@ -46,10 +49,10 @@ fn multisig_mock_querier_handler( operators: Vec, ) -> QuerierResult { let result = match msg { - multisig::msg::QueryMsg::GetMultisig { session_id: _ } => { - to_json_binary(&mock_get_multisig(operators)) + multisig::msg::QueryMsg::Multisig { session_id: _ } => { + to_json_binary(&mock_multisig(operators)) } - multisig::msg::QueryMsg::GetPublicKey { + multisig::msg::QueryMsg::PublicKey { verifier_address, key_type: _, } => to_json_binary( @@ -65,7 +68,7 @@ fn multisig_mock_querier_handler( Ok(result.into()).into() } -fn mock_get_multisig(operators: Vec) -> Multisig { +fn mock_multisig(operators: Vec) -> Multisig { let quorum = test_data::quorum(); let signers = operators @@ -109,24 +112,46 @@ fn mock_get_multisig(operators: Vec) -> Multisig { } } -fn service_registry_mock_querier_handler(operators: Vec) -> QuerierResult { - Ok(to_json_binary( - &operators - .clone() - .into_iter() - .map(|op| WeightedVerifier { - verifier_info: Verifier { - address: op.address, - bonding_state: BondingState::Bonded { amount: op.weight }, - authorization_state: AuthorizationState::Authorized, - service_name: SERVICE_NAME.to_string(), - }, - weight: VERIFIER_WEIGHT, +fn service_registry_mock_querier_handler( + msg: service_registry::msg::QueryMsg, + operators: Vec, +) -> QuerierResult { + let result = match msg { + service_registry::msg::QueryMsg::Service { service_name } => { + to_json_binary(&service_registry::Service { + name: service_name.to_string(), + coordinator_contract: Addr::unchecked(COORDINATOR_ADDRESS), + min_num_verifiers: 1, + max_num_verifiers: Some(100), + min_verifier_bond: Uint128::new(1).try_into().unwrap(), + bond_denom: "uaxl".to_string(), + unbonding_period_days: 1, + description: "verifiers".to_string(), }) - .collect::>(), - ) - .into()) - .into() + } + service_registry::msg::QueryMsg::ActiveVerifiers { + service_name: _, + chain_name: _, + } => to_json_binary( + &operators + .clone() + .into_iter() + .map(|op| WeightedVerifier { + verifier_info: Verifier { + address: op.address, + bonding_state: BondingState::Bonded { + amount: op.weight.try_into().unwrap(), + }, + authorization_state: AuthorizationState::Authorized, + service_name: SERVICE_NAME.to_string(), + }, + weight: VERIFIER_WEIGHT, + }) + .collect::>(), + ), + _ => panic!("unexpected query: {:?}", msg), + }; + Ok(result.into()).into() } fn voting_verifier_mock_querier_handler(status: VerificationStatus) -> QuerierResult { diff --git a/contracts/multisig-prover/src/testdata/payload_messages_id_unchanged.golden b/contracts/multisig-prover/src/testdata/payload_messages_id_unchanged.golden new file mode 100644 index 000000000..7b45c5ed4 --- /dev/null +++ b/contracts/multisig-prover/src/testdata/payload_messages_id_unchanged.golden @@ -0,0 +1 @@ +"068d17b0fe521c092561cd0fcf63928fc148b6fb9000fa1558030c1e55590a49" \ No newline at end of file diff --git a/contracts/multisig-prover/src/testdata/payload_verifier_set_id_unchanged.golden b/contracts/multisig-prover/src/testdata/payload_verifier_set_id_unchanged.golden new file mode 100644 index 000000000..9fcd783ad --- /dev/null +++ b/contracts/multisig-prover/src/testdata/payload_verifier_set_id_unchanged.golden @@ -0,0 +1 @@ +"76dc1094e9f8dbb6a98c077e3a67c889d49820e98e8560f0a91e3531d41d4168" \ No newline at end of file diff --git a/contracts/multisig-prover/src/types.rs b/contracts/multisig-prover/src/types.rs deleted file mode 100644 index 25e1b56c3..000000000 --- a/contracts/multisig-prover/src/types.rs +++ /dev/null @@ -1,7 +0,0 @@ -use axelar_wasm_std::{Participant, Snapshot}; -use multisig::key::PublicKey; - -pub struct VerifiersInfo { - pub snapshot: Snapshot, - pub pubkeys_by_participant: Vec<(Participant, PublicKey)>, -} diff --git a/contracts/router/.cargo/config b/contracts/multisig/.cargo/config.toml similarity index 100% rename from contracts/router/.cargo/config rename to contracts/multisig/.cargo/config.toml diff --git a/contracts/multisig/Cargo.toml b/contracts/multisig/Cargo.toml index 47f30c9ef..2b3058375 100644 --- a/contracts/multisig/Cargo.toml +++ b/contracts/multisig/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "multisig" -version = "0.3.0" +version = "1.0.0" rust-version = { workspace = true } -edition = "2021" +edition = { workspace = true } description = "Multisig contract" exclude = [ @@ -36,24 +36,24 @@ test = [] optimize = """docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/rust-optimizer:0.12.6 + cosmwasm/optimizer:0.16.0 """ [dependencies] -axelar-wasm-std = { workspace = true } -axelar-wasm-std-derive = { workspace = true } +axelar-wasm-std = { workspace = true, features = ["derive"] } cosmwasm-crypto = "1.2.7" cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } cw-storage-plus = { workspace = true } cw-utils = "1.0.1" cw2 = { workspace = true } -ed25519-dalek = { version = "2.1.1", default-features = false } +ed25519-dalek = { workspace = true } enum-display-derive = "0.1.1" error-stack = { workspace = true } getrandom = { version = "0.2", default-features = false, features = ["custom"] } itertools = "0.11.0" -k256 = { version = "0.13.1", features = ["ecdsa"] } +k256 = { workspace = true } +msgs-derive = { workspace = true } report = { workspace = true } rewards = { workspace = true, features = ["library"] } router-api = { workspace = true } @@ -66,6 +66,8 @@ thiserror = { workspace = true } [dev-dependencies] curve25519-dalek = "4.1.1" cw-multi-test = "0.15.1" +goldie = { workspace = true } +hex = "0.4" [lints] workspace = true diff --git a/contracts/multisig/src/bin/schema.rs b/contracts/multisig/src/bin/schema.rs index 923fac67b..6b8f5bdfd 100644 --- a/contracts/multisig/src/bin/schema.rs +++ b/contracts/multisig/src/bin/schema.rs @@ -1,5 +1,4 @@ use cosmwasm_schema::write_api; - use multisig::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; fn main() { diff --git a/contracts/multisig/src/contract.rs b/contracts/multisig/src/contract.rs index 7bcc5b0be..b30683d52 100644 --- a/contracts/multisig/src/contract.rs +++ b/contracts/multisig/src/contract.rs @@ -1,22 +1,26 @@ +use std::collections::HashMap; + +use axelar_wasm_std::{address, killswitch, permission_control, FnExt}; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_json_binary, Addr, Binary, Deps, DepsMut, Empty, Env, HexBinary, MessageInfo, Response, - StdResult, Uint64, + to_json_binary, Addr, Binary, Deps, DepsMut, Env, HexBinary, MessageInfo, Response, StdResult, + Storage, Uint64, }; - -use crate::{ - events::Event, - migrations, - msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, - state::{ - get_verifier_set, Config, CONFIG, SIGNING_SESSIONS, SIGNING_SESSION_COUNTER, VERIFIER_SETS, - }, - types::{MsgToSign, MultisigState}, - ContractError, +use error_stack::{report, Report, ResultExt}; +use itertools::Itertools; +use router_api::ChainName; + +use crate::events::Event; +use crate::msg::{ExecuteMsg, InstantiateMsg, MigrationMsg, QueryMsg}; +use crate::state::{ + verifier_set, Config, CONFIG, SIGNING_SESSIONS, SIGNING_SESSION_COUNTER, VERIFIER_SETS, }; +use crate::types::{MsgToSign, MultisigState}; +use crate::ContractError; mod execute; +mod migrations; mod query; const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); @@ -26,11 +30,26 @@ const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); pub fn migrate( deps: DepsMut, _env: Env, - _msg: Empty, -) -> Result { + msg: MigrationMsg, +) -> Result { + let admin = address::validate_cosmwasm_address(deps.api, &msg.admin_address)?; + let authorized_callers = msg + .authorized_callers + .into_iter() + .map(|(contract_address, chain_name)| { + address::validate_cosmwasm_address(deps.api, &contract_address) + .map(|addr| (addr, chain_name)) + }) + .try_collect()?; + + migrations::v0_4_1::migrate(deps.storage, admin, authorized_callers) + .change_context(ContractError::Migration)?; + + // this needs to be the last thing to do during migration, + // because previous migration steps should check the old version cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - migrations::v_0_3::migrate_verifier_sets(deps) + Ok(Response::default()) } #[cfg_attr(not(feature = "library"), entry_point)] @@ -39,12 +58,19 @@ pub fn instantiate( _env: Env, _info: MessageInfo, msg: InstantiateMsg, -) -> Result { +) -> Result { cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + let admin = address::validate_cosmwasm_address(deps.api, &msg.admin_address)?; + let governance = address::validate_cosmwasm_address(deps.api, &msg.governance_address)?; + + permission_control::set_admin(deps.storage, &admin)?; + permission_control::set_governance(deps.storage, &governance)?; + + killswitch::init(deps.storage, killswitch::State::Disengaged)?; + let config = Config { - governance: deps.api.addr_validate(&msg.governance_address)?, - rewards_contract: deps.api.addr_validate(&msg.rewards_address)?, + rewards_contract: address::validate_cosmwasm_address(deps.api, &msg.rewards_address)?, block_expiry: msg.block_expiry, }; CONFIG.save(deps.storage, &config)?; @@ -60,25 +86,27 @@ pub fn execute( env: Env, info: MessageInfo, msg: ExecuteMsg, -) -> Result { - match msg { +) -> Result { + match msg.ensure_permissions( + deps.storage, + &info.sender, + can_start_signing_session(&info.sender), + )? { ExecuteMsg::StartSigningSession { verifier_set_id, msg, chain_name, sig_verifier, } => { - execute::require_authorized_caller(&deps, info.sender)?; - let sig_verifier = sig_verifier - .map(|addr| deps.api.addr_validate(&addr)) + .map(|addr| address::validate_cosmwasm_address(deps.api, &addr)) .transpose()?; execute::start_signing_session( deps, env, verifier_set_id, msg.try_into() - .map_err(axelar_wasm_std::ContractError::from)?, + .map_err(axelar_wasm_std::error::ContractError::from)?, chain_name, sig_verifier, ) @@ -94,78 +122,118 @@ pub fn execute( public_key, signed_sender_address, } => execute::register_pub_key(deps, info, public_key, signed_sender_address), - ExecuteMsg::AuthorizeCaller { contract_address } => { - execute::require_governance(&deps, info.sender)?; - execute::authorize_caller(deps, contract_address) + ExecuteMsg::AuthorizeCallers { contracts } => { + let contracts = validate_contract_addresses(&deps, contracts)?; + execute::authorize_callers(deps, contracts) + } + ExecuteMsg::UnauthorizeCallers { contracts } => { + let contracts = validate_contract_addresses(&deps, contracts)?; + execute::unauthorize_callers(deps, contracts) } - ExecuteMsg::UnauthorizeCaller { contract_address } => { - execute::require_governance(&deps, info.sender)?; - execute::unauthorize_caller(deps, contract_address) + ExecuteMsg::DisableSigning => execute::disable_signing(deps), + ExecuteMsg::EnableSigning => execute::enable_signing(deps), + }? + .then(Ok) +} + +fn validate_contract_addresses( + deps: &DepsMut, + contracts: HashMap, +) -> Result, Report> { + contracts + .into_iter() + .map(|(contract_address, chain_name)| { + Ok(( + address::validate_cosmwasm_address(deps.api, &contract_address)?, + chain_name, + )) + }) + .try_collect() +} + +fn can_start_signing_session( + sender: &Addr, +) -> impl FnOnce(&dyn Storage, &ExecuteMsg) -> error_stack::Result + '_ +{ + |storage, msg| match msg { + ExecuteMsg::StartSigningSession { chain_name, .. } => { + execute::require_authorized_caller(storage, sender, chain_name) + .change_context(permission_control::Error::Unauthorized) } + _ => Err(report!(permission_control::Error::WrongVariant)), } - .map_err(axelar_wasm_std::ContractError::from) } #[cfg_attr(not(feature = "library"), entry_point)] -pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { +pub fn query( + deps: Deps, + _env: Env, + msg: QueryMsg, +) -> Result { match msg { - QueryMsg::GetMultisig { session_id } => { - to_json_binary(&query::get_multisig(deps, session_id)?) + QueryMsg::Multisig { session_id } => to_json_binary(&query::multisig(deps, session_id)?)?, + QueryMsg::VerifierSet { verifier_set_id } => { + to_json_binary(&query::verifier_set(deps, verifier_set_id)?)? } - QueryMsg::GetVerifierSet { verifier_set_id } => { - to_json_binary(&query::get_verifier_set(deps, verifier_set_id)?) - } - QueryMsg::GetPublicKey { + QueryMsg::PublicKey { verifier_address, key_type, - } => to_json_binary(&query::get_public_key( + } => to_json_binary(&query::public_key( deps, - deps.api.addr_validate(&verifier_address)?, + address::validate_cosmwasm_address(deps.api, &verifier_address)?, key_type, - )?), + )?)?, + QueryMsg::IsCallerAuthorized { + contract_address, + chain_name, + } => to_json_binary(&query::caller_authorized( + deps, + address::validate_cosmwasm_address(deps.api, &contract_address)?, + chain_name, + )?)?, } + .then(Ok) } #[cfg(feature = "test")] #[cfg(test)] mod tests { + use std::collections::HashMap; use std::vec; - use cosmwasm_std::{ - from_json, - testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, - Addr, Empty, OwnedDeps, WasmMsg, + use cosmwasm_std::testing::{ + mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage, }; - use serde_json::from_str; - + use cosmwasm_std::{from_json, Addr, Empty, OwnedDeps, WasmMsg}; + use permission_control::Permission; use router_api::ChainName; - - use crate::{ - key::{KeyType, PublicKey, Signature}, - multisig::Multisig, - state::load_session_signatures, - test::common::{build_verifier_set, TestSigner}, - test::common::{ecdsa_test_data, ed25519_test_data}, - types::MultisigState, - verifier_set::VerifierSet, - }; + use serde_json::from_str; use super::*; + use crate::key::{KeyType, PublicKey, Signature}; + use crate::multisig::Multisig; + use crate::state::load_session_signatures; + use crate::test::common::{build_verifier_set, ecdsa_test_data, ed25519_test_data, TestSigner}; + use crate::types::MultisigState; + use crate::verifier_set::VerifierSet; const INSTANTIATOR: &str = "inst"; const PROVER: &str = "prover"; const REWARDS_CONTRACT: &str = "rewards"; + const GOVERNANCE: &str = "governance"; + const ADMIN: &str = "admin"; const SIGNATURE_BLOCK_EXPIRY: u64 = 100; - fn do_instantiate(deps: DepsMut) -> Result { + fn do_instantiate(deps: DepsMut) -> Result { let info = mock_info(INSTANTIATOR, &[]); let env = mock_env(); let msg = InstantiateMsg { - governance_address: "governance".parse().unwrap(), + governance_address: GOVERNANCE.parse().unwrap(), + admin_address: ADMIN.parse().unwrap(), rewards_address: REWARDS_CONTRACT.to_string(), - block_expiry: SIGNATURE_BLOCK_EXPIRY, + block_expiry: SIGNATURE_BLOCK_EXPIRY.try_into().unwrap(), }; instantiate(deps, env, info, msg) @@ -174,7 +242,7 @@ mod tests { fn generate_verifier_set( key_type: KeyType, deps: DepsMut, - ) -> Result<(Response, VerifierSet), axelar_wasm_std::ContractError> { + ) -> Result<(Response, VerifierSet), axelar_wasm_std::error::ContractError> { let info = mock_info(PROVER, &[]); let env = mock_env(); @@ -191,12 +259,15 @@ mod tests { execute(deps, env, info.clone(), msg).map(|res| (res, verifier_set)) } - fn query_verifier_set(verifier_set_id: &str, deps: Deps) -> StdResult { + fn query_verifier_set( + verifier_set_id: &str, + deps: Deps, + ) -> Result { let env = mock_env(); query( deps, env, - QueryMsg::GetVerifierSet { + QueryMsg::VerifierSet { verifier_set_id: verifier_set_id.to_string(), }, ) @@ -207,7 +278,7 @@ mod tests { sender: &str, verifier_set_id: &str, chain_name: ChainName, - ) -> Result { + ) -> Result { let info = mock_info(sender, &[]); let env = mock_env(); @@ -226,7 +297,7 @@ mod tests { env: Env, session_id: Uint64, signer: &TestSigner, - ) -> Result { + ) -> Result { let msg = ExecuteMsg::SubmitSignature { session_id, signature: signer.signature.clone(), @@ -239,7 +310,7 @@ mod tests { verifier: Addr, public_key: PublicKey, signed_sender_address: HexBinary, - ) -> Result { + ) -> Result { let msg = ExecuteMsg::RegisterPublicKey { public_key, signed_sender_address, @@ -247,27 +318,57 @@ mod tests { execute(deps, mock_env(), mock_info(verifier.as_str(), &[]), msg) } - fn do_authorize_caller( + fn do_authorize_callers( deps: DepsMut, - contract_address: Addr, - ) -> Result { - let config = CONFIG.load(deps.storage)?; - let info = mock_info(config.governance.as_str(), &[]); + contracts: Vec<(Addr, ChainName)>, + ) -> Result { + let info = mock_info(GOVERNANCE, &[]); let env = mock_env(); - let msg = ExecuteMsg::AuthorizeCaller { contract_address }; + let msg = ExecuteMsg::AuthorizeCallers { + contracts: contracts + .into_iter() + .map(|(addr, chain_name)| (addr.to_string(), chain_name)) + .collect(), + }; execute(deps, env, info, msg) } fn do_unauthorize_caller( deps: DepsMut, - contract_address: Addr, - ) -> Result { - let config = CONFIG.load(deps.storage)?; - let info = mock_info(config.governance.as_str(), &[]); + contracts: Vec<(Addr, ChainName)>, + ) -> Result { + let info = mock_info(GOVERNANCE, &[]); + let env = mock_env(); + + let msg = ExecuteMsg::UnauthorizeCallers { + contracts: contracts + .into_iter() + .map(|(addr, chain_name)| (addr.to_string(), chain_name)) + .collect(), + }; + execute(deps, env, info, msg) + } + + fn do_disable_signing( + deps: DepsMut, + sender: &str, + ) -> Result { + let info = mock_info(sender, &[]); + let env = mock_env(); + + let msg = ExecuteMsg::DisableSigning; + execute(deps, env, info, msg) + } + + fn do_enable_signing( + deps: DepsMut, + sender: &str, + ) -> Result { + let info = mock_info(sender, &[]); let env = mock_env(); - let msg = ExecuteMsg::UnauthorizeCaller { contract_address }; + let msg = ExecuteMsg::EnableSigning; execute(deps, env, info, msg) } @@ -275,12 +376,12 @@ mod tests { deps: Deps, verifier: Addr, key_type: KeyType, - ) -> StdResult { + ) -> Result { let env = mock_env(); query( deps, env, - QueryMsg::GetPublicKey { + QueryMsg::PublicKey { verifier_address: verifier.to_string(), key_type, }, @@ -307,7 +408,7 @@ mod tests { } // TODO: move to external crate? - fn get_event_attribute<'a>( + fn event_attribute<'a>( event: &'a cosmwasm_std::Event, attribute_name: &'a str, ) -> Option<&'a str> { @@ -349,6 +450,18 @@ mod tests { let session_counter = SIGNING_SESSION_COUNTER.load(deps.as_ref().storage).unwrap(); + assert_eq!( + permission_control::sender_role(deps.as_ref().storage, &Addr::unchecked(ADMIN)) + .unwrap(), + Permission::Admin.into() + ); + + assert_eq!( + permission_control::sender_role(deps.as_ref().storage, &Addr::unchecked(GOVERNANCE)) + .unwrap(), + Permission::Governance.into() + ); + assert_eq!(session_counter, Uint64::zero()); } @@ -387,18 +500,18 @@ mod tests { #[test] fn start_signing_session() { let (mut deps, ecdsa_subkey, ed25519_subkey) = setup(); - do_authorize_caller(deps.as_mut(), Addr::unchecked(PROVER)).unwrap(); + let chain_name: ChainName = "mock-chain".parse().unwrap(); + do_authorize_callers( + deps.as_mut(), + vec![(Addr::unchecked(PROVER), chain_name.clone())], + ) + .unwrap(); for (i, subkey) in [ecdsa_subkey.clone(), ed25519_subkey.clone()] .into_iter() .enumerate() { - let res = do_start_signing_session( - deps.as_mut(), - PROVER, - &subkey, - "mock-chain".parse().unwrap(), - ); + let res = do_start_signing_session(deps.as_mut(), PROVER, &subkey, chain_name.clone()); assert!(res.is_ok()); @@ -407,7 +520,7 @@ mod tests { .unwrap(); let verifier_set_id = subkey.to_string(); - let verifier_set = get_verifier_set(deps.as_ref().storage, &verifier_set_id).unwrap(); + let verifier_set = verifier_set(deps.as_ref().storage, &verifier_set_id).unwrap(); let message = match subkey { _ if subkey == ecdsa_subkey => ecdsa_test_data::message(), _ if subkey == ed25519_subkey => ed25519_test_data::message(), @@ -429,25 +542,30 @@ mod tests { let event = res.events.first().unwrap(); assert_eq!(event.ty, "signing_started".to_string()); assert_eq!( - get_event_attribute(event, "session_id").unwrap(), + event_attribute(event, "session_id").unwrap(), session.id.to_string() ); assert_eq!( - get_event_attribute(event, "verifier_set_id").unwrap(), + event_attribute(event, "verifier_set_id").unwrap(), session.verifier_set_id ); assert_eq!( - verifier_set.get_pub_keys(), - from_str(get_event_attribute(event, "pub_keys").unwrap()).unwrap() + verifier_set.pub_keys(), + from_str(event_attribute(event, "pub_keys").unwrap()).unwrap() ); - assert_eq!(get_event_attribute(event, "msg").unwrap(), message.to_hex()); + assert_eq!(event_attribute(event, "msg").unwrap(), message.to_hex()); } } #[test] fn start_signing_session_wrong_sender() { let (mut deps, ecdsa_subkey, ed25519_subkey) = setup(); - do_authorize_caller(deps.as_mut(), Addr::unchecked(PROVER)).unwrap(); + let chain_name: ChainName = "mock-chain".parse().unwrap(); + do_authorize_callers( + deps.as_mut(), + vec![(Addr::unchecked(PROVER), chain_name.clone())], + ) + .unwrap(); let sender = "someone else"; @@ -456,22 +574,25 @@ mod tests { deps.as_mut(), sender, &verifier_set_id, - "mock-chain".parse().unwrap(), + chain_name.clone(), ); assert!(res .unwrap_err() .to_string() - .contains(&ContractError::Unauthorized.to_string())); + .contains(&permission_control::Error::Unauthorized.to_string())); } } #[test] fn submit_signature() { let (mut deps, ecdsa_subkey, ed25519_subkey) = setup(); - do_authorize_caller(deps.as_mut(), Addr::unchecked(PROVER)).unwrap(); - let chain_name: ChainName = "mock-chain".parse().unwrap(); + do_authorize_callers( + deps.as_mut(), + vec![(Addr::unchecked(PROVER), chain_name.clone())], + ) + .unwrap(); for (key_type, verifier_set_id, signers, session_id) in signature_test_data(&ecdsa_subkey, &ed25519_subkey) @@ -520,15 +641,15 @@ mod tests { let event = res.events.first().unwrap(); assert_eq!(event.ty, "signature_submitted".to_string()); assert_eq!( - get_event_attribute(event, "session_id").unwrap(), + event_attribute(event, "session_id").unwrap(), session_id.to_string() ); assert_eq!( - get_event_attribute(event, "participant").unwrap(), + event_attribute(event, "participant").unwrap(), signer.address.into_string() ); assert_eq!( - get_event_attribute(event, "signature").unwrap(), + event_attribute(event, "signature").unwrap(), signer.signature.to_hex() ); } @@ -537,13 +658,17 @@ mod tests { #[test] fn submit_signature_completes_session() { let (mut deps, ecdsa_subkey, ed25519_subkey) = setup(); - do_authorize_caller(deps.as_mut(), Addr::unchecked(PROVER)).unwrap(); + let chain_name: ChainName = "mock-chain".parse().unwrap(); + do_authorize_callers( + deps.as_mut(), + vec![(Addr::unchecked(PROVER), chain_name.clone())], + ) + .unwrap(); for (key_type, subkey, signers, session_id) in signature_test_data(&ecdsa_subkey, &ed25519_subkey) { - do_start_signing_session(deps.as_mut(), PROVER, subkey, "mock-chain".parse().unwrap()) - .unwrap(); + do_start_signing_session(deps.as_mut(), PROVER, subkey, chain_name.clone()).unwrap(); let signer = signers.first().unwrap().to_owned(); do_sign(deps.as_mut(), mock_env(), session_id, &signer).unwrap(); @@ -581,7 +706,7 @@ mod tests { let event = res.events.get(1).unwrap(); assert_eq!(event.ty, "signing_completed".to_string()); assert_eq!( - get_event_attribute(event, "session_id").unwrap(), + event_attribute(event, "session_id").unwrap(), session_id.to_string() ); } @@ -590,9 +715,12 @@ mod tests { #[test] fn submit_signature_before_expiry() { let (mut deps, ecdsa_subkey, ed25519_subkey) = setup(); - do_authorize_caller(deps.as_mut(), Addr::unchecked(PROVER)).unwrap(); - let chain_name: ChainName = "mock-chain".parse().unwrap(); + do_authorize_callers( + deps.as_mut(), + vec![(Addr::unchecked(PROVER), chain_name.clone())], + ) + .unwrap(); for (_key_type, subkey, signers, session_id) in signature_test_data(&ecdsa_subkey, &ed25519_subkey) @@ -636,13 +764,18 @@ mod tests { #[test] fn submit_signature_after_expiry() { let (mut deps, ecdsa_subkey, ed25519_subkey) = setup(); - do_authorize_caller(deps.as_mut(), Addr::unchecked(PROVER)).unwrap(); + + let chain_name: ChainName = "mock-chain".parse().unwrap(); + do_authorize_callers( + deps.as_mut(), + vec![(Addr::unchecked(PROVER), chain_name.clone())], + ) + .unwrap(); for (_key_type, subkey, signers, session_id) in signature_test_data(&ecdsa_subkey, &ed25519_subkey) { - do_start_signing_session(deps.as_mut(), PROVER, subkey, "mock-chain".parse().unwrap()) - .unwrap(); + do_start_signing_session(deps.as_mut(), PROVER, subkey, chain_name.clone()).unwrap(); let signer = signers.first().unwrap().to_owned(); do_sign(deps.as_mut(), mock_env(), session_id, &signer).unwrap(); @@ -659,7 +792,7 @@ mod tests { assert_eq!( res.unwrap_err().to_string(), - axelar_wasm_std::ContractError::from(ContractError::SigningSessionClosed { + axelar_wasm_std::error::ContractError::from(ContractError::SigningSessionClosed { session_id }) .to_string() @@ -670,14 +803,13 @@ mod tests { #[test] fn submit_signature_wrong_session_id() { let (mut deps, ecdsa_subkey, _) = setup(); - do_authorize_caller(deps.as_mut(), Addr::unchecked(PROVER)).unwrap(); - do_start_signing_session( + let chain_name: ChainName = "mock-chain".parse().unwrap(); + do_authorize_callers( deps.as_mut(), - PROVER, - &ecdsa_subkey, - "mock-chain".parse().unwrap(), + vec![(Addr::unchecked(PROVER), chain_name.clone())], ) .unwrap(); + do_start_signing_session(deps.as_mut(), PROVER, &ecdsa_subkey, chain_name.clone()).unwrap(); let invalid_session_id = Uint64::zero(); let signer = ecdsa_test_data::signers().first().unwrap().to_owned(); @@ -685,7 +817,7 @@ mod tests { assert_eq!( res.unwrap_err().to_string(), - axelar_wasm_std::ContractError::from(ContractError::SigningSessionNotFound { + axelar_wasm_std::error::ContractError::from(ContractError::SigningSessionNotFound { session_id: invalid_session_id }) .to_string() @@ -695,7 +827,12 @@ mod tests { #[test] fn query_signing_session() { let (mut deps, ecdsa_subkey, ed25519_subkey) = setup(); - do_authorize_caller(deps.as_mut(), Addr::unchecked(PROVER)).unwrap(); + let chain_name: ChainName = "mock-chain".parse().unwrap(); + do_authorize_callers( + deps.as_mut(), + vec![(Addr::unchecked(PROVER), chain_name.clone())], + ) + .unwrap(); for (_key_type, subkey, signers, session_id) in signature_test_data(&ecdsa_subkey, &ed25519_subkey) @@ -715,12 +852,12 @@ mod tests { let expected_completed_at = env.block.height; do_sign(deps.as_mut(), env, session_id, signers.get(1).unwrap()).unwrap(); - let msg = QueryMsg::GetMultisig { session_id }; + let msg = QueryMsg::Multisig { session_id }; let res = query(deps.as_ref(), mock_env(), msg); assert!(res.is_ok()); - let query_res: Multisig = from_json(&res.unwrap()).unwrap(); + let query_res: Multisig = from_json(res.unwrap()).unwrap(); let session = SIGNING_SESSIONS .load(deps.as_ref().storage, session_id.into()) .unwrap(); @@ -802,7 +939,7 @@ mod tests { for (addr, _, _) in &expected_pub_keys { let res = query_registered_public_key(deps.as_ref(), addr.clone(), key_type); assert!(res.is_ok()); - ret_pub_keys.push(from_json(&res.unwrap()).unwrap()); + ret_pub_keys.push(from_json(res.unwrap()).unwrap()); } assert_eq!( expected_pub_keys @@ -907,7 +1044,7 @@ mod tests { ); assert_eq!( res.unwrap_err().to_string(), - axelar_wasm_std::ContractError::from( + axelar_wasm_std::error::ContractError::from( ContractError::InvalidPublicKeyRegistrationSignature ) .to_string() @@ -926,7 +1063,7 @@ mod tests { ); assert_eq!( res.unwrap_err().to_string(), - axelar_wasm_std::ContractError::from( + axelar_wasm_std::error::ContractError::from( ContractError::InvalidPublicKeyRegistrationSignature ) .to_string() @@ -960,43 +1097,101 @@ mod tests { assert_eq!( res.unwrap_err().to_string(), - axelar_wasm_std::ContractError::from(ContractError::DuplicatePublicKey).to_string() + axelar_wasm_std::error::ContractError::from(ContractError::DuplicatePublicKey) + .to_string() ); } #[test] - fn authorize_and_unauthorize_caller() { + fn authorize_and_unauthorize_callers() { let (mut deps, ecdsa_subkey, ed25519_subkey) = setup(); + let prover_address = Addr::unchecked(PROVER); + let chain_name: ChainName = "mock-chain".parse().unwrap(); // authorize - do_authorize_caller(deps.as_mut(), Addr::unchecked(PROVER)).unwrap(); + do_authorize_callers( + deps.as_mut(), + vec![(prover_address.clone(), chain_name.clone())], + ) + .unwrap(); for verifier_set_id in [ecdsa_subkey.clone(), ed25519_subkey.clone()] { let res = do_start_signing_session( deps.as_mut(), PROVER, &verifier_set_id, - "mock-chain".parse().unwrap(), + chain_name.clone(), ); assert!(res.is_ok()); } + let caller_authorization_status = + query::caller_authorized(deps.as_ref(), prover_address.clone(), chain_name.clone()) + .unwrap(); + assert!(caller_authorization_status); + // unauthorize - do_unauthorize_caller(deps.as_mut(), Addr::unchecked(PROVER)).unwrap(); + do_unauthorize_caller( + deps.as_mut(), + vec![(prover_address.clone(), chain_name.clone())], + ) + .unwrap(); for verifier_set_id in [ecdsa_subkey, ed25519_subkey] { let res = do_start_signing_session( deps.as_mut(), PROVER, &verifier_set_id, - "mock-chain".parse().unwrap(), + chain_name.clone(), ); assert!(res .unwrap_err() .to_string() - .contains(&ContractError::Unauthorized.to_string())); + .contains(&permission_control::Error::Unauthorized.to_string())); } + + let caller_authorization_status = + query::caller_authorized(deps.as_ref(), prover_address, chain_name.clone()).unwrap(); + assert!(!caller_authorization_status); + } + + #[test] + fn authorize_and_unauthorize_many_callers() { + let (mut deps, _, _) = setup(); + + let contracts = vec![ + (Addr::unchecked("addr1"), "chain1".parse().unwrap()), + (Addr::unchecked("addr2"), "chain2".parse().unwrap()), + (Addr::unchecked("addr3"), "chain3".parse().unwrap()), + ]; + do_authorize_callers(deps.as_mut(), contracts.clone()).unwrap(); + assert!(contracts + .iter() + .all(|(addr, chain_name)| query::caller_authorized( + deps.as_ref(), + addr.clone(), + chain_name.clone() + ) + .unwrap())); + let (authorized, unauthorized) = contracts.split_at(1); + do_unauthorize_caller(deps.as_mut(), unauthorized.to_vec()).unwrap(); + assert!(unauthorized + .iter() + .all(|(addr, chain_name)| !query::caller_authorized( + deps.as_ref(), + addr.clone(), + chain_name.clone() + ) + .unwrap())); + assert!(authorized + .iter() + .all(|(addr, chain_name)| query::caller_authorized( + deps.as_ref(), + addr.clone(), + chain_name.clone() + ) + .unwrap())); } #[test] @@ -1006,14 +1201,18 @@ mod tests { let info = mock_info("user", &[]); let env = mock_env(); - let msg = ExecuteMsg::AuthorizeCaller { - contract_address: Addr::unchecked(PROVER), + let msg = ExecuteMsg::AuthorizeCallers { + contracts: HashMap::from([(PROVER.to_string(), "mock-chain".parse().unwrap())]), }; let res = execute(deps.as_mut(), env, info, msg); assert_eq!( res.unwrap_err().to_string(), - axelar_wasm_std::ContractError::from(ContractError::Unauthorized).to_string() + permission_control::Error::PermissionDenied { + expected: Permission::Governance.into(), + actual: Permission::NoPrivilege.into() + } + .to_string() ); } @@ -1024,14 +1223,134 @@ mod tests { let info = mock_info("user", &[]); let env = mock_env(); - let msg = ExecuteMsg::UnauthorizeCaller { - contract_address: Addr::unchecked(PROVER), + let msg = ExecuteMsg::UnauthorizeCallers { + contracts: HashMap::from([(PROVER.to_string(), "mock-chain".parse().unwrap())]), }; let res = execute(deps.as_mut(), env, info, msg); assert_eq!( res.unwrap_err().to_string(), - axelar_wasm_std::ContractError::from(ContractError::Unauthorized).to_string() + permission_control::Error::PermissionDenied { + expected: Permission::Elevated.into(), + actual: Permission::NoPrivilege.into() + } + .to_string() ); } + + #[test] + fn disable_enable_signing() { + let (mut deps, ecdsa_subkey, ed25519_subkey) = setup(); + let prover_address = Addr::unchecked(PROVER); + let chain_name: ChainName = "mock-chain".parse().unwrap(); + + // authorize + do_authorize_callers( + deps.as_mut(), + vec![(prover_address.clone(), chain_name.clone())], + ) + .unwrap(); + + do_disable_signing(deps.as_mut(), ADMIN).unwrap(); + + for verifier_set_id in [ecdsa_subkey.clone(), ed25519_subkey.clone()] { + let res = do_start_signing_session( + deps.as_mut(), + PROVER, + &verifier_set_id, + chain_name.clone(), + ); + + assert_eq!( + res.unwrap_err().to_string(), + ContractError::SigningDisabled.to_string() + ); + } + + do_enable_signing(deps.as_mut(), ADMIN).unwrap(); + + for verifier_set_id in [ecdsa_subkey.clone(), ed25519_subkey.clone()] { + let res = do_start_signing_session( + deps.as_mut(), + PROVER, + &verifier_set_id, + "mock-chain".parse().unwrap(), + ); + + assert!(res.is_ok()); + } + } + + #[test] + fn disable_signing_after_session_creation() { + let (mut deps, ecdsa_subkey, ed25519_subkey) = setup(); + let chain_name: ChainName = "mock-chain".parse().unwrap(); + do_authorize_callers( + deps.as_mut(), + vec![(Addr::unchecked(PROVER), chain_name.clone())], + ) + .unwrap(); + + for (_, verifier_set_id, signers, session_id) in + signature_test_data(&ecdsa_subkey, &ed25519_subkey) + { + do_start_signing_session(deps.as_mut(), PROVER, verifier_set_id, chain_name.clone()) + .unwrap(); + + do_disable_signing(deps.as_mut(), ADMIN).unwrap(); + + let signer = signers.first().unwrap().to_owned(); + + let res = do_sign(deps.as_mut(), mock_env(), session_id, &signer); + + assert_eq!( + res.unwrap_err().to_string(), + ContractError::SigningDisabled.to_string() + ); + + do_enable_signing(deps.as_mut(), ADMIN).unwrap(); + assert!(do_sign(deps.as_mut(), mock_env(), session_id, &signer).is_ok()); + } + } + + #[test] + fn disable_enable_signing_has_correct_permissions() { + let mut deps = setup().0; + + assert!(do_disable_signing(deps.as_mut(), "user1").is_err()); + assert!(do_disable_signing(deps.as_mut(), ADMIN).is_ok()); + assert!(do_enable_signing(deps.as_mut(), "user").is_err()); + assert!(do_enable_signing(deps.as_mut(), ADMIN).is_ok()); + assert!(do_disable_signing(deps.as_mut(), GOVERNANCE).is_ok()); + assert!(do_enable_signing(deps.as_mut(), GOVERNANCE).is_ok()); + } + + #[test] + fn start_signing_session_wrong_chain() { + let (mut deps, ecdsa_subkey, ed25519_subkey) = setup(); + let chain_name: ChainName = "mock-chain".parse().unwrap(); + do_authorize_callers( + deps.as_mut(), + vec![(Addr::unchecked(PROVER), chain_name.clone())], + ) + .unwrap(); + + let wrong_chain_name: ChainName = "some-other-chain".parse().unwrap(); + + for verifier_set_id in [ecdsa_subkey, ed25519_subkey] { + let res = do_start_signing_session( + deps.as_mut(), + PROVER, + &verifier_set_id, + wrong_chain_name.clone(), + ); + + assert!(res.unwrap_err().to_string().contains( + &ContractError::WrongChainName { + expected: chain_name.clone() + } + .to_string() + )); + } + } } diff --git a/contracts/multisig/src/contract/execute.rs b/contracts/multisig/src/contract/execute.rs index 3fc0a3cbf..9993622e7 100644 --- a/contracts/multisig/src/contract/execute.rs +++ b/contracts/multisig/src/contract/execute.rs @@ -1,19 +1,15 @@ -use cosmwasm_std::{OverflowError, OverflowOperation, WasmMsg}; +use std::collections::HashMap; + +use cosmwasm_std::{ensure, OverflowError, OverflowOperation, Storage, WasmMsg}; use router_api::ChainName; use sha3::{Digest, Keccak256}; use signature_verifier_api::client::SignatureVerifier; -use crate::signing::validate_session_signature; -use crate::state::{load_session_signatures, save_pub_key, save_signature}; -use crate::verifier_set::VerifierSet; -use crate::{ - key::{KeyTyped, PublicKey, Signature}, - signing::SigningSession, - state::AUTHORIZED_CALLERS, -}; -use error_stack::ResultExt; - use super::*; +use crate::key::{KeyTyped, PublicKey, Signature}; +use crate::signing::{validate_session_signature, SigningSession}; +use crate::state::{load_session_signatures, save_pub_key, save_signature, AUTHORIZED_CALLERS}; +use crate::verifier_set::VerifierSet; pub fn start_signing_session( deps: DepsMut, @@ -23,8 +19,14 @@ pub fn start_signing_session( chain_name: ChainName, sig_verifier: Option, ) -> Result { + ensure!( + killswitch::is_contract_active(deps.storage), + ContractError::SigningDisabled + ); + let config = CONFIG.load(deps.storage)?; - let verifier_set = get_verifier_set(deps.storage, &verifier_set_id)?; + + let verifier_set = verifier_set(deps.storage, &verifier_set_id)?; let session_id = SIGNING_SESSION_COUNTER.update( deps.storage, @@ -39,7 +41,7 @@ pub fn start_signing_session( let expires_at = env .block .height - .checked_add(config.block_expiry) + .checked_add(config.block_expiry.into()) .ok_or_else(|| { OverflowError::new( OverflowOperation::Add, @@ -62,7 +64,7 @@ pub fn start_signing_session( let event = Event::SigningStarted { session_id, verifier_set_id, - pub_keys: verifier_set.get_pub_keys(), + pub_keys: verifier_set.pub_keys(), msg, chain_name, expires_at, @@ -80,6 +82,11 @@ pub fn submit_signature( session_id: Uint64, signature: HexBinary, ) -> Result { + ensure!( + killswitch::is_contract_active(deps.storage), + ContractError::SigningDisabled + ); + let config = CONFIG.load(deps.storage)?; let mut session = SIGNING_SESSIONS .load(deps.storage, session_id.into()) @@ -171,35 +178,66 @@ pub fn register_pub_key( } pub fn require_authorized_caller( - deps: &DepsMut, - contract_address: Addr, -) -> error_stack::Result<(), ContractError> { - AUTHORIZED_CALLERS - .load(deps.storage, &contract_address) - .change_context(ContractError::Unauthorized) + storage: &dyn Storage, + contract_address: &Addr, + chain_name: &ChainName, +) -> Result { + let expected_chain_name = AUTHORIZED_CALLERS.load(storage, contract_address)?; + if expected_chain_name != *chain_name { + return Err(ContractError::WrongChainName { + expected: expected_chain_name, + }); + } + Ok(contract_address.clone()) } -pub fn authorize_caller(deps: DepsMut, contract_address: Addr) -> Result { - AUTHORIZED_CALLERS.save(deps.storage, &contract_address, &())?; - - Ok(Response::new().add_event(Event::CallerAuthorized { contract_address }.into())) +pub fn authorize_callers( + deps: DepsMut, + contracts: HashMap, +) -> Result { + contracts + .iter() + .map(|(contract_address, chain_name)| { + AUTHORIZED_CALLERS.save(deps.storage, contract_address, chain_name) + }) + .try_collect()?; + + Ok( + Response::new().add_events(contracts.into_iter().map(|(contract_address, chain_name)| { + Event::CallerAuthorized { + contract_address, + chain_name, + } + .into() + })), + ) } -pub fn unauthorize_caller( +pub fn unauthorize_callers( deps: DepsMut, - contract_address: Addr, + contracts: HashMap, ) -> Result { - AUTHORIZED_CALLERS.remove(deps.storage, &contract_address); + contracts.iter().for_each(|(contract_address, _)| { + AUTHORIZED_CALLERS.remove(deps.storage, contract_address) + }); + + Ok( + Response::new().add_events(contracts.into_iter().map(|(contract_address, chain_name)| { + Event::CallerUnauthorized { + contract_address, + chain_name, + } + .into() + })), + ) +} - Ok(Response::new().add_event(Event::CallerUnauthorized { contract_address }.into())) +pub fn enable_signing(deps: DepsMut) -> Result { + killswitch::disengage(deps.storage, Event::SigningEnabled).map_err(|err| err.into()) } -pub fn require_governance(deps: &DepsMut, sender: Addr) -> Result<(), ContractError> { - let config = CONFIG.load(deps.storage)?; - if config.governance != sender { - return Err(ContractError::Unauthorized); - } - Ok(()) +pub fn disable_signing(deps: DepsMut) -> Result { + killswitch::engage(deps.storage, Event::SigningDisabled).map_err(|err| err.into()) } fn signing_response( @@ -212,7 +250,7 @@ fn signing_response( let rewards_msg = WasmMsg::Execute { contract_addr: rewards_contract, msg: to_json_binary(&rewards::msg::ExecuteMsg::RecordParticipation { - chain_name: session.chain_name, + chain_name: session.chain_name.clone(), event_id: session .id .to_string() @@ -240,6 +278,7 @@ fn signing_response( Event::SigningCompleted { session_id: session.id, completed_at, + chain_name: session.chain_name, } .into(), ) diff --git a/contracts/multisig/src/contract/migrations/mod.rs b/contracts/multisig/src/contract/migrations/mod.rs new file mode 100644 index 000000000..080e953e7 --- /dev/null +++ b/contracts/multisig/src/contract/migrations/mod.rs @@ -0,0 +1 @@ +pub mod v0_4_1; diff --git a/contracts/multisig/src/contract/migrations/v0_4_1.rs b/contracts/multisig/src/contract/migrations/v0_4_1.rs new file mode 100644 index 000000000..24570ab1e --- /dev/null +++ b/contracts/multisig/src/contract/migrations/v0_4_1.rs @@ -0,0 +1,369 @@ +#![allow(deprecated)] + +use axelar_wasm_std::killswitch::State; +use axelar_wasm_std::{killswitch, nonempty, permission_control}; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, Order, StdError, Storage}; +use cw2::VersionError; +use cw_storage_plus::Item; +use itertools::Itertools; +use router_api::ChainName; + +use crate::contract::CONTRACT_NAME; +use crate::signing::SigningSession; +use crate::state::{AUTHORIZED_CALLERS, SIGNING_SESSIONS, VERIFIER_SETS}; + +const BASE_VERSION: &str = "0.4.1"; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Std(#[from] StdError), + #[error(transparent)] + Version(#[from] VersionError), + #[error(transparent)] + NonEmpty(#[from] nonempty::Error), +} + +pub fn migrate( + storage: &mut dyn Storage, + admin: Addr, + authorized_callers: Vec<(Addr, ChainName)>, +) -> Result<(), Error> { + cw2::assert_contract_version(storage, CONTRACT_NAME, BASE_VERSION)?; + + killswitch::init(storage, State::Disengaged)?; + + let config = ensure_expiry_height_is_not_zero(storage)?; + permission_control::set_governance(storage, &config.governance)?; + permission_control::set_admin(storage, &admin)?; + migrate_config(storage, config)?; + migrate_authorized_callers(storage, authorized_callers)?; + migrate_signing_sessions(storage)?; + migrate_verifier_set_ids(storage)?; + Ok(()) +} + +fn migrate_authorized_callers( + storage: &mut dyn Storage, + authorized_callers: Vec<(Addr, ChainName)>, +) -> Result<(), Error> { + AUTHORIZED_CALLERS.clear(storage); + authorized_callers + .iter() + .map(|(contract_address, chain_name)| { + AUTHORIZED_CALLERS.save(storage, contract_address, chain_name) + }) + .try_collect()?; + Ok(()) +} + +pub fn migrate_signing_sessions(storage: &mut dyn Storage) -> Result<(), Error> { + let all: Vec<_> = SIGNING_SESSIONS + .range(storage, None, None, Order::Ascending) + .collect::, _>>()?; + + for (session_id, session) in all { + let verifier_set = VERIFIER_SETS.load(storage, &session.verifier_set_id)?; + let new_session = SigningSession { + verifier_set_id: verifier_set.id(), + ..session + }; + SIGNING_SESSIONS.save(storage, session_id, &new_session)?; + } + + Ok(()) +} + +pub fn migrate_verifier_set_ids(storage: &mut dyn Storage) -> Result<(), Error> { + let all: Vec<_> = VERIFIER_SETS + .range(storage, None, None, Order::Ascending) + .collect::, _>>()?; + + for v in all { + VERIFIER_SETS.remove(storage, &v.0); + VERIFIER_SETS.save(storage, &v.1.id(), &v.1)?; + } + + Ok(()) +} + +fn ensure_expiry_height_is_not_zero(storage: &mut dyn Storage) -> Result { + CONFIG.update(storage, |mut config| { + if config.block_expiry == 0 { + config.block_expiry = 10; + } + Ok(config) + }) +} + +fn migrate_config(storage: &mut dyn Storage, config: Config) -> Result<(), Error> { + let new_config = crate::state::Config { + rewards_contract: config.rewards_contract, + block_expiry: nonempty::Uint64::try_from(config.block_expiry)?, + }; + + CONFIG.remove(storage); + crate::state::CONFIG.save(storage, &new_config)?; + Ok(()) +} + +#[cw_serde] +#[deprecated(since = "0.4.1", note = "only used during migration")] +struct Config { + pub governance: Addr, + pub rewards_contract: Addr, + pub block_expiry: u64, // number of blocks after which a signing session expires +} + +#[deprecated(since = "0.4.1", note = "only used during migration")] +const CONFIG: Item = Item::new("config"); + +#[cfg(test)] +mod tests { + use axelar_wasm_std::nonempty; + use cosmwasm_schema::cw_serde; + use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; + use cosmwasm_std::{Addr, DepsMut, Env, HexBinary, MessageInfo, Response, Uint64}; + use router_api::ChainName; + + use crate::contract::migrations::v0_4_1::{self, BASE_VERSION}; + use crate::contract::{execute, query, CONTRACT_NAME}; + use crate::msg::ExecuteMsg::{DisableSigning, SubmitSignature}; + use crate::signing::SigningSession; + use crate::state::{SIGNING_SESSIONS, SIGNING_SESSION_COUNTER, VERIFIER_SETS}; + use crate::test::common::build_verifier_set; + use crate::test::common::ecdsa_test_data::signers; + use crate::ContractError; + + #[test] + fn migrate_checks_contract_version() { + let mut deps = mock_dependencies(); + instantiate( + deps.as_mut(), + mock_env(), + mock_info("admin", &[]), + InstantiateMsg { + governance_address: "governance".to_string(), + rewards_address: "rewards".to_string(), + block_expiry: 100, + }, + ) + .unwrap(); + + cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, "something wrong").unwrap(); + + assert!(v0_4_1::migrate(deps.as_mut().storage, Addr::unchecked("admin"), vec![]).is_err()); + + cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, BASE_VERSION).unwrap(); + + assert!(v0_4_1::migrate(deps.as_mut().storage, Addr::unchecked("admin"), vec![]).is_ok()); + } + + #[test] + fn migrate_config() { + let mut deps = mock_dependencies(); + instantiate( + deps.as_mut(), + mock_env(), + mock_info("admin", &[]), + InstantiateMsg { + governance_address: "governance".to_string(), + rewards_address: "rewards".to_string(), + block_expiry: 0, + }, + ) + .unwrap(); + + assert!(v0_4_1::migrate(deps.as_mut().storage, Addr::unchecked("admin"), vec![]).is_ok()); + + assert!(v0_4_1::CONFIG.load(deps.as_mut().storage).is_err()); + + let new_config = crate::state::CONFIG.load(deps.as_mut().storage); + assert!(new_config.is_ok()); + let new_config = new_config.unwrap(); + assert_eq!( + new_config.block_expiry, + nonempty::Uint64::try_from(10).unwrap() + ); + } + + #[test] + fn permissions_are_set_after_migration_and_contract_can_be_disabled() { + let mut deps = mock_dependencies(); + instantiate( + deps.as_mut(), + mock_env(), + mock_info("admin", &[]), + InstantiateMsg { + governance_address: "governance".to_string(), + rewards_address: "rewards".to_string(), + block_expiry: 100, + }, + ) + .unwrap(); + + assert!(v0_4_1::migrate(deps.as_mut().storage, Addr::unchecked("admin"), vec![]).is_ok()); + + // contract is enabled + assert!(!execute( + deps.as_mut(), + mock_env(), + mock_info("any_addr", &[]), + SubmitSignature { + session_id: 0u64.into(), + signature: HexBinary::from_hex("04").unwrap(), + } + ) + .unwrap_err() + .to_string() + .contains(&ContractError::SigningDisabled.to_string())); + + assert!(execute( + deps.as_mut(), + mock_env(), + mock_info("any_addr", &[]), + DisableSigning + ) + .is_err()); + assert!(execute( + deps.as_mut(), + mock_env(), + mock_info("admin", &[]), + DisableSigning + ) + .is_ok()); + assert!(execute( + deps.as_mut(), + mock_env(), + mock_info("governance", &[]), + DisableSigning + ) + .is_ok()); + + // contract is disabled + assert!(execute( + deps.as_mut(), + mock_env(), + mock_info("any_addr", &[]), + SubmitSignature { + session_id: 0u64.into(), + signature: HexBinary::from_hex("04").unwrap(), + } + ) + .unwrap_err() + .to_string() + .contains(&ContractError::SigningDisabled.to_string())); + } + + #[test] + fn callers_are_authorized() { + let mut deps = mock_dependencies(); + instantiate( + deps.as_mut(), + mock_env(), + mock_info("admin", &[]), + InstantiateMsg { + governance_address: "governance".to_string(), + rewards_address: "rewards".to_string(), + block_expiry: 100, + }, + ) + .unwrap(); + + let prover = Addr::unchecked("prover1"); + let chain_name: ChainName = "mock-chain".parse().unwrap(); + assert!(v0_4_1::migrate( + deps.as_mut().storage, + Addr::unchecked("admin"), + vec![(prover.clone(), chain_name.clone())] + ) + .is_ok()); + assert!(query::caller_authorized(deps.as_ref(), prover, chain_name).unwrap()); + } + + #[test] + fn should_be_able_to_migrate_verifier_set_ids() { + let mut deps = mock_dependencies(); + + instantiate( + deps.as_mut(), + mock_env(), + mock_info("admin", &[]), + InstantiateMsg { + governance_address: "governance".to_string(), + rewards_address: "rewards".to_string(), + block_expiry: 100, + }, + ) + .unwrap(); + let signers = signers(); + let verifier_set = build_verifier_set(crate::key::KeyType::Ecdsa, &signers); + VERIFIER_SETS + .save(&mut deps.storage, "foobar", &verifier_set) + .unwrap(); + let signing_session = SigningSession { + id: Uint64::one(), + verifier_set_id: "foobar".to_string(), + chain_name: "ethereum".parse().unwrap(), + msg: HexBinary::from([2; 32]).try_into().unwrap(), + state: crate::types::MultisigState::Pending, + expires_at: 100, + sig_verifier: None, + }; + SIGNING_SESSIONS + .save( + &mut deps.storage, + signing_session.id.u64(), + &signing_session, + ) + .unwrap(); + + v0_4_1::migrate(deps.as_mut().storage, Addr::unchecked("admin"), vec![]).unwrap(); + + let new_verifier_set = VERIFIER_SETS + .load(&deps.storage, &verifier_set.id()) + .unwrap(); + assert_eq!(new_verifier_set, verifier_set); + + let expected_signing_session = SigningSession { + verifier_set_id: verifier_set.id(), + ..signing_session + }; + let new_signing_session = SIGNING_SESSIONS + .load(&deps.storage, expected_signing_session.id.u64()) + .unwrap(); + assert_eq!(new_signing_session, expected_signing_session); + } + + #[deprecated(since = "0.4.1", note = "only used to test the migration")] + fn instantiate( + deps: DepsMut, + _env: Env, + _info: MessageInfo, + msg: InstantiateMsg, + ) -> Result { + cw2::set_contract_version(deps.storage, CONTRACT_NAME, BASE_VERSION)?; + + let config = v0_4_1::Config { + governance: deps.api.addr_validate(&msg.governance_address)?, + rewards_contract: deps.api.addr_validate(&msg.rewards_address)?, + block_expiry: msg.block_expiry, + }; + + v0_4_1::CONFIG.save(deps.storage, &config)?; + + SIGNING_SESSION_COUNTER.save(deps.storage, &Uint64::zero())?; + + Ok(Response::default()) + } + + #[cw_serde] + #[deprecated(since = "0.4.1", note = "only used to test the migration")] + struct InstantiateMsg { + // the governance address is allowed to modify the authorized caller list for this contract + pub governance_address: String, + pub rewards_address: String, + pub block_expiry: u64, + } +} diff --git a/contracts/multisig/src/contract/query.rs b/contracts/multisig/src/contract/query.rs index e79b1100d..8279883ee 100644 --- a/contracts/multisig/src/contract/query.rs +++ b/contracts/multisig/src/contract/query.rs @@ -1,13 +1,12 @@ -use crate::{ - key::{KeyType, PublicKey}, - multisig::Multisig, - state::{load_pub_key, load_session_signatures}, - verifier_set::VerifierSet, -}; +use router_api::ChainName; use super::*; +use crate::key::{KeyType, PublicKey}; +use crate::multisig::Multisig; +use crate::state::{load_pub_key, load_session_signatures, AUTHORIZED_CALLERS}; +use crate::verifier_set::VerifierSet; -pub fn get_multisig(deps: Deps, session_id: Uint64) -> StdResult { +pub fn multisig(deps: Deps, session_id: Uint64) -> StdResult { let session = SIGNING_SESSIONS.load(deps.storage, session_id.into())?; let verifier_set = VERIFIER_SETS.load(deps.storage, &session.verifier_set_id)?; @@ -20,11 +19,16 @@ pub fn get_multisig(deps: Deps, session_id: Uint64) -> StdResult { }) } -pub fn get_verifier_set(deps: Deps, verifier_set_id: String) -> StdResult { +pub fn verifier_set(deps: Deps, verifier_set_id: String) -> StdResult { VERIFIER_SETS.load(deps.storage, &verifier_set_id) } -pub fn get_public_key(deps: Deps, verifier: Addr, key_type: KeyType) -> StdResult { +pub fn public_key(deps: Deps, verifier: Addr, key_type: KeyType) -> StdResult { let raw = load_pub_key(deps.storage, verifier, key_type)?; Ok(PublicKey::try_from((key_type, raw)).expect("could not decode pub key")) } + +pub fn caller_authorized(deps: Deps, address: Addr, chain_name: ChainName) -> StdResult { + let is_authorized = AUTHORIZED_CALLERS.may_load(deps.storage, &address)? == Some(chain_name); + Ok(is_authorized) +} diff --git a/contracts/multisig/src/ed25519.rs b/contracts/multisig/src/ed25519.rs index 29c2159a7..550e3ace7 100644 --- a/contracts/multisig/src/ed25519.rs +++ b/contracts/multisig/src/ed25519.rs @@ -14,9 +14,8 @@ pub fn ed25519_verify(msg_hash: &[u8], sig: &[u8], pub_key: &[u8]) -> Result for cosmwasm_std::Event { @@ -74,9 +77,11 @@ impl From for cosmwasm_std::Event { Event::SigningCompleted { session_id, completed_at, + chain_name, } => cosmwasm_std::Event::new("signing_completed") .add_attribute("session_id", session_id) - .add_attribute("completed_at", completed_at.to_string()), + .add_attribute("completed_at", completed_at.to_string()) + .add_attribute("chain", chain_name), Event::PublicKeyRegistered { verifier, public_key, @@ -89,14 +94,20 @@ impl From for cosmwasm_std::Event { "public_key", to_string(&public_key).expect("failed to serialize public key"), ), - Event::CallerAuthorized { contract_address } => { - cosmwasm_std::Event::new("caller_authorized") - .add_attribute("contract_address", contract_address) - } - Event::CallerUnauthorized { contract_address } => { - cosmwasm_std::Event::new("caller_unauthorized") - .add_attribute("contract_address", contract_address) - } + Event::CallerAuthorized { + contract_address, + chain_name, + } => cosmwasm_std::Event::new("caller_authorized") + .add_attribute("contract_address", contract_address) + .add_attribute("chain_name", chain_name), + Event::CallerUnauthorized { + contract_address, + chain_name, + } => cosmwasm_std::Event::new("caller_unauthorized") + .add_attribute("contract_address", contract_address) + .add_attribute("chain_name", chain_name), + Event::SigningEnabled => cosmwasm_std::Event::new("signing_enabled"), + Event::SigningDisabled => cosmwasm_std::Event::new("signing_disabled"), } } } diff --git a/contracts/multisig/src/key.rs b/contracts/multisig/src/key.rs index f2793e0b9..fb73bfc80 100644 --- a/contracts/multisig/src/key.rs +++ b/contracts/multisig/src/key.rs @@ -1,14 +1,15 @@ -use crate::{ - ed25519::{ed25519_verify, ED25519_SIGNATURE_LEN}, - secp256k1::ecdsa_verify, - ContractError, -}; +use std::fmt::Display; + use cosmwasm_schema::cw_serde; use cosmwasm_std::{HexBinary, StdError, StdResult}; use cw_storage_plus::{KeyDeserialize, PrimaryKey}; use enum_display_derive::Display; -use serde::{de::Error, Deserialize, Deserializer}; -use std::fmt::Display; +use serde::de::Error; +use serde::{Deserialize, Deserializer}; + +use crate::ed25519::{ed25519_verify, ED25519_SIGNATURE_LEN}; +use crate::secp256k1::ecdsa_verify; +use crate::ContractError; const ECDSA_COMPRESSED_PUBKEY_LEN: usize = 33; @@ -130,7 +131,7 @@ where { let pk: HexBinary = Deserialize::deserialize(deserializer)?; PublicKey::try_from((KeyType::Ecdsa, pk.clone())) - .map_err(|err| D::Error::custom(format!("failed to deserialize public key: {}", err)))?; + .map_err(|err| Error::custom(format!("failed to deserialize public key: {}", err)))?; Ok(pk) } @@ -140,7 +141,7 @@ where { let pk: HexBinary = Deserialize::deserialize(deserializer)?; PublicKey::try_from((KeyType::Ed25519, pk.clone())) - .map_err(|e| D::Error::custom(format!("failed to deserialize public key: {}", e)))?; + .map_err(|e| Error::custom(format!("failed to deserialize public key: {}", e)))?; Ok(pk) } @@ -312,8 +313,8 @@ impl AsRef<[u8]> for Signature { impl From for HexBinary { fn from(original: PublicKey) -> Self { match original { - PublicKey::Ecdsa(sig) => sig, - PublicKey::Ed25519(sig) => sig, + PublicKey::Ecdsa(key) => key, + PublicKey::Ed25519(key) => key, } } } @@ -323,14 +324,11 @@ mod ecdsa_tests { use cosmwasm_std::HexBinary; use k256::{AffinePoint, EncodedPoint}; - use crate::{ - key::{validate_and_normalize_public_key, Signature}, - test::common::ecdsa_test_data, - types::MsgToSign, - ContractError, - }; - use super::{KeyType, PublicKey}; + use crate::key::{validate_and_normalize_public_key, Signature}; + use crate::test::common::ecdsa_test_data; + use crate::types::MsgToSign; + use crate::ContractError; #[test] fn deserialize_ecdsa_key() { @@ -491,14 +489,11 @@ mod ed25519_tests { use cosmwasm_std::HexBinary; use curve25519_dalek::edwards::CompressedEdwardsY; - use crate::{ - key::{validate_and_normalize_public_key, Signature}, - test::common::ed25519_test_data, - types::MsgToSign, - ContractError, - }; - use super::{KeyType, PublicKey}; + use crate::key::{validate_and_normalize_public_key, Signature}; + use crate::test::common::ed25519_test_data; + use crate::types::MsgToSign; + use crate::ContractError; #[test] fn deserialize_ed25519_key() { diff --git a/contracts/multisig/src/lib.rs b/contracts/multisig/src/lib.rs index 32b01b9ca..e533db1d9 100644 --- a/contracts/multisig/src/lib.rs +++ b/contracts/multisig/src/lib.rs @@ -2,11 +2,10 @@ pub mod contract; pub mod error; pub mod events; pub mod key; -mod migrations; pub mod msg; pub mod multisig; -pub mod signing; -pub mod state; +mod signing; +mod state; pub mod types; pub mod verifier_set; @@ -16,7 +15,7 @@ mod secp256k1; #[cfg(feature = "ed25519")] mod ed25519; -#[cfg(feature = "test")] +#[cfg(any(test, feature = "test"))] pub mod test; pub use crate::error::ContractError; diff --git a/contracts/multisig/src/migrations/mod.rs b/contracts/multisig/src/migrations/mod.rs deleted file mode 100644 index 194e2b5c7..000000000 --- a/contracts/multisig/src/migrations/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod v_0_3; diff --git a/contracts/multisig/src/migrations/v_0_3.rs b/contracts/multisig/src/migrations/v_0_3.rs deleted file mode 100644 index d820eeb51..000000000 --- a/contracts/multisig/src/migrations/v_0_3.rs +++ /dev/null @@ -1,62 +0,0 @@ -use crate::{state::VERIFIER_SETS, verifier_set::VerifierSet}; -use cosmwasm_std::{DepsMut, Order, Response}; -use cw_storage_plus::Map; - -type VerifierSetId = str; -pub const WORKER_SETS: Map<&VerifierSetId, VerifierSet> = Map::new("worker_sets"); - -pub fn migrate_verifier_sets(deps: DepsMut) -> Result { - let all: Vec<_> = WORKER_SETS - .range(deps.storage, None, None, Order::Ascending) - .collect::, _>>()?; - - for v in all { - VERIFIER_SETS.save(deps.storage, &v.0, &v.1)?; - WORKER_SETS.remove(deps.storage, &v.0); - } - - Ok(Response::default()) -} - -#[cfg(test)] -mod test { - use crate::{ - migrations::v_0_3::WORKER_SETS, - state::VERIFIER_SETS, - test::common::{build_verifier_set, ecdsa_test_data::signers}, - }; - - use cosmwasm_std::testing::mock_dependencies; - - use super::migrate_verifier_sets; - - #[test] - fn should_be_able_to_migrate_worker_set_to_verifier_set() { - let mut deps = mock_dependencies(); - let signers = signers(); - let mut worker_sets = vec![]; - let worker_set = build_verifier_set(crate::key::KeyType::Ecdsa, &signers); - WORKER_SETS - .save(&mut deps.storage, &worker_set.id(), &worker_set) - .unwrap(); - worker_sets.push(worker_set); - for s in signers { - let new_signers = vec![s]; - let worker_set = build_verifier_set(crate::key::KeyType::Ecdsa, &new_signers); - WORKER_SETS - .save(&mut deps.storage, &worker_set.id(), &worker_set) - .unwrap(); - worker_sets.push(worker_set); - } - let res = migrate_verifier_sets(deps.as_mut()); - assert!(res.is_ok()); - for worker_set in worker_sets { - let res = VERIFIER_SETS.load(&deps.storage, &worker_set.id()).unwrap(); - assert_eq!(res, worker_set); - assert!(WORKER_SETS - .may_load(&deps.storage, &worker_set.id()) - .unwrap() - .is_none()) - } - } -} diff --git a/contracts/multisig/src/msg.rs b/contracts/multisig/src/msg.rs index fbae987b5..213d76c81 100644 --- a/contracts/multisig/src/msg.rs +++ b/contracts/multisig/src/msg.rs @@ -1,24 +1,37 @@ +use std::collections::HashMap; + +use axelar_wasm_std::nonempty; use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{Addr, HexBinary, Uint128, Uint64}; +use msgs_derive::EnsurePermissions; use router_api::ChainName; -use crate::{ - key::{KeyType, PublicKey, Signature}, - multisig::Multisig, - verifier_set::VerifierSet, -}; +use crate::key::{KeyType, PublicKey, Signature}; +use crate::multisig::Multisig; +use crate::verifier_set::VerifierSet; + +#[cw_serde] +pub struct MigrationMsg { + pub admin_address: String, + pub authorized_callers: HashMap, +} #[cw_serde] pub struct InstantiateMsg { - // the governance address is allowed to modify the authorized caller list for this contract + /// the governance address is allowed to modify the authorized caller list for this contract pub governance_address: String, + /// The admin address (or governance) is allowed to disable signing and enable signing + pub admin_address: String, pub rewards_address: String, - pub block_expiry: u64, + /// number of blocks after which a signing session expires + pub block_expiry: nonempty::Uint64, } #[cw_serde] +#[derive(EnsurePermissions)] pub enum ExecuteMsg { - // Can only be called by an authorized contract. + /// Can only be called by an authorized contract. + #[permission(Specific(authorized))] StartSigningSession { verifier_set_id: String, msg: HexBinary, @@ -31,43 +44,60 @@ pub enum ExecuteMsg { /// [signature_verifier_api::msg] sig_verifier: Option, }, + #[permission(Any)] SubmitSignature { session_id: Uint64, signature: HexBinary, }, - RegisterVerifierSet { - verifier_set: VerifierSet, - }, + #[permission(Any)] + RegisterVerifierSet { verifier_set: VerifierSet }, + #[permission(Any)] RegisterPublicKey { public_key: PublicKey, - /* To prevent anyone from registering a public key that belongs to someone else, we require the sender - to sign their own address using the private key */ + /// To prevent anyone from registering a public key that belongs to someone else, we require the sender + /// to sign their own address using the private key signed_sender_address: HexBinary, }, - // Authorizes a contract to call StartSigningSession. Callable only by governance - AuthorizeCaller { - contract_address: Addr, + /// Authorizes a set of contracts to call StartSigningSession. + #[permission(Governance)] + AuthorizeCallers { + contracts: HashMap, }, - // Unauthorizes a contract, so it can no longer call StartSigningSession. Callable only by governance - UnauthorizeCaller { - contract_address: Addr, + /// Unauthorizes a set of contracts, so they can no longer call StartSigningSession. + #[permission(Elevated)] + UnauthorizeCallers { + contracts: HashMap, }, + + /// Emergency command to stop all amplifier signing + #[permission(Elevated)] + DisableSigning, + + /// Resumes routing after an emergency shutdown + #[permission(Elevated)] + EnableSigning, } #[cw_serde] #[derive(QueryResponses)] pub enum QueryMsg { #[returns(Multisig)] - GetMultisig { session_id: Uint64 }, + Multisig { session_id: Uint64 }, #[returns(VerifierSet)] - GetVerifierSet { verifier_set_id: String }, + VerifierSet { verifier_set_id: String }, #[returns(PublicKey)] - GetPublicKey { + PublicKey { verifier_address: String, key_type: KeyType, }, + + #[returns(bool)] + IsCallerAuthorized { + contract_address: String, + chain_name: ChainName, + }, } #[cw_serde] diff --git a/contracts/multisig/src/multisig.rs b/contracts/multisig/src/multisig.rs index 413e90a75..8e876d364 100644 --- a/contracts/multisig/src/multisig.rs +++ b/contracts/multisig/src/multisig.rs @@ -4,12 +4,10 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::Uint128; use itertools::Itertools; -use crate::{ - key::Signature, - msg::{Signer, SignerWithSig}, - types::MultisigState, - verifier_set::VerifierSet, -}; +use crate::key::Signature; +use crate::msg::{Signer, SignerWithSig}; +use crate::types::MultisigState; +use crate::verifier_set::VerifierSet; #[cw_serde] pub struct Multisig { @@ -51,13 +49,11 @@ impl Multisig { mod test { use cosmwasm_std::{Addr, HexBinary, Uint128}; - use crate::{ - key::{PublicKey, Signature}, - msg::Signer, - multisig::Multisig, - types::MultisigState, - verifier_set::VerifierSet, - }; + use crate::key::{PublicKey, Signature}; + use crate::msg::Signer; + use crate::multisig::Multisig; + use crate::types::MultisigState; + use crate::verifier_set::VerifierSet; #[test] fn optimize_signatures() { diff --git a/contracts/multisig/src/secp256k1.rs b/contracts/multisig/src/secp256k1.rs index c82071575..abedd433d 100644 --- a/contracts/multisig/src/secp256k1.rs +++ b/contracts/multisig/src/secp256k1.rs @@ -14,9 +14,8 @@ pub fn ecdsa_verify(msg_hash: &[u8], sig: &[u8], pub_key: &[u8]) -> Result, verifier_set: &Verifi #[cfg(test)] mod tests { - use cosmwasm_std::{ - testing::{MockQuerier, MockStorage}, - to_json_binary, Addr, HexBinary, QuerierWrapper, - }; - - use crate::{ - key::KeyType, - test::common::build_verifier_set, - test::common::{ecdsa_test_data, ed25519_test_data}, - }; + use cosmwasm_std::testing::MockQuerier; + use cosmwasm_std::{to_json_binary, Addr, HexBinary, QuerierWrapper}; use super::*; + use crate::key::KeyType; + use crate::test::common::{build_verifier_set, ecdsa_test_data, ed25519_test_data}; pub struct TestConfig { - pub store: MockStorage, pub verifier_set: VerifierSet, pub session: SigningSession, pub signatures: HashMap, @@ -155,8 +146,6 @@ mod tests { } fn ecdsa_setup() -> TestConfig { - let store = MockStorage::new(); - let signers = ecdsa_test_data::signers(); let verifier_set_id = "subkey".to_string(); @@ -185,7 +174,6 @@ mod tests { .collect(); TestConfig { - store, verifier_set, session, signatures, @@ -194,8 +182,6 @@ mod tests { } fn ed25519_setup() -> TestConfig { - let store = MockStorage::new(); - let signers = ed25519_test_data::signers(); let verifier_set_id = "subkey".to_string(); @@ -224,7 +210,6 @@ mod tests { .collect(); TestConfig { - store, verifier_set, session, signatures, diff --git a/contracts/multisig/src/state.rs b/contracts/multisig/src/state.rs index 66e5cf529..844235418 100644 --- a/contracts/multisig/src/state.rs +++ b/contracts/multisig/src/state.rs @@ -1,26 +1,30 @@ use std::collections::HashMap; +use axelar_wasm_std::nonempty; use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, HexBinary, Order, StdResult, Storage, Uint64}; use cw_storage_plus::{Index, IndexList, IndexedMap, Item, Map, UniqueIndex}; +use router_api::ChainName; -use crate::{ - key::{KeyType, KeyTyped, PublicKey, Signature}, - signing::SigningSession, - verifier_set::VerifierSet, - ContractError, -}; +use crate::key::{KeyType, KeyTyped, PublicKey, Signature}; +use crate::signing::SigningSession; +use crate::verifier_set::VerifierSet; +use crate::ContractError; #[cw_serde] pub struct Config { - pub governance: Addr, pub rewards_contract: Addr, - pub block_expiry: u64, // number of blocks after which a signing session expires + pub block_expiry: nonempty::Uint64, // number of blocks after which a signing session expires } +type VerifierSetId = str; + pub const CONFIG: Item = Item::new("config"); pub const SIGNING_SESSION_COUNTER: Item = Item::new("signing_session_counter"); pub const SIGNING_SESSIONS: Map = Map::new("signing_sessions"); +// The keys represent the addresses that can start a signing session. +pub const AUTHORIZED_CALLERS: Map<&Addr, ChainName> = Map::new("authorized_callers"); +pub const VERIFIER_SETS: Map<&VerifierSetId, VerifierSet> = Map::new("verifier_sets"); /// Signatures by session id and signer address pub const SIGNATURES: Map<(u64, &str), Signature> = Map::new("signatures"); @@ -56,9 +60,7 @@ pub fn save_signature( ) } -type VerifierSetId = str; -pub const VERIFIER_SETS: Map<&VerifierSetId, VerifierSet> = Map::new("verifier_sets"); -pub fn get_verifier_set( +pub fn verifier_set( store: &dyn Storage, verifier_set_id: &str, ) -> Result { @@ -110,17 +112,13 @@ pub fn save_pub_key( Ok(pub_keys().save(store, (signer, pub_key.key_type()), &pub_key.into())?) } -// The keys represent the addresses that can start a signing session. -pub const AUTHORIZED_CALLERS: Map<&Addr, ()> = Map::new("authorized_callers"); - #[cfg(test)] mod tests { use cosmwasm_std::testing::mock_dependencies; - use crate::test::common::ecdsa_test_data; - use super::*; + use crate::test::common::ecdsa_test_data; #[test] fn should_fail_if_duplicate_public_key() { diff --git a/contracts/multisig/src/test/common.rs b/contracts/multisig/src/test/common.rs index ff32de0c6..9b0ad9335 100644 --- a/contracts/multisig/src/test/common.rs +++ b/contracts/multisig/src/test/common.rs @@ -1,10 +1,8 @@ use axelar_wasm_std::Participant; use cosmwasm_std::{Addr, HexBinary, Uint128}; -use crate::{ - key::{KeyType, PublicKey}, - verifier_set::VerifierSet, -}; +use crate::key::{KeyType, PublicKey}; +use crate::verifier_set::VerifierSet; #[derive(Clone)] pub struct TestSigner { diff --git a/contracts/multisig/src/testdata/verifier_set_hash_unchanged.golden b/contracts/multisig/src/testdata/verifier_set_hash_unchanged.golden new file mode 100644 index 000000000..4c871bd0e --- /dev/null +++ b/contracts/multisig/src/testdata/verifier_set_hash_unchanged.golden @@ -0,0 +1 @@ +"1dbf312c423dd70771c471c71d18e3d756aaab6d5b313b41289e002017610e84" \ No newline at end of file diff --git a/contracts/multisig/src/testdata/verifier_set_id_unchanged.golden b/contracts/multisig/src/testdata/verifier_set_id_unchanged.golden new file mode 100644 index 000000000..4c871bd0e --- /dev/null +++ b/contracts/multisig/src/testdata/verifier_set_id_unchanged.golden @@ -0,0 +1 @@ +"1dbf312c423dd70771c471c71d18e3d756aaab6d5b313b41289e002017610e84" \ No newline at end of file diff --git a/contracts/multisig/src/types.rs b/contracts/multisig/src/types.rs index f20a77904..60091ee4e 100644 --- a/contracts/multisig/src/types.rs +++ b/contracts/multisig/src/types.rs @@ -18,6 +18,7 @@ impl AsRef<[u8]> for MsgToSign { } } +#[cfg(any(test, feature = "test"))] impl MsgToSign { pub fn unchecked(hex: HexBinary) -> Self { Self(hex) @@ -44,15 +45,14 @@ impl TryFrom for MsgToSign { }); } - Ok(MsgToSign::unchecked(other)) + Ok(MsgToSign(other)) } } #[cfg(test)] mod tests { - use crate::test::common::ecdsa_test_data; - use super::*; + use crate::test::common::ecdsa_test_data; #[test] fn test_try_from_hexbinary_to_message() { diff --git a/contracts/multisig/src/verifier_set.rs b/contracts/multisig/src/verifier_set.rs index dc8eaabed..1e3881ef6 100644 --- a/contracts/multisig/src/verifier_set.rs +++ b/contracts/multisig/src/verifier_set.rs @@ -1,14 +1,17 @@ use std::collections::{BTreeMap, HashMap}; -use crate::{key::PublicKey, msg::Signer}; +use axelar_wasm_std::hash::Hash; use axelar_wasm_std::Participant; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{HexBinary, Uint128}; +use cosmwasm_std::{Addr, HexBinary, Uint128}; use sha3::{Digest, Keccak256}; +use crate::key::PublicKey; +use crate::msg::Signer; + #[cw_serde] pub struct VerifierSet { - // An ordered map with the signer's address as the key, and the signer as the value. + // An ordered map with the signer's axelar address as the key, and the signer as the value. pub signers: BTreeMap, pub threshold: Uint128, // for hash uniqueness. The same exact verifier set could be in use at two different times, @@ -43,20 +46,60 @@ impl VerifierSet { } } - pub fn hash(&self) -> HexBinary { - Keccak256::digest(serde_json::to_vec(&self).expect("couldn't serialize verifier set")) - .as_slice() - .into() + pub fn hash(&self) -> Hash { + let mut hasher = Keccak256::new(); + + // Length prefix the bytes to be hashed to prevent hash collisions + hasher.update(self.signers.len().to_be_bytes()); + + self.signers.values().for_each(|signer| { + hasher.update(signer.address.as_bytes()); + hasher.update(signer.pub_key.as_ref()); + hasher.update(signer.weight.to_be_bytes()); + }); + + hasher.update(self.threshold.to_be_bytes()); + hasher.update(self.created_at.to_be_bytes()); + + hasher.finalize().into() } pub fn id(&self) -> String { - self.hash().to_hex() + HexBinary::from(self.hash()).to_hex() } - pub fn get_pub_keys(&self) -> HashMap { + pub fn pub_keys(&self) -> HashMap { self.signers .iter() .map(|(address, signer)| (address.clone(), signer.pub_key.clone())) .collect() } + + pub fn includes(&self, signer: &Addr) -> bool { + self.signers.contains_key(signer.as_str()) + } +} + +#[cfg(test)] +mod tests { + use crate::key::KeyType; + use crate::test::common::{build_verifier_set, ecdsa_test_data}; + + // If this test fails, it means the verifier set hash has changed and therefore a migration is needed. + #[test] + fn verifier_set_hash_unchanged() { + let signers = ecdsa_test_data::signers(); + let verifier_set = build_verifier_set(KeyType::Ecdsa, &signers); + + goldie::assert_json!(hex::encode(verifier_set.hash())); + } + + // If this test fails, it means the verifier set hash has changed and therefore a migration is needed. + #[test] + fn verifier_set_id_unchanged() { + let signers = ecdsa_test_data::signers(); + let verifier_set = build_verifier_set(KeyType::Ecdsa, &signers); + + goldie::assert_json!(verifier_set.id()); + } } diff --git a/contracts/service-registry/.cargo/config b/contracts/nexus-gateway/.cargo/config.toml similarity index 100% rename from contracts/service-registry/.cargo/config rename to contracts/nexus-gateway/.cargo/config.toml diff --git a/contracts/nexus-gateway/Cargo.toml b/contracts/nexus-gateway/Cargo.toml index 818749f73..409087cce 100644 --- a/contracts/nexus-gateway/Cargo.toml +++ b/contracts/nexus-gateway/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "nexus-gateway" -version = "0.2.3" +version = "1.0.0" rust-version = { workspace = true } -edition = "2021" +edition = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] @@ -12,9 +12,13 @@ crate-type = ["cdylib", "rlib"] name = "nexus-gateway-schema" path = "src/bin/schema.rs" +[features] +library = [] + [dependencies] -axelar-wasm-std = { workspace = true } -axelar-wasm-std-derive = { workspace = true } +axelar-wasm-std = { workspace = true, features = ["derive"] } +axelarnet-gateway = { workspace = true, features = ["library"] } +client = { workspace = true } cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } cw-storage-plus = { workspace = true } @@ -22,10 +26,12 @@ cw2 = { workspace = true } error-stack = { workspace = true } hex = "0.4.3" mockall = "0.11.4" +msgs-derive = { workspace = true } report = { workspace = true } router-api = { workspace = true } schemars = "0.8.15" serde = { version = "1.0.188", features = ["derive"] } +sha3 = { workspace = true } thiserror = { workspace = true } voting-verifier = { workspace = true, features = ["library"] } diff --git a/contracts/nexus-gateway/src/bin/schema.rs b/contracts/nexus-gateway/src/bin/schema.rs index cb8b8c93c..a3a20bcbe 100644 --- a/contracts/nexus-gateway/src/bin/schema.rs +++ b/contracts/nexus-gateway/src/bin/schema.rs @@ -1,5 +1,4 @@ use cosmwasm_schema::write_api; - use nexus_gateway::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; fn main() { diff --git a/contracts/nexus-gateway/src/contract.rs b/contracts/nexus-gateway/src/contract.rs index 71983bf06..0fbf6d312 100644 --- a/contracts/nexus-gateway/src/contract.rs +++ b/contracts/nexus-gateway/src/contract.rs @@ -1,12 +1,14 @@ +use axelar_wasm_std::address; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{DepsMut, Empty, Env, MessageInfo, Response}; -use error_stack::ResultExt; +use cosmwasm_std::{Addr, DepsMut, Empty, Env, MessageInfo, Response, Storage}; +use error_stack::Report; +use crate::contract::execute::{call_contract_with_token, route_to_nexus, route_to_router}; use crate::error::ContractError; use crate::msg::{ExecuteMsg, InstantiateMsg}; -use crate::nexus; -use crate::state::{Config, GatewayStore, Store}; +use crate::state::Config; +use crate::{nexus, state}; mod execute; @@ -18,7 +20,7 @@ pub fn migrate( deps: DepsMut, _env: Env, _msg: Empty, -) -> Result { +) -> Result { // any version checks should be done before here cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; @@ -32,15 +34,22 @@ pub fn instantiate( _env: Env, _info: MessageInfo, msg: InstantiateMsg, -) -> Result { +) -> Result { cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - let nexus = deps.api.addr_validate(&msg.nexus)?; - let router = deps.api.addr_validate(&msg.router)?; + let nexus = address::validate_cosmwasm_address(deps.api, &msg.nexus)?; + let router = address::validate_cosmwasm_address(deps.api, &msg.router)?; + let axelar_gateway = address::validate_cosmwasm_address(deps.api, &msg.axelar_gateway)?; - GatewayStore::new(deps.storage) - .save_config(Config { nexus, router }) - .expect("config must be saved"); + state::save_config( + deps.storage, + Config { + nexus, + router, + axelar_gateway, + }, + ) + .expect("config must be saved"); Ok(Response::default()) } @@ -48,50 +57,56 @@ pub fn instantiate( #[cfg_attr(not(feature = "library"), entry_point)] pub fn execute( deps: DepsMut, - _env: Env, + _: Env, info: MessageInfo, msg: ExecuteMsg, -) -> Result, axelar_wasm_std::ContractError> { - let contract = Contract::new(GatewayStore::new(deps.storage)); - - let res = match msg { - ExecuteMsg::RouteMessages(msgs) => contract - .route_to_nexus(info.sender, msgs) - .change_context(ContractError::RouteToNexus)?, - ExecuteMsg::RouteMessagesFromNexus(msgs) => contract - .route_to_router(info.sender, msgs) - .change_context(ContractError::RouteToRouter)?, +) -> Result, axelar_wasm_std::error::ContractError> { + let res = match msg.ensure_permissions(deps.storage, &info.sender, match_router, match_nexus)? { + ExecuteMsg::CallContractWithToken { + destination_chain, + destination_address, + payload, + } => call_contract_with_token( + deps.storage, + deps.querier, + info, + destination_chain, + destination_address, + payload, + )?, + ExecuteMsg::RouteMessages(msgs) => route_to_nexus(deps.storage, msgs)?, + ExecuteMsg::RouteMessagesFromNexus(msgs) => route_to_router(deps.storage, msgs)?, }; Ok(res) } -struct Contract -where - S: Store, -{ - store: S, - config: Config, +fn match_router(storage: &dyn Storage, _: &ExecuteMsg) -> Result> { + Ok(state::load_config(storage)?.router) } -impl Contract -where - S: Store, -{ - pub fn new(store: S) -> Self { - let config = store.load_config().expect("config must be loaded"); - - Self { store, config } - } +fn match_nexus(storage: &dyn Storage, _: &ExecuteMsg) -> Result> { + Ok(state::load_config(storage)?.nexus) } #[cfg(test)] mod tests { - - use cosmwasm_std::testing::{mock_dependencies, mock_env}; + use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; + use axelar_wasm_std::{err_contains, permission_control}; + use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; + use cosmwasm_std::{ + from_json, to_json_binary, Addr, BankMsg, Coin, CosmosMsg, QuerierResult, SubMsg, WasmMsg, + WasmQuery, + }; + use hex::decode; + use router_api::CrossChainId; use super::*; + const NEXUS: &str = "nexus"; + const ROUTER: &str = "router"; + const AXELAR_GATEWAY: &str = "axelar_gateway"; + #[test] fn migrate_sets_contract_version() { let mut deps = mock_dependencies(); @@ -102,4 +117,305 @@ mod tests { assert_eq!(contract_version.contract, "nexus-gateway"); assert_eq!(contract_version.version, CONTRACT_VERSION); } + + #[test] + fn call_contract_with_token_no_token() { + let mut deps = mock_dependencies(); + instantiate_contract(deps.as_mut()); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(NEXUS, &[]), + ExecuteMsg::CallContractWithToken { + destination_chain: "destinationChain".parse().unwrap(), + destination_address: "0xD419".parse().unwrap(), + payload: decode("bb9b5566c2f4876863333e481f4698350154259ffe6226e283b16ce18a64bcf1") + .unwrap() + .into(), + }, + ); + assert!(res.is_err_and(|err| err_contains!( + err.report, + ContractError, + ContractError::InvalidToken { .. } + ))); + } + + #[test] + fn call_contract_with_token() { + let mut deps = mock_dependencies(); + deps.querier.update_wasm(move |msg| match msg { + WasmQuery::Smart { contract_addr, msg } if contract_addr == AXELAR_GATEWAY => { + let msg = from_json::(msg).unwrap(); + + match msg { + axelarnet_gateway::msg::QueryMsg::ChainName => { + QuerierResult::Ok(to_json_binary("axelarnet").into()) + } + _ => panic!("unexpected query: {:?}", msg), + } + } + _ => panic!("unexpected query: {:?}", msg), + }); + instantiate_contract(deps.as_mut()); + + let token = Coin { + denom: "denom".to_string(), + amount: 100u128.into(), + }; + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(NEXUS, &[token.clone()]), + ExecuteMsg::CallContractWithToken { + destination_chain: "destinationChain".parse().unwrap(), + destination_address: "0xD419".parse().unwrap(), + payload: decode("bb9b5566c2f4876863333e481f4698350154259ffe6226e283b16ce18a64bcf1") + .unwrap() + .into(), + }, + ); + assert!(res.is_ok_and(move |res| match res.messages.as_slice() { + [SubMsg { + msg: CosmosMsg::Bank(BankMsg::Send { to_address, amount }), + .. + }, SubMsg { + msg: + CosmosMsg::Custom(nexus::Message { + token: Some(actual_token), + .. + }), + .. + }] => *actual_token == token && to_address == NEXUS && *amount == vec![token], + _ => false, + })); + } + + #[test] + fn route_to_router_unauthorized() { + let mut deps = mock_dependencies(); + instantiate_contract(deps.as_mut()); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info("unauthorized", &[]), + ExecuteMsg::RouteMessagesFromNexus(vec![]), + ); + + assert!(res.is_err_and(|err| err_contains!( + err.report, + permission_control::Error, + permission_control::Error::AddressNotWhitelisted { .. } + ))); + } + + #[test] + fn route_to_router_with_no_msg() { + let mut deps = mock_dependencies(); + instantiate_contract(deps.as_mut()); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(NEXUS, &[]), + ExecuteMsg::RouteMessagesFromNexus(vec![]), + ); + + assert!(res.is_ok_and(|res| res.messages.is_empty())); + } + + #[test] + fn route_to_router_with_msgs() { + let mut deps = mock_dependencies(); + instantiate_contract(deps.as_mut()); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(NEXUS, &[]), + ExecuteMsg::RouteMessagesFromNexus(nexus_messages()), + ); + + assert!(res.is_ok_and(|res| { + if res.messages.len() != 1 { + return false; + } + + match &res.messages[0].msg { + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr, + msg, + funds, + }) => { + if let Ok(router_api::msg::ExecuteMsg::RouteMessages(msgs)) = from_json(msg) { + return *contract_addr == Addr::unchecked(ROUTER) + && msgs.len() == 2 + && funds.is_empty(); + } + + false + } + _ => false, + } + })); + } + + #[test] + fn route_to_nexus_unauthorized() { + let mut deps = mock_dependencies(); + instantiate_contract(deps.as_mut()); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info("unauthorized", &[]), + ExecuteMsg::RouteMessages(vec![]), + ); + + assert!(res.is_err_and(|err| err_contains!( + err.report, + permission_control::Error, + permission_control::Error::AddressNotWhitelisted { .. } + ))); + } + + #[test] + fn route_to_nexus_with_no_msg() { + let mut deps = mock_dependencies(); + instantiate_contract(deps.as_mut()); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(ROUTER, &[]), + ExecuteMsg::RouteMessages(vec![]), + ); + + assert!(res.is_ok_and(|res| res.messages.is_empty())); + } + + #[test] + fn route_to_nexus_with_msgs_only_route_once() { + let mut deps = mock_dependencies(); + instantiate_contract(deps.as_mut()); + + let msgs = router_messages(); + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(ROUTER, &[]), + ExecuteMsg::RouteMessages(msgs.clone()), + ); + + assert!(res.is_ok_and(|res| res.messages.len() == 2)); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(ROUTER, &[]), + ExecuteMsg::RouteMessages(msgs.clone()), + ); + + assert!(res.is_ok_and(|res| res.messages.is_empty())); + } + + fn nexus_messages() -> Vec { + let msg_ids = [ + HexTxHashAndEventIndex { + tx_hash: vec![0x2f; 32].try_into().unwrap(), + event_index: 100, + }, + HexTxHashAndEventIndex { + tx_hash: vec![0x23; 32].try_into().unwrap(), + event_index: 1000, + }, + ]; + let msgs = vec![ + nexus::Message { + source_chain: "sourceChain".parse().unwrap(), + source_address: "0xb860".parse().unwrap(), + destination_address: "0xD419".parse().unwrap(), + destination_chain: "destinationChain".parse().unwrap(), + payload_hash: decode( + "bb9b5566c2f4876863333e481f4698350154259ffe6226e283b16ce18a64bcf1", + ) + .unwrap() + .try_into() + .unwrap(), + source_tx_id: msg_ids[0].tx_hash.to_vec().try_into().unwrap(), + source_tx_index: msg_ids[0].event_index as u64, + id: msg_ids[0].to_string(), + token: None, + }, + nexus::Message { + source_chain: "sourceChain".parse().unwrap(), + source_address: "0xc860".parse().unwrap(), + destination_address: "0xA419".parse().unwrap(), + destination_chain: "destinationChain".parse().unwrap(), + payload_hash: decode( + "cb9b5566c2f4876853333e481f4698350154259ffe6226e283b16ce18a64bcf1", + ) + .unwrap() + .try_into() + .unwrap(), + source_tx_id: msg_ids[1].tx_hash.to_vec().try_into().unwrap(), + source_tx_index: msg_ids[1].event_index as u64, + id: msg_ids[1].to_string(), + token: None, + }, + ]; + msgs + } + + fn router_messages() -> Vec { + let msgs = vec![ + router_api::Message { + cc_id: CrossChainId { + source_chain: "sourceChain".parse().unwrap(), + message_id: "0x2fe4:0".parse().unwrap(), + }, + source_address: "0xb860".parse().unwrap(), + destination_address: "0xD419".parse().unwrap(), + destination_chain: "destinationChain".parse().unwrap(), + payload_hash: decode( + "bb9b5566c2f4876863333e481f4698350154259ffe6226e283b16ce18a64bcf1", + ) + .unwrap() + .try_into() + .unwrap(), + }, + router_api::Message { + cc_id: CrossChainId { + source_chain: "sourceChain".parse().unwrap(), + message_id: "0x6b33:10".parse().unwrap(), + }, + source_address: "0x70725".parse().unwrap(), + destination_address: "0x7FAD".parse().unwrap(), + destination_chain: "destinationChain".parse().unwrap(), + payload_hash: decode( + "bb9b5566c2f4876863333e481f4698350154259ffe6226e283b16ce18a64bcf1", + ) + .unwrap() + .try_into() + .unwrap(), + }, + ]; + msgs + } + + fn instantiate_contract(deps: DepsMut) { + instantiate( + deps, + mock_env(), + mock_info("admin", &[]), + InstantiateMsg { + nexus: NEXUS.to_string(), + router: ROUTER.to_string(), + axelar_gateway: AXELAR_GATEWAY.to_string(), + }, + ) + .unwrap(); + } } diff --git a/contracts/nexus-gateway/src/contract/execute.rs b/contracts/nexus-gateway/src/contract/execute.rs index c602c6b42..71374ca8a 100644 --- a/contracts/nexus-gateway/src/contract/execute.rs +++ b/contracts/nexus-gateway/src/contract/execute.rs @@ -1,332 +1,101 @@ -use cosmwasm_std::{to_json_binary, Addr, Response, WasmMsg}; -use error_stack::report; +use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; +use axelar_wasm_std::nonempty; +use cosmwasm_std::{BankMsg, HexBinary, MessageInfo, QuerierWrapper, Response, Storage}; +use error_stack::{bail, ResultExt}; +use router_api::{Address, ChainName}; +use sha3::{Digest, Keccak256}; use crate::error::ContractError; -use crate::nexus; -use crate::state::Store; - -use super::Contract; +use crate::state::load_config; +use crate::{nexus, state}; type Result = error_stack::Result; -impl Contract -where - S: Store, -{ - pub fn route_to_router( - self, - sender: Addr, - msgs: Vec, - ) -> Result> { - if sender != self.config.nexus { - return Err(report!(ContractError::Unauthorized)); - } - - let msgs: Vec<_> = msgs - .into_iter() - .map(router_api::Message::try_from) - .collect::>>()?; - if msgs.is_empty() { - return Ok(Response::default()); - } - - Ok(Response::new().add_message(WasmMsg::Execute { - contract_addr: self.config.router.to_string(), - msg: to_json_binary(&router_api::msg::ExecuteMsg::RouteMessages(msgs)) - .expect("must serialize route-messages message"), - funds: vec![], - })) - } - - pub fn route_to_nexus( - mut self, - sender: Addr, - msgs: Vec, - ) -> Result> { - if sender != self.config.router { - return Err(report!(ContractError::Unauthorized)); - } - - let msgs = msgs - .into_iter() - .filter_map(|msg| match self.store.is_message_routed(&msg.cc_id) { - Ok(true) => None, - Ok(false) => Some(Ok(msg)), - Err(err) => Some(Err(err)), - }) - .collect::>>()?; - - msgs.iter() - .try_for_each(|msg| self.store.set_message_routed(&msg.cc_id))?; - - let msgs: Vec = msgs.into_iter().map(Into::into).collect(); - - Ok(Response::new().add_messages(msgs)) - } +pub fn call_contract_with_token( + storage: &mut dyn Storage, + querier: QuerierWrapper, + info: MessageInfo, + destination_chain: ChainName, + destination_address: Address, + payload: HexBinary, +) -> Result> { + let config = load_config(storage)?; + let axelarnet_gateway: axelarnet_gateway::Client = + client::Client::new(querier, config.axelar_gateway).into(); + let source_address: Address = info + .sender + .into_string() + .parse() + .expect("invalid sender address"); + let token = match info.funds.as_slice() { + [token] => token.clone(), + _ => bail!(ContractError::InvalidToken(info.funds)), + }; + // TODO: Retrieve the actual tx hash and event index from core, since cosmwasm doesn't provide it. Use the all zeros as the placeholder in the meantime. + let tx_hash = [0; 32]; + let event_index = 0u32; + let id = HexTxHashAndEventIndex::new(tx_hash, event_index); + + // send the token to the nexus module account + let bank_transfer_msg = BankMsg::Send { + to_address: config.nexus.to_string(), + amount: vec![token.clone()], + }; + let msg = nexus::Message { + source_chain: axelarnet_gateway + .chain_name() + .change_context(ContractError::AxelarnetGateway)? + .into(), + source_address, + destination_chain, + destination_address, + payload_hash: Keccak256::digest(payload.as_slice()).into(), + source_tx_id: tx_hash + .to_vec() + .try_into() + .expect("tx hash must not be empty"), + source_tx_index: event_index.into(), + id: nonempty::String::from(id).into(), + token: Some(token), + }; + + Ok(Response::new() + .add_message(bank_transfer_msg) + .add_message(msg)) } -#[cfg(test)] -mod test { - use cosmwasm_std::{from_json, CosmosMsg}; - use hex::decode; - - use router_api::CrossChainId; - - use crate::state::{Config, MockStore}; - - use super::*; - - #[test] - fn route_to_router_unauthorized() { - let mut store = MockStore::new(); - let config = Config { - nexus: Addr::unchecked("nexus"), - router: Addr::unchecked("router"), - }; - store - .expect_load_config() - .returning(move || Ok(config.clone())); - let contract = Contract::new(store); - - let res = contract.route_to_router(Addr::unchecked("unauthorized"), vec![]); - - assert!(res.is_err_and(|err| matches!(err.current_context(), ContractError::Unauthorized))); - } - - #[test] - fn route_to_router_with_no_msg() { - let mut store = MockStore::new(); - let config = Config { - nexus: Addr::unchecked("nexus"), - router: Addr::unchecked("router"), - }; - store - .expect_load_config() - .returning(move || Ok(config.clone())); - let contract = Contract::new(store); - - let res = contract.route_to_router(Addr::unchecked("nexus"), vec![]); - - assert!(res.is_ok_and(|res| res.messages.is_empty())); - } - - #[test] - fn route_to_router_with_msgs() { - let mut store = MockStore::new(); - let config = Config { - nexus: Addr::unchecked("nexus"), - router: Addr::unchecked("router"), - }; - store - .expect_load_config() - .returning(move || Ok(config.clone())); - let contract = Contract::new(store); - - let msgs = vec![ - nexus::Message { - source_chain: "sourceChain".parse().unwrap(), - source_address: "0xb860".parse().unwrap(), - destination_address: "0xD419".parse().unwrap(), - destination_chain: "destinationChain".parse().unwrap(), - payload_hash: decode( - "bb9b5566c2f4876863333e481f4698350154259ffe6226e283b16ce18a64bcf1", - ) - .unwrap() - .try_into() - .unwrap(), - source_tx_id: vec![0x2f; 32].try_into().unwrap(), - source_tx_index: 100, - }, - nexus::Message { - source_chain: "sourceChain".parse().unwrap(), - source_address: "0xc860".parse().unwrap(), - destination_address: "0xA419".parse().unwrap(), - destination_chain: "destinationChain".parse().unwrap(), - payload_hash: decode( - "cb9b5566c2f4876853333e481f4698350154259ffe6226e283b16ce18a64bcf1", - ) - .unwrap() - .try_into() - .unwrap(), - source_tx_id: vec![0x23; 32].try_into().unwrap(), - source_tx_index: 1000, - }, - ]; - let res = contract.route_to_router(Addr::unchecked("nexus"), msgs); - - assert!(res.is_ok_and(|res| { - if res.messages.len() != 1 { - return false; - } - - match &res.messages[0].msg { - CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr, - msg, - funds, - }) => { - if let Ok(router_api::msg::ExecuteMsg::RouteMessages(msgs)) = from_json(msg) { - return *contract_addr == Addr::unchecked("router") - && msgs.len() == 2 - && funds.is_empty(); - } - - false - } - _ => false, - } - })); - } - - #[test] - fn route_to_nexus_unauthorized() { - let mut store = MockStore::new(); - let config = Config { - nexus: Addr::unchecked("nexus"), - router: Addr::unchecked("router"), - }; - store - .expect_load_config() - .returning(move || Ok(config.clone())); - let contract = Contract::new(store); - - let res = contract.route_to_nexus(Addr::unchecked("unauthorized"), vec![]); - - assert!(res.is_err_and(|err| matches!(err.current_context(), ContractError::Unauthorized))); - } - - #[test] - fn route_to_nexus_with_no_msg() { - let mut store = MockStore::new(); - let config = Config { - nexus: Addr::unchecked("nexus"), - router: Addr::unchecked("router"), - }; - store - .expect_load_config() - .returning(move || Ok(config.clone())); - let contract = Contract::new(store); - - let res = contract.route_to_nexus(Addr::unchecked("router"), vec![]); - - assert!(res.is_ok_and(|res| res.messages.is_empty())); - } - - #[test] - fn route_to_nexus_with_msgs_that_have_not_been_routed() { - let mut store = MockStore::new(); - let config = Config { - nexus: Addr::unchecked("nexus"), - router: Addr::unchecked("router"), - }; - store - .expect_load_config() - .returning(move || Ok(config.clone())); - store - .expect_is_message_routed() - .times(2) - .returning(|_| Ok(false)); - store - .expect_set_message_routed() - .times(2) - .returning(|_| Ok(())); - let contract = Contract::new(store); - - let msgs = vec![ - router_api::Message { - cc_id: CrossChainId { - chain: "sourceChain".parse().unwrap(), - id: "0x2fe4:0".parse().unwrap(), - }, - source_address: "0xb860".parse().unwrap(), - destination_address: "0xD419".parse().unwrap(), - destination_chain: "destinationChain".parse().unwrap(), - payload_hash: decode( - "bb9b5566c2f4876863333e481f4698350154259ffe6226e283b16ce18a64bcf1", - ) - .unwrap() - .try_into() - .unwrap(), - }, - router_api::Message { - cc_id: CrossChainId { - chain: "sourceChain".parse().unwrap(), - id: "0x6b33:10".parse().unwrap(), - }, - source_address: "0x0725".parse().unwrap(), - destination_address: "0x7FAD".parse().unwrap(), - destination_chain: "destinationChain".parse().unwrap(), - payload_hash: decode( - "bb9b5566c2f4876863333e481f4698350154259ffe6226e283b16ce18a64bcf1", - ) - .unwrap() - .try_into() - .unwrap(), - }, - ]; - let res = contract.route_to_nexus(Addr::unchecked("router"), msgs); - - assert!(res.is_ok_and(|res| res.messages.len() == 2)); - } - - #[test] - fn route_to_nexus_with_msgs_that_have_been_routed() { - let mut store = MockStore::new(); - let config = Config { - nexus: Addr::unchecked("nexus"), - router: Addr::unchecked("router"), - }; - store - .expect_load_config() - .returning(move || Ok(config.clone())); - store - .expect_is_message_routed() - .once() - .returning(|_| Ok(false)); - store - .expect_is_message_routed() - .once() - .returning(|_| Ok(true)); - store - .expect_set_message_routed() - .once() - .returning(|_| Ok(())); - let contract = Contract::new(store); - - let msgs = vec![ - router_api::Message { - cc_id: CrossChainId { - chain: "sourceChain".parse().unwrap(), - id: "0x2fe4:0".parse().unwrap(), - }, - source_address: "0xb860".parse().unwrap(), - destination_address: "0xD419".parse().unwrap(), - destination_chain: "destinationChain".parse().unwrap(), - payload_hash: decode( - "bb9b5566c2f4876863333e481f4698350154259ffe6226e283b16ce18a64bcf1", - ) - .unwrap() - .try_into() - .unwrap(), - }, - router_api::Message { - cc_id: CrossChainId { - chain: "sourceChain".parse().unwrap(), - id: "0x6b33:10".parse().unwrap(), - }, - source_address: "0x70725".parse().unwrap(), - destination_address: "0x7FAD".parse().unwrap(), - destination_chain: "destinationChain".parse().unwrap(), - payload_hash: decode( - "bb9b5566c2f4876863333e481f4698350154259ffe6226e283b16ce18a64bcf1", - ) - .unwrap() - .try_into() - .unwrap(), - }, - ]; - let res = contract.route_to_nexus(Addr::unchecked("router"), msgs); +pub fn route_to_router( + storage: &dyn Storage, + msgs: Vec, +) -> Result> { + let msgs: Vec<_> = msgs + .into_iter() + .map(router_api::Message::try_from) + .collect::>>()?; + let router = router_api::client::Router { + address: state::load_config(storage)?.router, + }; + + Ok(Response::new().add_messages(router.route(msgs))) +} - assert!(res.is_ok_and(|res| res.messages.len() == 1)); - } +pub fn route_to_nexus( + storage: &mut dyn Storage, + msgs: Vec, +) -> Result> { + let msgs = msgs + .into_iter() + .filter_map(|msg| match state::is_message_routed(storage, &msg.cc_id) { + Ok(true) => None, + Ok(false) => Some(Ok(msg)), + Err(err) => Some(Err(err)), + }) + .collect::>>()?; + + msgs.iter() + .try_for_each(|msg| state::set_message_routed(storage, &msg.cc_id))?; + + let msgs: Vec = msgs.into_iter().map(Into::into).collect(); + + Ok(Response::new().add_messages(msgs)) } diff --git a/contracts/nexus-gateway/src/error.rs b/contracts/nexus-gateway/src/error.rs index 385d2d5b4..18a9fed2f 100644 --- a/contracts/nexus-gateway/src/error.rs +++ b/contracts/nexus-gateway/src/error.rs @@ -1,13 +1,9 @@ -use cosmwasm_std::HexBinary; +use axelar_wasm_std::IntoContractError; +use cosmwasm_std::{Coin, HexBinary}; use thiserror::Error; -use axelar_wasm_std_derive::IntoContractError; - #[derive(Error, Debug, PartialEq, IntoContractError)] pub enum ContractError { - #[error("caller is not authorized")] - Unauthorized, - #[error("store failed saving/loading data")] StoreFailure, @@ -23,9 +19,9 @@ pub enum ContractError { #[error("invalid payload hash {0}")] InvalidMessagePayloadHash(HexBinary), - #[error("failed routing messages to the nexus module")] - RouteToNexus, + #[error("invalid token: one and only one token is required for this operation, got {0:?}")] + InvalidToken(Vec), - #[error("failed routing messages to the router")] - RouteToRouter, + #[error("failed querying the axelarnet gateway")] + AxelarnetGateway, } diff --git a/contracts/nexus-gateway/src/msg.rs b/contracts/nexus-gateway/src/msg.rs index 058fb4291..0cfbf3e16 100644 --- a/contracts/nexus-gateway/src/msg.rs +++ b/contracts/nexus-gateway/src/msg.rs @@ -1,4 +1,7 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::HexBinary; +use msgs_derive::EnsurePermissions; +use router_api::{Address, ChainName}; use crate::nexus; @@ -6,11 +9,25 @@ use crate::nexus; pub struct InstantiateMsg { pub nexus: String, pub router: String, + pub axelar_gateway: String, } #[cw_serde] +#[derive(EnsurePermissions)] pub enum ExecuteMsg { + /// Initiate a cross-chain contract call with token from Axelarnet to another chain. + /// Note: This only works when the destination chain is a legacy chain. + #[permission(Any)] + CallContractWithToken { + destination_chain: ChainName, + destination_address: Address, + payload: HexBinary, + }, + /// Route a cross-chain message from Axelarnet to another chain. + /// Note: This only works when the destination chain is a legacy chain. + #[permission(Specific(router))] RouteMessages(Vec), + #[permission(Specific(nexus))] RouteMessagesFromNexus(Vec), } diff --git a/contracts/nexus-gateway/src/nexus.rs b/contracts/nexus-gateway/src/nexus.rs index 7e3f751d2..9e6e227a0 100644 --- a/contracts/nexus-gateway/src/nexus.rs +++ b/contracts/nexus-gateway/src/nexus.rs @@ -1,10 +1,10 @@ use std::str::FromStr; -use axelar_wasm_std::{msg_id::tx_hash_event_index::HexTxHashAndEventIndex, nonempty}; -use cosmwasm_std::{CosmosMsg, CustomMsg}; +use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; +use axelar_wasm_std::nonempty; +use cosmwasm_std::{Coin, CosmosMsg, CustomMsg}; use error_stack::{Report, Result, ResultExt}; -use hex::ToHex; -use router_api::{Address, ChainName, CrossChainId}; +use router_api::{Address, ChainName, ChainNameRaw, CrossChainId}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -14,13 +14,15 @@ use crate::error::ContractError; // this matches the message type defined in the nexus module // https://github.com/axelarnetwork/axelar-core/blob/6c887df3797ba660093061662aff04e325b9c429/x/nexus/exported/types.pb.go#L405 pub struct Message { - pub source_chain: ChainName, + pub source_chain: ChainNameRaw, pub source_address: Address, pub destination_chain: ChainName, pub destination_address: Address, pub payload_hash: [u8; 32], pub source_tx_id: nonempty::Vec, pub source_tx_index: u64, + pub id: String, + pub token: Option, } impl CustomMsg for Message {} @@ -38,18 +40,20 @@ fn parse_message_id(message_id: &str) -> Result<(nonempty::Vec, u64), Contra impl From for Message { fn from(msg: router_api::Message) -> Self { - // fallback to using the message ID as the tx ID if it's not in the expected format + // fallback to using all 0's as the tx ID if it's not in the expected format let (source_tx_id, source_tx_index) = - parse_message_id(&msg.cc_id.id).unwrap_or((msg.cc_id.id.into(), u64::MAX)); + parse_message_id(&msg.cc_id.message_id).unwrap_or((vec![0; 32].try_into().unwrap(), 0)); Self { - source_chain: msg.cc_id.chain, + source_chain: msg.cc_id.source_chain, source_address: msg.source_address, destination_chain: msg.destination_chain, destination_address: msg.destination_address, payload_hash: msg.payload_hash, source_tx_id, source_tx_index, + id: msg.cc_id.message_id.into(), + token: None, } } } @@ -58,19 +62,11 @@ impl TryFrom for router_api::Message { type Error = Report; fn try_from(msg: Message) -> Result { - let msg_id = HexTxHashAndEventIndex { - tx_hash: <[u8; 32]>::try_from(msg.source_tx_id.as_ref().as_slice()).map_err(|_| { - ContractError::InvalidSourceTxId(msg.source_tx_id.as_ref().encode_hex::()) - })?, - event_index: u32::try_from(msg.source_tx_index) - .map_err(|_| ContractError::InvalidEventIndex(msg.source_tx_index))?, - }; - Ok(Self { cc_id: CrossChainId { - chain: msg.source_chain, - id: nonempty::String::try_from(msg_id.to_string()) - .change_context(ContractError::InvalidMessageId(msg_id.to_string()))?, + source_chain: msg.source_chain, + message_id: nonempty::String::try_from(msg.id.as_str()) + .change_context(ContractError::InvalidMessageId(msg.id))?, }, source_address: msg.source_address, destination_chain: msg.destination_chain, @@ -90,33 +86,105 @@ impl From for CosmosMsg { mod test { use std::vec; - use cosmwasm_std::HexBinary; + use axelar_wasm_std::msg_id::{Base58TxDigestAndEventIndex, HexTxHashAndEventIndex}; + use router_api::CrossChainId; use super::Message; #[test] fn should_convert_nexus_message_to_router_message() { + let msg_id = HexTxHashAndEventIndex { + tx_hash: vec![2; 32].try_into().unwrap(), + event_index: 1, + }; let msg = Message { source_chain: "ethereum".parse().unwrap(), source_address: "something".parse().unwrap(), destination_chain: "polygon".parse().unwrap(), destination_address: "something else".parse().unwrap(), payload_hash: [1; 32], - source_tx_id: vec![2; 32].try_into().unwrap(), - source_tx_index: 1, + source_tx_id: msg_id.tx_hash.to_vec().try_into().unwrap(), + source_tx_index: msg_id.event_index as u64, + id: msg_id.to_string(), + token: None, }; let router_msg = router_api::Message::try_from(msg.clone()); assert!(router_msg.is_ok()); let router_msg = router_msg.unwrap(); - assert_eq!(router_msg.cc_id.chain, msg.source_chain); + let router_msg_cc_id = router_msg.cc_id; + assert_eq!(router_msg_cc_id.source_chain, msg.source_chain); + assert_eq!(router_msg_cc_id.message_id.to_string(), msg.id); + } + + #[test] + fn should_convert_router_message_to_nexus_message() { + let msg = router_api::Message { + cc_id: CrossChainId::new( + "ethereum", + HexTxHashAndEventIndex { + tx_hash: [2; 32], + event_index: 1, + } + .to_string() + .as_str(), + ) + .unwrap(), + source_address: "something".parse().unwrap(), + destination_chain: "polygon".parse().unwrap(), + destination_address: "something else".parse().unwrap(), + payload_hash: [1; 32], + }; + + let nexus_msg = Message::from(msg.clone()); + + let router_msg_cc_id = msg.cc_id; + + assert_eq!(nexus_msg.id, *router_msg_cc_id.message_id); + assert_eq!(nexus_msg.destination_address, msg.destination_address); + assert_eq!(nexus_msg.destination_chain, msg.destination_chain); + assert_eq!(nexus_msg.source_address, msg.source_address); assert_eq!( - router_msg.cc_id.id.to_string(), - format!( - "0x{}-{}", - HexBinary::from(msg.source_tx_id.as_ref().clone()).to_hex(), - msg.source_tx_index + nexus_msg.source_chain, + router_msg_cc_id.source_chain.clone() + ); + assert_eq!(nexus_msg.source_tx_id, vec![2; 32].try_into().unwrap()); + assert_eq!(nexus_msg.source_tx_index, 1); + } + + #[test] + fn should_convert_router_message_with_non_hex_msg_id_to_nexus_message() { + let msg = router_api::Message { + cc_id: CrossChainId::new( + "ethereum", + Base58TxDigestAndEventIndex { + tx_digest: [2; 32], + event_index: 1, + } + .to_string() + .as_str(), ) + .unwrap(), + source_address: "something".parse().unwrap(), + destination_chain: "polygon".parse().unwrap(), + destination_address: "something else".parse().unwrap(), + payload_hash: [1; 32], + }; + + let nexus_msg = Message::from(msg.clone()); + + let router_msg_cc_id = msg.cc_id; + + assert_eq!(nexus_msg.id, *router_msg_cc_id.message_id); + assert_eq!(nexus_msg.source_tx_id, vec![0; 32].try_into().unwrap()); + assert_eq!(nexus_msg.source_tx_index, 0); + + assert_eq!(nexus_msg.destination_address, msg.destination_address); + assert_eq!(nexus_msg.destination_chain, msg.destination_chain); + assert_eq!(nexus_msg.source_address, msg.source_address); + assert_eq!( + nexus_msg.source_chain, + router_msg_cc_id.source_chain.clone() ); } } diff --git a/contracts/nexus-gateway/src/state.rs b/contracts/nexus-gateway/src/state.rs index 1936b770d..8efd48a2c 100644 --- a/contracts/nexus-gateway/src/state.rs +++ b/contracts/nexus-gateway/src/state.rs @@ -2,63 +2,43 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, Storage}; use cw_storage_plus::{Item, Map}; use error_stack::{self, ResultExt}; -use mockall::automock; use router_api::CrossChainId; use crate::error::ContractError; const CONFIG: Item = Item::new("config"); -const ROUTED_MESSAGE_IDS: Map = Map::new("routed_message_ids"); +const ROUTED_MESSAGE_IDS: Map<&CrossChainId, ()> = Map::new("routed_message_ids"); type Result = error_stack::Result; -#[automock] -pub trait Store { - fn save_config(&mut self, config: Config) -> Result<()>; - fn load_config(&self) -> Result; - fn set_message_routed(&mut self, id: &CrossChainId) -> Result<()>; - fn is_message_routed(&self, id: &CrossChainId) -> Result; +pub fn save_config(storage: &mut dyn Storage, config: Config) -> Result<()> { + CONFIG + .save(storage, &config) + .change_context(ContractError::StoreFailure) } -pub struct GatewayStore<'a> { - storage: &'a mut dyn Storage, +pub fn load_config(storage: &dyn Storage) -> Result { + CONFIG + .load(storage) + .change_context(ContractError::StoreFailure) } -impl<'a> GatewayStore<'a> { - pub fn new(storage: &'a mut dyn Storage) -> Self { - Self { storage } - } +pub fn set_message_routed(storage: &mut dyn Storage, id: &CrossChainId) -> Result<()> { + ROUTED_MESSAGE_IDS + .save(storage, id, &()) + .change_context(ContractError::StoreFailure) } -impl Store for GatewayStore<'_> { - fn save_config(&mut self, config: Config) -> Result<()> { - CONFIG - .save(self.storage, &config) - .change_context(ContractError::StoreFailure) - } - - fn load_config(&self) -> Result { - CONFIG - .load(self.storage) - .change_context(ContractError::StoreFailure) - } - - fn set_message_routed(&mut self, id: &CrossChainId) -> Result<()> { - ROUTED_MESSAGE_IDS - .save(self.storage, id.clone(), &()) - .change_context(ContractError::StoreFailure) - } - - fn is_message_routed(&self, id: &CrossChainId) -> Result { - ROUTED_MESSAGE_IDS - .may_load(self.storage, id.clone()) - .map(|result| result.is_some()) - .change_context(ContractError::StoreFailure) - } +pub fn is_message_routed(storage: &dyn Storage, id: &CrossChainId) -> Result { + ROUTED_MESSAGE_IDS + .may_load(storage, id) + .map(|result| result.is_some()) + .change_context(ContractError::StoreFailure) } #[cw_serde] pub struct Config { pub nexus: Addr, pub router: Addr, + pub axelar_gateway: Addr, } diff --git a/contracts/rewards/.cargo/config.toml b/contracts/rewards/.cargo/config.toml new file mode 100644 index 000000000..af5698e58 --- /dev/null +++ b/contracts/rewards/.cargo/config.toml @@ -0,0 +1,4 @@ +[alias] +wasm = "build --release --lib --target wasm32-unknown-unknown" +unit-test = "test --lib" +schema = "run --bin schema" diff --git a/contracts/rewards/Cargo.toml b/contracts/rewards/Cargo.toml index c1911fd77..29d0dd41b 100644 --- a/contracts/rewards/Cargo.toml +++ b/contracts/rewards/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "rewards" -version = "0.3.0" +version = "1.0.0" rust-version = { workspace = true } -edition = "2021" +edition = { workspace = true } description = "Validator rewards contract" exclude = [ @@ -29,20 +29,21 @@ library = [] optimize = """docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/rust-optimizer:0.12.6 + cosmwasm/optimizer:0.16.0 """ [dependencies] -axelar-wasm-std = { workspace = true } -axelar-wasm-std-derive = { workspace = true } +axelar-wasm-std = { workspace = true, features = ["derive"] } cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } cw-storage-plus = { workspace = true } cw2 = { workspace = true } error-stack = { workspace = true } itertools = "0.11.0" +msgs-derive = { workspace = true } report = { workspace = true } router-api = { workspace = true } +serde_json = { workspace = true } thiserror = { workspace = true } [dev-dependencies] diff --git a/contracts/rewards/src/bin/schema.rs b/contracts/rewards/src/bin/schema.rs index e0a5ec117..c7b7ba02e 100644 --- a/contracts/rewards/src/bin/schema.rs +++ b/contracts/rewards/src/bin/schema.rs @@ -1,5 +1,4 @@ use cosmwasm_schema::write_api; - use rewards::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; fn main() { diff --git a/contracts/rewards/src/contract.rs b/contracts/rewards/src/contract.rs index d1ef6ede3..cca76834f 100644 --- a/contracts/rewards/src/contract.rs +++ b/contracts/rewards/src/contract.rs @@ -1,4 +1,4 @@ -use axelar_wasm_std::nonempty; +use axelar_wasm_std::{address, nonempty, permission_control}; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ @@ -7,13 +7,13 @@ use cosmwasm_std::{ use error_stack::ResultExt; use itertools::Itertools; -use crate::{ - error::ContractError, - msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, - state::{self, Config, Epoch, ParamsSnapshot, PoolId, CONFIG, PARAMS}, -}; +use crate::error::ContractError; +use crate::events; +use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use crate::state::{self, Config, PoolId, CONFIG}; mod execute; +mod migrations; mod query; const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); @@ -24,7 +24,9 @@ pub fn migrate( deps: DepsMut, _env: Env, _msg: Empty, -) -> Result { +) -> Result { + migrations::v1_0_0::migrate(deps.storage)?; + // any version checks should be done before here cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; @@ -35,33 +37,22 @@ pub fn migrate( #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( deps: DepsMut, - env: Env, + _env: Env, _info: MessageInfo, msg: InstantiateMsg, -) -> Result { +) -> Result { cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - let governance = deps.api.addr_validate(&msg.governance_address)?; + let governance = address::validate_cosmwasm_address(deps.api, &msg.governance_address)?; + permission_control::set_governance(deps.storage, &governance)?; CONFIG.save( deps.storage, &Config { - governance, rewards_denom: msg.rewards_denom, }, )?; - PARAMS.save( - deps.storage, - &ParamsSnapshot { - params: msg.params, - created_at: Epoch { - epoch_num: 0, - block_height_started: env.block.height, - }, - }, - )?; - Ok(Response::new()) } @@ -71,17 +62,17 @@ pub fn execute( env: Env, info: MessageInfo, msg: ExecuteMsg, -) -> Result { - match msg { +) -> Result { + match msg.ensure_permissions(deps.storage, &info.sender)? { ExecuteMsg::RecordParticipation { chain_name, event_id, verifier_address, } => { - let verifier_address = deps.api.addr_validate(&verifier_address)?; + let verifier_address = address::validate_cosmwasm_address(deps.api, &verifier_address)?; let pool_id = PoolId { chain_name, - contract: info.sender.clone(), + contract: info.sender, }; execute::record_participation( deps.storage, @@ -89,13 +80,12 @@ pub fn execute( verifier_address, pool_id, env.block.height, - ) - .map_err(axelar_wasm_std::ContractError::from)?; + )?; Ok(Response::new()) } ExecuteMsg::AddRewards { pool_id } => { - deps.api.addr_validate(pool_id.contract.as_str())?; + address::validate_cosmwasm_address(deps.api, pool_id.contract.as_str())?; let amount = info .funds @@ -117,13 +107,18 @@ pub fn execute( pool_id, epoch_count, } => { - deps.api.addr_validate(pool_id.contract.as_str())?; + address::validate_cosmwasm_address(deps.api, pool_id.contract.as_str())?; - let rewards = - execute::distribute_rewards(deps.storage, pool_id, env.block.height, epoch_count) - .map_err(axelar_wasm_std::ContractError::from)?; + let rewards_distribution = execute::distribute_rewards( + deps.storage, + pool_id.clone(), + env.block.height, + epoch_count, + )?; - let msgs = rewards + let msgs = rewards_distribution + .rewards + .clone() .into_iter() .sorted() .map(|(addr, amount)| BankMsg::Send { @@ -134,13 +129,19 @@ pub fn execute( }], }); - Ok(Response::new().add_messages(msgs)) + Ok(Response::new() + .add_messages(msgs) + .add_event(events::Event::from(rewards_distribution).into())) } - ExecuteMsg::UpdateParams { params } => { - execute::update_params(deps.storage, params, env.block.height, info.sender)?; + ExecuteMsg::UpdatePoolParams { params, pool_id } => { + execute::update_pool_params(deps.storage, &pool_id, params, env.block.height)?; Ok(Response::new()) } + ExecuteMsg::CreatePool { params, pool_id } => { + execute::create_pool(deps.storage, params, env.block.height, &pool_id)?; + Ok(Response::new()) + } } } @@ -149,13 +150,19 @@ pub fn query( deps: Deps, env: Env, msg: QueryMsg, -) -> Result { +) -> Result { match msg { QueryMsg::RewardsPool { pool_id } => { let pool = query::rewards_pool(deps.storage, pool_id, env.block.height)?; to_json_binary(&pool) .change_context(ContractError::SerializeResponse) - .map_err(axelar_wasm_std::ContractError::from) + .map_err(axelar_wasm_std::error::ContractError::from) + } + QueryMsg::VerifierParticipation { pool_id, epoch_num } => { + let tally = query::participation(deps.storage, pool_id, epoch_num, env.block.height)?; + to_json_binary(&tally) + .change_context(ContractError::SerializeResponse) + .map_err(axelar_wasm_std::error::ContractError::from) } } } @@ -167,19 +174,21 @@ mod tests { use cw_multi_test::{App, ContractWrapper, Executor}; use router_api::ChainName; + use super::*; use crate::msg::{ExecuteMsg, InstantiateMsg, Params, QueryMsg, RewardsPool}; use crate::state::PoolId; - use super::*; - #[test] fn migrate_sets_contract_version() { let mut deps = mock_dependencies(); + #[allow(deprecated)] + migrations::v1_0_0::tests::instantiate_contract(deps.as_mut(), "denom"); + migrate(deps.as_mut(), mock_env(), Empty {}).unwrap(); let contract_version = cw2::get_contract_version(deps.as_mut().storage).unwrap(); - assert_eq!(contract_version.contract, "rewards"); + assert_eq!(contract_version.contract, CONTRACT_NAME); assert_eq!(contract_version.version, CONTRACT_VERSION); } @@ -217,7 +226,6 @@ mod tests { &InstantiateMsg { governance_address: governance_address.to_string(), rewards_denom: AXL_DENOMINATION.to_string(), - params: initial_params.clone(), }, &[], "Contract", @@ -230,6 +238,17 @@ mod tests { contract: pool_contract.clone(), }; + let res = app.execute_contract( + governance_address.clone(), + contract_address.clone(), + &ExecuteMsg::CreatePool { + params: initial_params.clone(), + pool_id: pool_id.clone(), + }, + &[], + ); + assert!(res.is_ok()); + let rewards = 200; let res = app.execute_contract( user.clone(), @@ -248,8 +267,9 @@ mod tests { let res = app.execute_contract( governance_address, contract_address.clone(), - &ExecuteMsg::UpdateParams { + &ExecuteMsg::UpdatePoolParams { params: updated_params.clone(), + pool_id: pool_id.clone(), }, &[], ); @@ -260,7 +280,7 @@ mod tests { contract_address.clone(), &ExecuteMsg::RecordParticipation { chain_name: chain_name.clone(), - event_id: "some event".to_string().try_into().unwrap(), + event_id: "some event".try_into().unwrap(), verifier_address: verifier.to_string(), }, &[], @@ -272,7 +292,7 @@ mod tests { contract_address.clone(), &ExecuteMsg::RecordParticipation { chain_name: chain_name.clone(), - event_id: "some other event".to_string().try_into().unwrap(), + event_id: "some other event".try_into().unwrap(), verifier_address: verifier.to_string(), }, &[], diff --git a/contracts/rewards/src/contract/execute.rs b/contracts/rewards/src/contract/execute.rs index 49a8dff1e..382afdeba 100644 --- a/contracts/rewards/src/contract/execute.rs +++ b/contracts/rewards/src/contract/execute.rs @@ -2,32 +2,26 @@ use std::collections::HashMap; use axelar_wasm_std::{nonempty, FnExt}; use cosmwasm_std::{Addr, OverflowError, OverflowOperation, Storage, Uint128}; -use error_stack::{Report, Result}; +use error_stack::{ensure, Report, Result}; -use crate::{ - error::ContractError, - msg::Params, - state::{self, Config, Epoch, EpochTally, Event, ParamsSnapshot, PoolId, StorageState}, +use crate::error::ContractError; +use crate::msg::Params; +use crate::state::{ + self, Epoch, EpochTally, Event, ParamsSnapshot, PoolId, RewardsDistribution, RewardsPool, + StorageState, }; const DEFAULT_EPOCHS_TO_PROCESS: u64 = 10; const EPOCH_PAYOUT_DELAY: u64 = 2; -fn require_governance(config: Config, sender: Addr) -> Result<(), ContractError> { - if config.governance != sender { - return Err(ContractError::Unauthorized.into()); - } - Ok(()) -} - -pub(crate) fn record_participation( +pub fn record_participation( storage: &mut dyn Storage, event_id: nonempty::String, verifier: Addr, pool_id: PoolId, block_height: u64, ) -> Result<(), ContractError> { - let current_params = state::load_params(storage); + let current_params = state::load_rewards_pool_params(storage, pool_id.clone())?; let cur_epoch = Epoch::current(¤t_params, block_height)?; let event = load_or_store_event(storage, event_id, pool_id.clone(), cur_epoch.epoch_num)?; @@ -61,20 +55,20 @@ fn load_or_store_event( } } -pub(crate) fn distribute_rewards( +pub fn distribute_rewards( storage: &mut dyn Storage, pool_id: PoolId, cur_block_height: u64, epoch_process_limit: Option, -) -> Result, ContractError> { +) -> Result { let epoch_process_limit = epoch_process_limit.unwrap_or(DEFAULT_EPOCHS_TO_PROCESS); - let cur_epoch = Epoch::current(&state::load_params(storage), cur_block_height)?; + let cur_epoch = state::current_epoch(storage, &pool_id, cur_block_height)?; let from = state::load_rewards_watermark(storage, pool_id.clone())? .map_or(0, |last_processed| last_processed.saturating_add(1)); let to = std::cmp::min( - (from.saturating_add(epoch_process_limit)).saturating_sub(1), // for process limit =1 "from" and "to" must be equal + from.saturating_add(epoch_process_limit).saturating_sub(1), // for process limit =1 "from" and "to" must be equal cur_epoch.epoch_num.saturating_sub(EPOCH_PAYOUT_DELAY), ); @@ -84,7 +78,12 @@ pub(crate) fn distribute_rewards( let rewards = process_rewards_for_epochs(storage, pool_id.clone(), from, to)?; state::save_rewards_watermark(storage, pool_id, to)?; - Ok(rewards) + Ok(RewardsDistribution { + rewards, + epochs_processed: (from..=to).collect(), + current_epoch: cur_epoch.clone(), + can_distribute_more: to < cur_epoch.epoch_num.saturating_sub(EPOCH_PAYOUT_DELAY), + }) } fn process_rewards_for_epochs( @@ -94,7 +93,7 @@ fn process_rewards_for_epochs( to: u64, ) -> Result, ContractError> { let rewards = cumulate_rewards(storage, &pool_id, from, to)?; - state::load_rewards_pool_or_new(storage, pool_id.clone())? + state::load_rewards_pool(storage, pool_id.clone())? .sub_reward(rewards.values().sum())? .then(|pool| state::save_rewards_pool(storage, &pool))?; @@ -123,14 +122,43 @@ fn iterate_epoch_tallies<'a>( }) } -pub(crate) fn update_params( +pub fn create_pool( + storage: &mut dyn Storage, + params: Params, + block_height: u64, + pool_id: &PoolId, +) -> Result<(), ContractError> { + ensure!( + !state::pool_exists(storage, pool_id)?, + ContractError::RewardsPoolAlreadyExists + ); + + let cur_epoch = Epoch { + epoch_num: 0, + block_height_started: block_height, + }; + + let params_snapshot = ParamsSnapshot { + params, + created_at: cur_epoch, + }; + + let pool = RewardsPool { + id: pool_id.clone(), + balance: Uint128::zero(), + params: params_snapshot, + }; + + state::save_rewards_pool(storage, &pool) +} + +pub fn update_pool_params( storage: &mut dyn Storage, + pool_id: &PoolId, new_params: Params, block_height: u64, - sender: Addr, ) -> Result<(), ContractError> { - require_governance(state::load_config(storage), sender)?; - let cur_epoch = Epoch::current(&state::load_params(storage), block_height)?; + let cur_epoch = state::current_epoch(storage, pool_id, block_height)?; // If the param update reduces the epoch duration such that the current epoch immediately ends, // start a new epoch at this block, incrementing the current epoch number by 1. // This prevents us from jumping forward an arbitrary number of epochs, and maintains consistency for past events. @@ -160,22 +188,22 @@ pub(crate) fn update_params( } else { cur_epoch }; - state::save_params( - storage, - &ParamsSnapshot { - params: new_params, - created_at: cur_epoch, - }, - )?; + let new_params_snapshot = ParamsSnapshot { + params: new_params, + created_at: cur_epoch, + }; + + state::update_pool_params(storage, pool_id, &new_params_snapshot)?; + Ok(()) } -pub(crate) fn add_rewards( +pub fn add_rewards( storage: &mut dyn Storage, pool_id: PoolId, amount: nonempty::Uint128, ) -> Result<(), ContractError> { - let mut pool = state::load_rewards_pool_or_new(storage, pool_id)?; + let mut pool = state::load_rewards_pool(storage, pool_id)?; pool.balance = pool .balance .checked_add(Uint128::from(amount)) @@ -214,24 +242,17 @@ fn merge_rewards( #[cfg(test)] mod test { - use super::*; - use std::collections::HashMap; use axelar_wasm_std::nonempty; - use cosmwasm_std::{ - testing::{mock_dependencies, MockApi, MockQuerier, MockStorage}, - Addr, OwnedDeps, Uint128, Uint64, - }; + use cosmwasm_std::testing::{mock_dependencies, MockApi, MockQuerier, MockStorage}; + use cosmwasm_std::{Addr, OwnedDeps, Uint128, Uint64}; use router_api::ChainName; - use crate::{ - error::ContractError, - msg::Params, - state::{ - self, Config, Epoch, EpochTally, Event, ParamsSnapshot, PoolId, RewardsPool, CONFIG, - }, - }; + use super::*; + use crate::error::ContractError; + use crate::msg::Params; + use crate::state::{self, Config, Epoch, ParamsSnapshot, PoolId, CONFIG}; /// Tests that the current epoch is computed correctly when the expected epoch is the same as the stored epoch #[test] @@ -239,8 +260,19 @@ mod test { let cur_epoch_num = 1u64; let block_height_started = 250u64; let epoch_duration = 100u64; - let (mock_deps, _) = setup(cur_epoch_num, block_height_started, epoch_duration); - let current_params = state::load_params(mock_deps.as_ref().storage); + let pool_id = PoolId { + chain_name: "mock-chain".parse().unwrap(), + contract: Addr::unchecked("some contract"), + }; + let mock_deps = setup( + cur_epoch_num, + block_height_started, + epoch_duration, + pool_id.clone(), + ); + let current_params = state::load_rewards_pool(mock_deps.as_ref().storage, pool_id) + .unwrap() + .params; let new_epoch = Epoch::current(¤t_params, block_height_started).unwrap(); assert_eq!(new_epoch.epoch_num, cur_epoch_num); @@ -263,8 +295,19 @@ mod test { let cur_epoch_num = 1u64; let block_height_started = 250u64; let epoch_duration = 100u64; - let (mock_deps, _) = setup(cur_epoch_num, block_height_started, epoch_duration); - let current_params = state::load_params(mock_deps.as_ref().storage); + let pool_id = PoolId { + chain_name: "mock-chain".parse().unwrap(), + contract: Addr::unchecked("some contract"), + }; + let mock_deps = setup( + cur_epoch_num, + block_height_started, + epoch_duration, + pool_id.clone(), + ); + let current_params = state::load_rewards_pool(mock_deps.as_ref().storage, pool_id.clone()) + .unwrap() + .params; assert!(Epoch::current(¤t_params, block_height_started - 1).is_err()); assert!(Epoch::current(¤t_params, block_height_started - epoch_duration).is_err()); @@ -276,7 +319,16 @@ mod test { let cur_epoch_num = 1u64; let block_height_started = 250u64; let epoch_duration = 100u64; - let (mock_deps, _) = setup(cur_epoch_num, block_height_started, epoch_duration); + let pool_id = PoolId { + chain_name: "mock-chain".parse().unwrap(), + contract: Addr::unchecked("some contract"), + }; + let mock_deps = setup( + cur_epoch_num, + block_height_started, + epoch_duration, + pool_id.clone(), + ); // elements are (height, expected epoch number, expected epoch start) let test_cases = vec![ @@ -303,8 +355,13 @@ mod test { ]; for (height, expected_epoch_num, expected_block_start) in test_cases { - let new_epoch = - Epoch::current(&state::load_params(mock_deps.as_ref().storage), height).unwrap(); + let new_epoch = Epoch::current( + &state::load_rewards_pool(mock_deps.as_ref().storage, pool_id.clone()) + .unwrap() + .params, + height, + ) + .unwrap(); assert_eq!(new_epoch.epoch_num, expected_epoch_num); assert_eq!(new_epoch.block_height_started, expected_block_start); @@ -318,12 +375,16 @@ mod test { let epoch_block_start = 250u64; let epoch_duration = 100u64; - let (mut mock_deps, _) = setup(cur_epoch_num, epoch_block_start, epoch_duration); - let pool_id = PoolId { chain_name: "mock-chain".parse().unwrap(), contract: Addr::unchecked("some contract"), }; + let mut mock_deps = setup( + cur_epoch_num, + epoch_block_start, + epoch_duration, + pool_id.clone(), + ); let mut simulated_participation = HashMap::new(); simulated_participation.insert(Addr::unchecked("verifier_1"), 10); @@ -372,12 +433,16 @@ mod test { let block_height_started = 250u64; let epoch_duration = 100u64; - let (mut mock_deps, _) = setup(starting_epoch_num, block_height_started, epoch_duration); - let pool_id = PoolId { chain_name: "mock-chain".parse().unwrap(), contract: Addr::unchecked("some contract"), }; + let mut mock_deps = setup( + starting_epoch_num, + block_height_started, + epoch_duration, + pool_id.clone(), + ); let verifiers = vec![ Addr::unchecked("verifier_1"), @@ -390,7 +455,7 @@ mod test { for (i, verifiers) in verifiers.iter().enumerate() { record_participation( mock_deps.as_mut().storage, - "some event".to_string().try_into().unwrap(), + "some event".try_into().unwrap(), verifiers.clone(), pool_id.clone(), height_at_epoch_end + i as u64, @@ -399,7 +464,9 @@ mod test { } let cur_epoch = Epoch::current( - &state::load_params(mock_deps.as_ref().storage), + &state::load_rewards_pool(mock_deps.as_ref().storage, pool_id.clone()) + .unwrap() + .params, height_at_epoch_end, ) .unwrap(); @@ -432,9 +499,6 @@ mod test { fn record_participation_multiple_contracts() { let cur_epoch_num = 1u64; let block_height_started = 250u64; - let epoch_duration = 100u64; - - let (mut mock_deps, _) = setup(cur_epoch_num, block_height_started, epoch_duration); let mut simulated_participation = HashMap::new(); simulated_participation.insert( @@ -468,6 +532,20 @@ mod test { ), ); + let params = Params { + participation_threshold: (1, 2).try_into().unwrap(), + epoch_duration: 100u64.try_into().unwrap(), + rewards_per_epoch: 100u128.try_into().unwrap(), + }; + let mut mock_deps = setup_multiple_pools_with_params( + cur_epoch_num, + block_height_started, + simulated_participation + .iter() + .map(|(_, (pool_id, _))| (pool_id.clone(), params.clone())) + .collect(), + ); + for (verifier, (pool_contract, events_participated)) in &simulated_participation { for i in 0..*events_participated { let event_id = i.to_string().try_into().unwrap(); @@ -500,6 +578,7 @@ mod test { ); } } + /// Test that rewards parameters are updated correctly. In this test we don't change the epoch duration, so /// that computation of the current epoch is unaffected. #[test] @@ -509,12 +588,17 @@ mod test { let initial_rewards_per_epoch = 100u128; let initial_participation_threshold = (1, 2); let epoch_duration = 100u64; - let (mut mock_deps, config) = setup_with_params( + let pool_id = PoolId { + chain_name: "mock-chain".parse().unwrap(), + contract: Addr::unchecked("some contract"), + }; + let mut mock_deps = setup_with_params( initial_epoch_num, initial_epoch_start, epoch_duration, initial_rewards_per_epoch, initial_participation_threshold, + pool_id.clone(), ); // simulate the below tests running at this block height @@ -529,22 +613,34 @@ mod test { }; // the epoch shouldn't change when the params are updated, since we are not changing the epoch duration - let expected_epoch = - Epoch::current(&state::load_params(mock_deps.as_ref().storage), cur_height).unwrap(); + let expected_epoch = Epoch::current( + &state::load_rewards_pool(mock_deps.as_ref().storage, pool_id.clone()) + .unwrap() + .params, + cur_height, + ) + .unwrap(); - update_params( + update_pool_params( mock_deps.as_mut().storage, + &pool_id, new_params.clone(), cur_height, - config.governance.clone(), ) .unwrap(); - let stored = state::load_params(mock_deps.as_ref().storage); + let stored = state::load_rewards_pool(mock_deps.as_ref().storage, pool_id.clone()) + .unwrap() + .params; assert_eq!(stored.params, new_params); // current epoch shouldn't have changed - let cur_epoch = - Epoch::current(&state::load_params(mock_deps.as_ref().storage), cur_height).unwrap(); + let cur_epoch = Epoch::current( + &state::load_rewards_pool(mock_deps.as_ref().storage, pool_id) + .unwrap() + .params, + cur_height, + ) + .unwrap(); assert_eq!(expected_epoch.epoch_num, cur_epoch.epoch_num); assert_eq!( expected_epoch.block_height_started, @@ -555,43 +651,21 @@ mod test { assert_eq!(stored.created_at, cur_epoch); } - /// Test that rewards parameters cannot be updated by an address other than governance - #[test] - fn update_params_unauthorized() { - let initial_epoch_num = 1u64; - let initial_epoch_start = 250u64; - let epoch_duration = 100u64; - let (mut mock_deps, _) = setup(initial_epoch_num, initial_epoch_start, epoch_duration); - - let new_params = Params { - rewards_per_epoch: cosmwasm_std::Uint128::from(100u128).try_into().unwrap(), - participation_threshold: (Uint64::new(2), Uint64::new(3)).try_into().unwrap(), - epoch_duration: epoch_duration.try_into().unwrap(), - }; - - let res = update_params( - mock_deps.as_mut().storage, - new_params.clone(), - initial_epoch_start, - Addr::unchecked("some non governance address"), - ); - assert!(res.is_err()); - assert_eq!( - res.unwrap_err().current_context(), - &ContractError::Unauthorized - ); - } - /// Test extending the epoch duration. This should not change the current epoch #[test] fn extend_epoch_duration() { let initial_epoch_num = 1u64; let initial_epoch_start = 250u64; let initial_epoch_duration = 100u64; - let (mut mock_deps, config) = setup( + let pool_id = PoolId { + chain_name: "mock-chain".parse().unwrap(), + contract: Addr::unchecked("some contract"), + }; + let mut mock_deps = setup( initial_epoch_num, initial_epoch_start, initial_epoch_duration, + pool_id.clone(), ); // simulate the tests running after 5 epochs have passed @@ -599,24 +673,29 @@ mod test { let cur_height = initial_epoch_start + initial_epoch_duration * epochs_elapsed + 10; // add 10 here just to be a little past the epoch boundary // epoch shouldn't change if we are extending the duration - let initial_params_snapshot = state::load_params(mock_deps.as_ref().storage); + let initial_params_snapshot = + state::load_rewards_pool(mock_deps.as_ref().storage, pool_id.clone()) + .unwrap() + .params; let epoch_prior_to_update = Epoch::current(&initial_params_snapshot, cur_height).unwrap(); let new_epoch_duration = initial_epoch_duration * 2; let new_params = Params { - epoch_duration: (new_epoch_duration).try_into().unwrap(), + epoch_duration: new_epoch_duration.try_into().unwrap(), ..initial_params_snapshot.params // keep everything besides epoch duration the same }; - update_params( + update_pool_params( mock_deps.as_mut().storage, + &pool_id.clone(), new_params.clone(), cur_height, - config.governance.clone(), ) .unwrap(); - let updated_params_snapshot = state::load_params(mock_deps.as_ref().storage); + let updated_params_snapshot = state::load_rewards_pool(mock_deps.as_ref().storage, pool_id) + .unwrap() + .params; // current epoch shouldn't change let epoch = Epoch::current(&updated_params_snapshot, cur_height).unwrap(); @@ -647,10 +726,15 @@ mod test { let initial_epoch_num = 1u64; let initial_epoch_start = 256u64; let initial_epoch_duration = 100u64; - let (mut mock_deps, config) = setup( + let pool_id = PoolId { + chain_name: "mock-chain".parse().unwrap(), + contract: Addr::unchecked("some contract"), + }; + let mut mock_deps = setup( initial_epoch_num, initial_epoch_start, initial_epoch_duration, + pool_id.clone(), ); // simulate the tests running after 10 epochs have passed @@ -659,7 +743,10 @@ mod test { let new_epoch_duration = initial_epoch_duration / 2; - let initial_params_snapshot = state::load_params(mock_deps.as_ref().storage); + let initial_params_snapshot = + state::load_rewards_pool(mock_deps.as_ref().storage, pool_id.clone()) + .unwrap() + .params; let epoch_prior_to_update = Epoch::current(&initial_params_snapshot, cur_height).unwrap(); // we are shortening the epoch, but not so much it causes the epoch number to change. We want to remain in the same epoch assert!(cur_height - epoch_prior_to_update.block_height_started < new_epoch_duration); @@ -668,15 +755,18 @@ mod test { epoch_duration: new_epoch_duration.try_into().unwrap(), ..initial_params_snapshot.params }; - update_params( + update_pool_params( mock_deps.as_mut().storage, + &pool_id, new_params.clone(), cur_height, - config.governance.clone(), ) .unwrap(); - let updated_params_snapshot = state::load_params(mock_deps.as_ref().storage); + let updated_params_snapshot = + state::load_rewards_pool(mock_deps.as_ref().storage, pool_id.clone()) + .unwrap() + .params; // current epoch shouldn't have changed let epoch = Epoch::current(&updated_params_snapshot, cur_height).unwrap(); @@ -699,10 +789,15 @@ mod test { let initial_epoch_num = 1u64; let initial_epoch_start = 250u64; let initial_epoch_duration = 100u64; - let (mut mock_deps, config) = setup( + let pool_id = PoolId { + chain_name: "mock-chain".parse().unwrap(), + contract: Addr::unchecked("some contract"), + }; + let mut mock_deps = setup( initial_epoch_num, initial_epoch_start, initial_epoch_duration, + pool_id.clone(), ); // simulate running the test after 100 epochs have elapsed @@ -713,22 +808,28 @@ mod test { let cur_height = initial_epoch_start + initial_epoch_duration * epochs_elapsed + new_epoch_duration * 2; - let initial_params_snapshot = state::load_params(mock_deps.as_ref().storage); + let initial_params_snapshot = + state::load_rewards_pool(mock_deps.as_ref().storage, pool_id.clone()) + .unwrap() + .params; let epoch_prior_to_update = Epoch::current(&initial_params_snapshot, cur_height).unwrap(); let new_params = Params { epoch_duration: 10.try_into().unwrap(), ..initial_params_snapshot.params }; - update_params( + update_pool_params( mock_deps.as_mut().storage, + &pool_id.clone(), new_params.clone(), cur_height, - config.governance.clone(), ) .unwrap(); - let updated_params_snapshot = state::load_params(mock_deps.as_ref().storage); + let updated_params_snapshot = + state::load_rewards_pool(mock_deps.as_ref().storage, pool_id.clone()) + .unwrap() + .params; // should be in new epoch now let epoch = Epoch::current(&updated_params_snapshot, cur_height).unwrap(); @@ -748,15 +849,19 @@ mod test { let cur_epoch_num = 1u64; let block_height_started = 250u64; let epoch_duration = 100u64; - - let (mut mock_deps, _) = setup(cur_epoch_num, block_height_started, epoch_duration); - let pool_id = PoolId { chain_name: "mock-chain".parse().unwrap(), contract: Addr::unchecked("some contract"), }; - let pool = - state::load_rewards_pool_or_new(mock_deps.as_ref().storage, pool_id.clone()).unwrap(); + + let mut mock_deps = setup( + cur_epoch_num, + block_height_started, + epoch_duration, + pool_id.clone(), + ); + + let pool = state::load_rewards_pool(mock_deps.as_ref().storage, pool_id.clone()).unwrap(); assert!(pool.balance.is_zero()); let initial_amount = Uint128::from(100u128); @@ -767,8 +872,7 @@ mod test { ) .unwrap(); - let pool = - state::load_rewards_pool_or_new(mock_deps.as_ref().storage, pool_id.clone()).unwrap(); + let pool = state::load_rewards_pool(mock_deps.as_ref().storage, pool_id.clone()).unwrap(); assert_eq!(pool.balance, initial_amount); let added_amount = Uint128::from(500u128); @@ -779,7 +883,7 @@ mod test { ) .unwrap(); - let pool = state::load_rewards_pool_or_new(mock_deps.as_ref().storage, pool_id).unwrap(); + let pool = state::load_rewards_pool(mock_deps.as_ref().storage, pool_id).unwrap(); assert_eq!(pool.balance, initial_amount + added_amount); } @@ -789,8 +893,17 @@ mod test { let cur_epoch_num = 1u64; let block_height_started = 250u64; let epoch_duration = 100u64; + let pool_id = PoolId { + chain_name: "mock-chain".parse().unwrap(), + contract: Addr::unchecked("some contract"), + }; - let (mut mock_deps, _) = setup(cur_epoch_num, block_height_started, epoch_duration); + let mut mock_deps = setup( + cur_epoch_num, + block_height_started, + epoch_duration, + pool_id.clone(), + ); // a vector of (contract, rewards amounts) pairs let test_data = vec![ (Addr::unchecked("contract_1"), vec![100, 200, 50]), @@ -805,6 +918,19 @@ mod test { chain_name: chain_name.clone(), contract: pool_contract.clone(), }; + let participation_threshold = (1, 2); + let rewards_per_epoch = 100u128; + create_pool( + mock_deps.as_mut().storage, + Params { + epoch_duration: epoch_duration.try_into().unwrap(), + rewards_per_epoch: rewards_per_epoch.try_into().unwrap(), + participation_threshold: participation_threshold.try_into().unwrap(), + }, + block_height_started, + &pool_id, + ) + .unwrap(); for amount in rewards { add_rewards( @@ -822,8 +948,7 @@ mod test { contract: pool_contract.clone(), }; - let pool = - state::load_rewards_pool_or_new(mock_deps.as_ref().storage, pool_id).unwrap(); + let pool = state::load_rewards_pool(mock_deps.as_ref().storage, pool_id).unwrap(); assert_eq!( pool.balance, cosmwasm_std::Uint128::from(rewards.iter().sum::()) @@ -831,6 +956,276 @@ mod test { } } + /// Tests that pools can have different reward amounts + #[test] + fn multiple_pools_different_rewards_amount() { + let cur_epoch_num = 1u64; + let block_height_started = 250u64; + let epoch_duration = 100u64; + + let mut simulated_participation = HashMap::new(); + simulated_participation.insert( + Addr::unchecked("verifier-1"), + ( + PoolId { + chain_name: "mock-chain".parse().unwrap(), + contract: Addr::unchecked("contract-1"), + }, + 3, + ), + ); + simulated_participation.insert( + Addr::unchecked("verifier-2"), + ( + PoolId { + chain_name: "mock-chain-2".parse().unwrap(), + contract: Addr::unchecked("contract-1"), + }, + 4, + ), + ); + simulated_participation.insert( + Addr::unchecked("verifier-3"), + ( + PoolId { + chain_name: "mock-chain".parse().unwrap(), + contract: Addr::unchecked("contract-3"), + }, + 2, + ), + ); + let base_params = Params { + participation_threshold: (1, 2).try_into().unwrap(), + epoch_duration: 100u64.try_into().unwrap(), + rewards_per_epoch: 100u128.try_into().unwrap(), // this is overwritten below + }; + let rewards_per_epoch = vec![50u128, 100u128, 200u128]; + let pool_params: Vec<(PoolId, Params)> = simulated_participation + .values() + .map(|(pool_id, _)| pool_id.clone()) + .zip(rewards_per_epoch.into_iter().map(|r| Params { + rewards_per_epoch: r.try_into().unwrap(), + ..base_params.clone() + })) + .collect(); + + let mut mock_deps = setup_multiple_pools_with_params( + cur_epoch_num, + block_height_started, + pool_params.clone(), + ); + + for (verifier, (pool_contract, events_participated)) in &simulated_participation { + for i in 0..*events_participated { + let event_id = i.to_string().try_into().unwrap(); + record_participation( + mock_deps.as_mut().storage, + event_id, + verifier.clone(), + pool_contract.clone(), + block_height_started, + ) + .unwrap(); + } + } + + for (pool_id, params) in pool_params { + let rewards_to_add = params.rewards_per_epoch; + let _ = add_rewards( + mock_deps.as_mut().storage, + pool_id.clone(), + Uint128::from(rewards_to_add).try_into().unwrap(), + ); + + let distribution = distribute_rewards( + mock_deps.as_mut().storage, + pool_id, + block_height_started + epoch_duration * 2, + None, + ) + .unwrap(); + assert_eq!( + distribution.rewards.values().sum::(), + Uint128::from(params.rewards_per_epoch) + ); + } + } + + /// Tests that pools can have different participation thresholds + #[test] + fn multiple_pools_different_threshold() { + let cur_epoch_num = 1u64; + let block_height_started = 250u64; + let epoch_duration = 100u64; + let pools = vec![ + PoolId { + chain_name: "mock-chain".parse().unwrap(), + contract: Addr::unchecked("contract-1"), + }, + PoolId { + chain_name: "mock-chain-2".parse().unwrap(), + contract: Addr::unchecked("contract-1"), + }, + ]; + + let verifiers = [Addr::unchecked("verifier-1"), Addr::unchecked("verifier-2")]; + + // simulate two verifiers each participating in two pools + // the first verifier participates in 2 events, and the second in 3 events (out of a total of 3 events) + let simulated_participation = vec![ + (verifiers[0].clone(), (pools[0].clone(), 2)), + (verifiers[0].clone(), (pools[1].clone(), 2)), + (verifiers[1].clone(), (pools[0].clone(), 3)), + (verifiers[1].clone(), (pools[1].clone(), 3)), + ]; + let base_params = Params { + participation_threshold: (1, 2).try_into().unwrap(), // this is overwritten below + epoch_duration: 100u64.try_into().unwrap(), + rewards_per_epoch: 100u128.try_into().unwrap(), + }; + // the first pool has a 2/3 threshold, the second 3/4 threshold + let participation_thresholds = vec![(2, 3), (3, 4)]; + let pool_params: Vec<(PoolId, Params)> = pools + .clone() + .into_iter() + .zip(participation_thresholds.into_iter().map(|p| Params { + participation_threshold: p.try_into().unwrap(), + ..base_params.clone() + })) + .collect(); + + let mut mock_deps = setup_multiple_pools_with_params( + cur_epoch_num, + block_height_started, + pool_params.clone(), + ); + + for (verifier, (pool_contract, events_participated)) in &simulated_participation { + for i in 0..*events_participated { + let event_id = i.to_string().try_into().unwrap(); + record_participation( + mock_deps.as_mut().storage, + event_id, + verifier.clone(), + pool_contract.clone(), + block_height_started, + ) + .unwrap(); + } + } + + for (pool_id, params) in pool_params { + let rewards_to_add = params.rewards_per_epoch; + let _ = add_rewards(mock_deps.as_mut().storage, pool_id.clone(), rewards_to_add); + + let distribution = distribute_rewards( + mock_deps.as_mut().storage, + pool_id.clone(), + block_height_started + epoch_duration * 2, + None, + ) + .unwrap(); + + if pool_id == pools[0] { + // the first pool has a 2/3 threshold, which both verifiers meet + assert_eq!( + distribution.rewards, + HashMap::from_iter(verifiers.iter().map(|v| ( + v.clone(), + Uint128::from(Uint128::from(rewards_to_add).u128() / 2) + ))) + ); + } else { + // the second pool has 3/4 threshold, which only the second verifier meets + assert_eq!( + distribution.rewards, + HashMap::from([(verifiers[1].clone(), Uint128::from(rewards_to_add))]) + ); + } + } + } + + /// Tests that pools can have different epoch lengths + #[test] + fn multiple_pools_different_epoch_length() { + let cur_epoch_num = 1u64; + let block_height_started = 250u64; + let base_epoch_duration = 100u64; + let pools = vec![ + PoolId { + chain_name: "mock-chain".parse().unwrap(), + contract: Addr::unchecked("contract-1"), + }, + PoolId { + chain_name: "mock-chain-2".parse().unwrap(), + contract: Addr::unchecked("contract-1"), + }, + ]; + + let verifier = Addr::unchecked("verifier-1"); + + // simulate one verifier participating in two events in each pool + let simulated_participation = vec![ + (verifier.clone(), (pools[0].clone(), 2)), + (verifier.clone(), (pools[1].clone(), 2)), + ]; + + let base_params = Params { + participation_threshold: (1, 2).try_into().unwrap(), + epoch_duration: 100u64.try_into().unwrap(), // this is overwritten below + rewards_per_epoch: 100u128.try_into().unwrap(), + }; + // one pool has twice the epoch duration as the other + let epoch_durations = vec![base_epoch_duration, base_epoch_duration * 2]; + let pool_params: Vec<(PoolId, Params)> = pools + .clone() + .into_iter() + .zip(epoch_durations.into_iter().map(|e| Params { + epoch_duration: e.try_into().unwrap(), + ..base_params.clone() + })) + .collect(); + + let mut mock_deps = setup_multiple_pools_with_params( + cur_epoch_num, + block_height_started, + pool_params.clone(), + ); + + for (verifier, (pool_contract, events_participated)) in &simulated_participation { + for i in 0..*events_participated { + let event_id = i.to_string().try_into().unwrap(); + record_participation( + mock_deps.as_mut().storage, + event_id, + verifier.clone(), + pool_contract.clone(), + block_height_started, + ) + .unwrap(); + } + } + + for (pool_id, params) in pool_params { + let rewards_to_add = params.rewards_per_epoch; + add_rewards(mock_deps.as_mut().storage, pool_id.clone(), rewards_to_add).unwrap(); + + let distribution = distribute_rewards( + mock_deps.as_mut().storage, + pool_id.clone(), + block_height_started + base_epoch_duration * EPOCH_PAYOUT_DELAY, // this is long enough for the first pool to pay out, but not the second + None, + ) + .unwrap(); + + if pool_id == pools[0] { + assert_eq!(distribution.rewards.len(), 1); + } else { + assert_eq!(distribution.rewards.len(), 0); + } + } + } + /// Tests that rewards are distributed correctly based on participation #[test] fn successfully_distribute_rewards() { @@ -839,13 +1234,18 @@ mod test { let epoch_duration = 1000u64; let rewards_per_epoch = 100u128; let participation_threshold = (2, 3); + let pool_id = PoolId { + chain_name: "mock-chain".parse().unwrap(), + contract: Addr::unchecked("pool_contract"), + }; - let (mut mock_deps, _) = setup_with_params( + let mut mock_deps = setup_with_params( cur_epoch_num, block_height_started, epoch_duration, rewards_per_epoch, participation_threshold, + pool_id.clone(), ); let verifier1 = Addr::unchecked("verifier1"); let verifier2 = Addr::unchecked("verifier2"); @@ -881,11 +1281,6 @@ mod test { (verifier4.clone(), rewards_per_epoch / 4), ]); - let pool_id = PoolId { - chain_name: "mock-chain".parse().unwrap(), - contract: Addr::unchecked("pool_contract"), - }; - for (verifier, events_participated) in verifier_participation_per_epoch.clone() { for (epoch, events) in events_participated.iter().enumerate().take(epoch_count) { for event in events { @@ -901,8 +1296,8 @@ mod test { } } - // we add 2 epochs worth of rewards. There were 2 epochs of participation, but only 2 epochs where rewards should be given out - // These tests we are accounting correctly, and only removing from the pool when we actually give out rewards + // we add 2 epochs worth of rewards. There were 4 epochs of participation, but only 2 epochs where rewards should be given out + // This tests we are accounting correctly, and only removing from the pool when we actually give out rewards let rewards_added = 2 * rewards_per_epoch; let _ = add_rewards( mock_deps.as_mut().storage, @@ -910,14 +1305,16 @@ mod test { Uint128::from(rewards_added).try_into().unwrap(), ); - let rewards_claimed = distribute_rewards( + let distribution = distribute_rewards( mock_deps.as_mut().storage, pool_id, - block_height_started + epoch_duration * (epoch_count + 2) as u64, + block_height_started + epoch_duration * (epoch_count as u64 + 1), None, ) .unwrap(); + let rewards_claimed = distribution.rewards; + assert_eq!( rewards_claimed.len(), verifier_participation_per_epoch.len() @@ -929,6 +1326,11 @@ mod test { Some(&Uint128::from(rewards)) ); } + + assert_eq!( + distribution.epochs_processed, + Vec::from_iter(0u64..epoch_count as u64) + ); } /// Tests that rewards are distributed correctly for a specified number of epochs, and that pagination works correctly @@ -939,19 +1341,20 @@ mod test { let epoch_duration = 1000u64; let rewards_per_epoch = 100u128; let participation_threshold = (1, 2); + let pool_id = PoolId { + chain_name: "mock-chain".parse().unwrap(), + contract: Addr::unchecked("pool_contract"), + }; - let (mut mock_deps, _) = setup_with_params( + let mut mock_deps = setup_with_params( cur_epoch_num, block_height_started, epoch_duration, rewards_per_epoch, participation_threshold, + pool_id.clone(), ); let verifier = Addr::unchecked("verifier"); - let pool_id = PoolId { - chain_name: "mock-chain".parse().unwrap(), - contract: Addr::unchecked("pool_contract"), - }; for height in block_height_started..block_height_started + epoch_duration * 9 { let event_id = height.to_string() + "event"; @@ -971,34 +1374,45 @@ mod test { Uint128::from(rewards_added).try_into().unwrap(), ); - // this puts us in epoch 10 + // this puts us in epoch 9 let cur_height = block_height_started + epoch_duration * 9; let total_epochs_with_rewards = (cur_height / epoch_duration) - 1; // distribute 5 epochs worth of rewards let epochs_to_process = 5; - let rewards_claimed = distribute_rewards( + let distribution = distribute_rewards( mock_deps.as_mut().storage, pool_id.clone(), cur_height, Some(epochs_to_process), ) .unwrap(); + let rewards_claimed = distribution.rewards; assert_eq!(rewards_claimed.len(), 1); assert!(rewards_claimed.contains_key(&verifier)); assert_eq!( rewards_claimed.get(&verifier), Some(&(rewards_per_epoch * epochs_to_process as u128).into()) ); + assert_eq!( + distribution.epochs_processed, + Vec::from_iter(0u64..epochs_to_process) + ); + assert_eq!( + distribution.current_epoch.epoch_num, + cur_height / epoch_duration + ); + assert!(distribution.can_distribute_more); // distribute the remaining epochs worth of rewards - let rewards_claimed = distribute_rewards( + let distribution = distribute_rewards( mock_deps.as_mut().storage, pool_id.clone(), cur_height, None, ) .unwrap(); + let rewards_claimed = distribution.rewards; assert_eq!(rewards_claimed.len(), 1); assert!(rewards_claimed.contains_key(&verifier)); assert_eq!( @@ -1008,6 +1422,15 @@ mod test { .into() ) ); + assert_eq!( + distribution.epochs_processed, + Vec::from_iter(epochs_to_process..total_epochs_with_rewards) + ); + assert_eq!( + distribution.current_epoch.epoch_num, + cur_height / epoch_duration + ); + assert!(!distribution.can_distribute_more); } /// Tests that we do not distribute rewards for a given epoch until two epochs later @@ -1018,13 +1441,18 @@ mod test { let epoch_duration = 1000u64; let rewards_per_epoch = 100u128; let participation_threshold = (8, 10); + let pool_id = PoolId { + chain_name: "mock-chain".parse().unwrap(), + contract: Addr::unchecked("pool_contract"), + }; - let (mut mock_deps, _) = setup_with_params( + let mut mock_deps = setup_with_params( cur_epoch_num, block_height_started, epoch_duration, rewards_per_epoch, participation_threshold, + pool_id.clone(), ); let verifier = Addr::unchecked("verifier"); let pool_id = PoolId { @@ -1068,14 +1496,15 @@ mod test { assert_eq!(err.current_context(), &ContractError::NoRewardsToDistribute); // can claim now, two epochs after participation - let rewards_claimed = distribute_rewards( + let distribution = distribute_rewards( mock_deps.as_mut().storage, pool_id.clone(), block_height_started + epoch_duration * 2, None, ) .unwrap(); - assert_eq!(rewards_claimed.len(), 1); + assert_eq!(distribution.rewards.len(), 1); + assert!(!distribution.can_distribute_more); // should error if we try again let err = distribute_rewards( @@ -1097,13 +1526,18 @@ mod test { let epoch_duration = 1000u64; let rewards_per_epoch = 100u128; let participation_threshold = (8, 10); + let pool_id = PoolId { + chain_name: "mock-chain".parse().unwrap(), + contract: Addr::unchecked("pool_contract"), + }; - let (mut mock_deps, _) = setup_with_params( + let mut mock_deps = setup_with_params( cur_epoch_num, block_height_started, epoch_duration, rewards_per_epoch, participation_threshold, + pool_id.clone(), ); let verifier = Addr::unchecked("verifier"); let pool_id = PoolId { @@ -1146,14 +1580,14 @@ mod test { Uint128::from(rewards_added).try_into().unwrap(), ); - let result = distribute_rewards( + let distribution = distribute_rewards( mock_deps.as_mut().storage, pool_id, block_height_started + epoch_duration * 2, None, - ); - assert!(result.is_ok()); - assert_eq!(result.unwrap().len(), 1); + ) + .unwrap(); + assert_eq!(distribution.rewards.len(), 1); } /// Tests that an error is returned from distribute_rewards when trying to claim rewards for the same epoch more than once @@ -1164,19 +1598,20 @@ mod test { let epoch_duration = 1000u64; let rewards_per_epoch = 100u128; let participation_threshold = (8, 10); + let pool_id = PoolId { + chain_name: "mock-chain".parse().unwrap(), + contract: Addr::unchecked("pool_contract"), + }; - let (mut mock_deps, _) = setup_with_params( + let mut mock_deps = setup_with_params( cur_epoch_num, block_height_started, epoch_duration, rewards_per_epoch, participation_threshold, + pool_id.clone(), ); let verifier = Addr::unchecked("verifier"); - let pool_id = PoolId { - chain_name: "mock-chain".parse().unwrap(), - contract: Addr::unchecked("pool_contract"), - }; let _ = record_participation( mock_deps.as_mut().storage, @@ -1193,14 +1628,15 @@ mod test { Uint128::from(rewards_added).try_into().unwrap(), ); - let rewards_claimed = distribute_rewards( + let distribution = distribute_rewards( mock_deps.as_mut().storage, pool_id.clone(), block_height_started + epoch_duration * 2, None, ) .unwrap(); - assert_eq!(rewards_claimed.len(), 1); + assert_eq!(distribution.rewards.len(), 1); + assert_eq!(distribution.epochs_processed, vec![cur_epoch_num]); // try to claim again, shouldn't get an error let err = distribute_rewards( @@ -1213,62 +1649,99 @@ mod test { assert_eq!(err.current_context(), &ContractError::NoRewardsToDistribute); } - type MockDeps = OwnedDeps; + #[test] + fn cannot_record_participation_before_pool_is_created() { + let cur_epoch_num = 1u64; + let block_height_started = 250u64; + let mut mock_deps = + setup_multiple_pools_with_params(cur_epoch_num, block_height_started, vec![]); - fn set_initial_storage( - params_store: ParamsSnapshot, - events_store: Vec, - tally_store: Vec, - rewards_store: Vec, - watermark_store: HashMap, - ) -> (MockDeps, Config) { - let mut deps = mock_dependencies(); - let storage = deps.as_mut().storage; + assert!(record_participation( + mock_deps.as_mut().storage, + "some-event".parse().unwrap(), + Addr::unchecked("verifier"), + PoolId { + chain_name: "mock-chain".parse().unwrap(), + contract: Addr::unchecked("contract") + }, + block_height_started + ) + .is_err()); + } - state::save_params(storage, ¶ms_store).unwrap(); + #[test] + fn cannot_add_rewards_before_pool_is_created() { + let cur_epoch_num = 1u64; + let block_height_started = 250u64; + let mut mock_deps = + setup_multiple_pools_with_params(cur_epoch_num, block_height_started, vec![]); + assert!(add_rewards( + mock_deps.as_mut().storage, + PoolId { + chain_name: "mock-chain".parse().unwrap(), + contract: Addr::unchecked("contract") + }, + 100u128.try_into().unwrap(), + ) + .is_err()); + } - events_store.iter().for_each(|event| { - state::save_event(storage, event).unwrap(); - }); + #[test] + fn cannot_distribute_rewards_before_pool_is_created() { + let cur_epoch_num = 1u64; + let block_height_started = 250u64; + let mut mock_deps = + setup_multiple_pools_with_params(cur_epoch_num, block_height_started, vec![]); + assert!(distribute_rewards( + mock_deps.as_mut().storage, + PoolId { + chain_name: "mock-chain".parse().unwrap(), + contract: Addr::unchecked("contract") + }, + block_height_started, + None + ) + .is_err()); + } - tally_store.iter().for_each(|tally| { - state::save_epoch_tally(storage, tally).unwrap(); - }); + type MockDeps = OwnedDeps; + + fn setup_multiple_pools_with_params( + cur_epoch_num: u64, + block_height_started: u64, + pools: Vec<(PoolId, Params)>, + ) -> MockDeps { + let current_epoch = Epoch { + epoch_num: cur_epoch_num, + block_height_started, + }; - rewards_store.iter().for_each(|pool| { - state::save_rewards_pool(storage, pool).unwrap(); - }); + let mut deps = mock_dependencies(); + let storage = deps.as_mut().storage; + for (pool_id, params) in pools { + let params_snapshot = ParamsSnapshot { + params, + created_at: current_epoch.clone(), + }; - watermark_store - .into_iter() - .for_each(|(pool_id, epoch_num)| { - state::save_rewards_watermark(storage, pool_id, epoch_num).unwrap(); - }); + state::save_rewards_pool( + storage, + &RewardsPool { + id: pool_id, + params: params_snapshot, + balance: Uint128::zero(), + }, + ) + .unwrap(); + } let config = Config { - governance: Addr::unchecked("governance"), rewards_denom: "AXL".to_string(), }; CONFIG.save(storage, &config).unwrap(); - (deps, config) - } - - fn setup_with_stores( - params_store: ParamsSnapshot, - events_store: Vec, - tally_store: Vec, - rewards_store: Vec, - watermark_store: HashMap, - ) -> (MockDeps, Config) { - set_initial_storage( - params_store, - events_store, - tally_store, - rewards_store, - watermark_store, - ) + deps } fn setup_with_params( @@ -1277,7 +1750,8 @@ mod test { epoch_duration: u64, rewards_per_epoch: u128, participation_threshold: (u64, u64), - ) -> (MockDeps, Config) { + pool_id: PoolId, + ) -> MockDeps { let rewards_per_epoch: nonempty::Uint128 = cosmwasm_std::Uint128::from(rewards_per_epoch) .try_into() .unwrap(); @@ -1295,24 +1769,33 @@ mod test { created_at: current_epoch.clone(), }; - let rewards_store = Vec::new(); - let events_store = Vec::new(); - let tally_store = Vec::new(); - let watermark_store = HashMap::new(); - setup_with_stores( - params_snapshot, - events_store, - tally_store, - rewards_store, - watermark_store, + let mut deps = mock_dependencies(); + let storage = deps.as_mut().storage; + state::save_rewards_pool( + storage, + &RewardsPool { + id: pool_id, + params: params_snapshot, + balance: Uint128::zero(), + }, ) + .unwrap(); + + let config = Config { + rewards_denom: "AXL".to_string(), + }; + + CONFIG.save(storage, &config).unwrap(); + + deps } fn setup( cur_epoch_num: u64, block_height_started: u64, epoch_duration: u64, - ) -> (MockDeps, Config) { + pool_id: PoolId, + ) -> MockDeps { let participation_threshold = (1, 2); let rewards_per_epoch = 100u128; setup_with_params( @@ -1321,6 +1804,7 @@ mod test { epoch_duration, rewards_per_epoch, participation_threshold, + pool_id, ) } } diff --git a/contracts/rewards/src/contract/migrations/mod.rs b/contracts/rewards/src/contract/migrations/mod.rs new file mode 100644 index 000000000..1d185e9d7 --- /dev/null +++ b/contracts/rewards/src/contract/migrations/mod.rs @@ -0,0 +1 @@ +pub mod v1_0_0; diff --git a/contracts/rewards/src/contract/migrations/v1_0_0.rs b/contracts/rewards/src/contract/migrations/v1_0_0.rs new file mode 100644 index 000000000..3db5b8312 --- /dev/null +++ b/contracts/rewards/src/contract/migrations/v1_0_0.rs @@ -0,0 +1,175 @@ +#![allow(deprecated)] + +use axelar_wasm_std::error::ContractError; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Order, Storage, Uint128}; +use cw_storage_plus::{Item, Map}; + +use crate::contract::CONTRACT_NAME; +use crate::state::{self, ParamsSnapshot, PoolId}; + +const BASE_VERSION: &str = "1.0.0"; + +pub fn migrate(storage: &mut dyn Storage) -> Result<(), ContractError> { + cw2::assert_contract_version(storage, CONTRACT_NAME, BASE_VERSION)?; + + migrate_params(storage)?; + Ok(()) +} + +fn migrate_params(storage: &mut dyn Storage) -> Result<(), ContractError> { + let params = PARAMS.load(storage)?; + let pools = get_all_pools(storage)?; + + for pool in pools { + state::save_rewards_pool( + storage, + &state::RewardsPool { + params: params.to_owned(), + id: pool.id, + balance: pool.balance, + }, + )?; + } + PARAMS.remove(storage); + + Ok(()) +} + +const POOLS: Map = Map::new("pools"); + +fn get_all_pools(storage: &mut dyn Storage) -> Result, ContractError> { + POOLS + .range(storage, None, None, Order::Ascending) + .map(|res| res.map(|(_, pool)| pool).map_err(|err| err.into())) + .collect::, _>>() +} + +#[deprecated(since = "1.0.0", note = "only used during migration")] +const PARAMS: Item = Item::new("params"); + +#[cw_serde] +#[deprecated(since = "1.0.0", note = "only used during migration")] +struct RewardsPool { + pub id: PoolId, + pub balance: Uint128, +} + +#[cfg(test)] +pub mod tests { + use axelar_wasm_std::permission_control; + use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; + use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, Response, Uint128}; + + use super::{RewardsPool, PARAMS, POOLS}; + use crate::contract::migrations::v1_0_0; + use crate::contract::CONTRACT_NAME; + use crate::msg::{InstantiateMsg, Params}; + use crate::state::{self, Config, Epoch, ParamsSnapshot, PoolId, CONFIG}; + + #[test] + fn migrate_rewards_pools() { + let mut deps = mock_dependencies(); + instantiate_contract(deps.as_mut(), "denom"); + + let test_pools = vec![ + RewardsPool { + id: PoolId { + chain_name: "mock-chain".parse().unwrap(), + contract: Addr::unchecked("contract-1"), + }, + balance: Uint128::from(250u128), + }, + RewardsPool { + id: PoolId { + chain_name: "mock-chain-2".parse().unwrap(), + contract: Addr::unchecked("contract-2"), + }, + balance: Uint128::from(100u128), + }, + ]; + + for pool in &test_pools { + POOLS + .save(deps.as_mut().storage, pool.id.to_owned(), pool) + .unwrap(); + } + let params = PARAMS.load(deps.as_mut().storage).unwrap(); + + v1_0_0::migrate(deps.as_mut().storage).unwrap(); + + for pool in &test_pools { + let new_pool = + state::load_rewards_pool(deps.as_mut().storage, pool.id.to_owned()).unwrap(); + assert_eq!( + new_pool, + state::RewardsPool { + id: pool.id.to_owned(), + balance: pool.balance, + params: params.clone() + } + ); + } + } + + #[test] + fn migrate_checks_contract_version() { + let mut deps = mock_dependencies(); + instantiate_contract(deps.as_mut(), "denom"); + cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, "something wrong").unwrap(); + + assert!(v1_0_0::migrate(deps.as_mut().storage).is_err()); + + cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, v1_0_0::BASE_VERSION) + .unwrap(); + + assert!(v1_0_0::migrate(deps.as_mut().storage).is_ok()); + } + + #[deprecated(since = "0.4.0", note = "only used during migration tests")] + pub fn instantiate_contract(deps: DepsMut, denom: impl Into) { + let msg = InstantiateMsg { + governance_address: "governance".to_string(), + rewards_denom: denom.into(), + }; + instantiate(deps, mock_env(), mock_info("anyone", &[]), msg).unwrap(); + } + + #[deprecated(since = "0.4.0", note = "only used during migration tests")] + fn instantiate( + deps: DepsMut, + env: Env, + _info: MessageInfo, + msg: InstantiateMsg, + ) -> Result { + cw2::set_contract_version(deps.storage, CONTRACT_NAME, v1_0_0::BASE_VERSION)?; + + let governance = deps.api.addr_validate(&msg.governance_address)?; + permission_control::set_governance(deps.storage, &governance)?; + + CONFIG.save( + deps.storage, + &Config { + rewards_denom: msg.rewards_denom, + }, + )?; + + let params = Params { + epoch_duration: 100u64.try_into().unwrap(), + rewards_per_epoch: 1000u128.try_into().unwrap(), + participation_threshold: (1, 2).try_into().unwrap(), + }; + PARAMS.save( + deps.storage, + &ParamsSnapshot { + params, + created_at: Epoch { + epoch_num: 0, + block_height_started: env.block.height, + }, + }, + )?; + + Ok(Response::new()) + } +} diff --git a/contracts/rewards/src/contract/query.rs b/contracts/rewards/src/contract/query.rs index 2a01fdc15..998770ce2 100644 --- a/contracts/rewards/src/contract/query.rs +++ b/contracts/rewards/src/contract/query.rs @@ -1,11 +1,9 @@ use cosmwasm_std::{Storage, Uint64}; use error_stack::Result; -use crate::{ - error::ContractError, - msg, - state::{self, Epoch, PoolId}, -}; +use crate::error::ContractError; +use crate::msg; +use crate::state::{self, Epoch, PoolId}; pub fn rewards_pool( storage: &dyn Storage, @@ -13,7 +11,7 @@ pub fn rewards_pool( block_height: u64, ) -> Result { let pool = state::load_rewards_pool(storage, pool_id.clone())?; - let current_params = state::load_params(storage); + let current_params = pool.params; let cur_epoch = Epoch::current(¤t_params, block_height)?; // the params could have been updated since the tally was created. Therefore we use the params from the @@ -35,16 +33,43 @@ pub fn rewards_pool( }) } +pub fn participation( + storage: &dyn Storage, + pool_id: PoolId, + epoch_num: Option, + block_height: u64, +) -> Result, ContractError> { + let epoch_num = match epoch_num { + Some(num) => num, + None => { + let current_params = state::load_rewards_pool_params(storage, pool_id.clone())?; + Epoch::current(¤t_params, block_height)?.epoch_num + } + }; + + let tally = state::load_epoch_tally(storage, pool_id, epoch_num)?; + + match tally { + None => Ok(None), + Some(tally) => Ok(Some(msg::Participation { + event_count: tally.event_count, + participation: tally.verifier_participation(), + rewards_by_verifier: tally.rewards_by_verifier(), + epoch: tally.epoch, + params: tally.params, + })), + } +} + #[cfg(test)] mod tests { - use cosmwasm_std::{testing::mock_dependencies, Addr, Uint128, Uint64}; - - use crate::{ - msg::Params, - state::{EpochTally, ParamsSnapshot, RewardsPool}, - }; + use cosmwasm_std::testing::mock_dependencies; + use cosmwasm_std::{Addr, Uint128, Uint64}; + use msg::Participation; use super::*; + use crate::msg::Params; + use crate::state::{EpochTally, ParamsSnapshot, RewardsPool}; fn setup(storage: &mut dyn Storage, initial_balance: Uint128) -> (ParamsSnapshot, PoolId) { let pool_id = PoolId { @@ -69,9 +94,9 @@ mod tests { let rewards_pool = RewardsPool { id: pool_id.clone(), balance: initial_balance, + params: params_snapshot.clone(), }; - state::save_params(storage, ¶ms_snapshot).unwrap(); state::save_rewards_pool(storage, &rewards_pool).unwrap(); (params_snapshot, pool_id) @@ -234,4 +259,60 @@ mod tests { &ContractError::RewardsPoolNotFound ); } + + #[test] + fn should_get_participation() { + let mut deps = mock_dependencies(); + let balance = Uint128::from(1000u128); + let (current_params, pool_id) = setup(deps.as_mut().storage, balance); + + let block_height = 1000; + let epoch = Epoch::current(¤t_params, block_height).unwrap(); + + let mut tally = EpochTally::new( + pool_id.clone(), + epoch.clone(), + current_params.params.clone(), + ); + tally = tally.record_participation(Addr::unchecked("verifier_1")); + tally = tally.record_participation(Addr::unchecked("verifier_2")); + tally.event_count = tally.event_count.saturating_add(1); + state::save_epoch_tally(deps.as_mut().storage, &tally).unwrap(); + + let expected = Participation { + event_count: tally.event_count, + participation: tally.verifier_participation(), + rewards_by_verifier: tally.rewards_by_verifier(), + epoch: Epoch::current(¤t_params.clone(), block_height).unwrap(), + params: current_params.params.clone(), + }; + + // get participation for current epoch + let res = + participation(deps.as_mut().storage, pool_id.clone(), None, block_height).unwrap(); + assert_eq!(res.unwrap(), expected); + + // get participation for past epoch + let res = participation( + deps.as_mut().storage, + pool_id.clone(), + Some(epoch.epoch_num), + block_height + u64::from(current_params.params.epoch_duration), + ) + .unwrap(); + assert_eq!(res.unwrap(), expected); + } + + #[test] + fn participation_should_return_none_when_no_participation() { + let mut deps = mock_dependencies(); + let balance = Uint128::from(1000u128); + let (_, pool_id) = setup(deps.as_mut().storage, balance); + + let block_height = 1000; + + let res = + participation(deps.as_mut().storage, pool_id.clone(), None, block_height).unwrap(); + assert!(res.is_none()); + } } diff --git a/contracts/rewards/src/error.rs b/contracts/rewards/src/error.rs index 15a51ef38..11aaf4de3 100644 --- a/contracts/rewards/src/error.rs +++ b/contracts/rewards/src/error.rs @@ -1,9 +1,12 @@ -use axelar_wasm_std_derive::IntoContractError; -use cosmwasm_std::OverflowError; +use axelar_wasm_std::IntoContractError; +use cosmwasm_std::{OverflowError, StdError}; use thiserror::Error; #[derive(Error, Debug, PartialEq, IntoContractError)] pub enum ContractError { + #[error(transparent)] + Std(#[from] StdError), + #[error("error saving params")] SaveParams, @@ -16,6 +19,9 @@ pub enum ContractError { #[error("error saving rewards pool")] SaveRewardsPool, + #[error("error updating rewards pool")] + UpdateRewardsPool, + #[error("error saving rewards watermark")] SaveRewardsWatermark, @@ -31,6 +37,9 @@ pub enum ContractError { #[error("rewards pool not found")] RewardsPoolNotFound, + #[error("rewards pool already exists")] + RewardsPoolAlreadyExists, + #[error("error loading rewards watermark")] LoadRewardsWatermark, diff --git a/contracts/rewards/src/events.rs b/contracts/rewards/src/events.rs new file mode 100644 index 000000000..4bd221422 --- /dev/null +++ b/contracts/rewards/src/events.rs @@ -0,0 +1,53 @@ +use std::collections::HashMap; + +use cosmwasm_std::{Addr, Uint128}; + +use crate::state::{Epoch, RewardsDistribution}; + +pub enum Event { + RewardsDistributed { + rewards: HashMap, + epochs_processed: Vec, + current_epoch: Epoch, + can_distribute_more: bool, + }, +} + +impl From for Event { + fn from(value: RewardsDistribution) -> Self { + Event::RewardsDistributed { + rewards: value.rewards, + epochs_processed: value.epochs_processed, + current_epoch: value.current_epoch, + can_distribute_more: value.can_distribute_more, + } + } +} + +impl From for cosmwasm_std::Event { + fn from(other: Event) -> Self { + match other { + Event::RewardsDistributed { + rewards, + epochs_processed, + current_epoch, + can_distribute_more: more_epochs_to_distribute, + } => cosmwasm_std::Event::new("rewards_distributed") + .add_attribute( + "rewards", + serde_json::to_string(&rewards).expect("failed to serialize rewards"), + ) + .add_attribute( + "epochs_processed", + serde_json::to_string(&epochs_processed) + .expect("failed to serialize epochs processed"), + ) + .add_attribute( + "current_epoch", + serde_json::to_string(¤t_epoch) + .expect("failed to serialize current epoch"), + ) + .add_attribute("can_distribute_more", more_epochs_to_distribute.to_string()), + } + } +} diff --git a/contracts/rewards/src/lib.rs b/contracts/rewards/src/lib.rs index a5abdbb0f..bf0cc0a11 100644 --- a/contracts/rewards/src/lib.rs +++ b/contracts/rewards/src/lib.rs @@ -1,4 +1,7 @@ pub mod contract; pub mod error; +pub mod events; pub mod msg; -pub mod state; +mod state; + +pub use state::{Epoch, PoolId}; diff --git a/contracts/rewards/src/msg.rs b/contracts/rewards/src/msg.rs index ea7ce45dc..be31741f3 100644 --- a/contracts/rewards/src/msg.rs +++ b/contracts/rewards/src/msg.rs @@ -1,15 +1,17 @@ +use std::collections::HashMap; + use axelar_wasm_std::{nonempty, Threshold}; use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{Uint128, Uint64}; +use cosmwasm_std::{Addr, Uint128, Uint64}; +use msgs_derive::EnsurePermissions; use router_api::ChainName; -use crate::state::PoolId; +use crate::state::{Epoch, PoolId}; #[cw_serde] pub struct InstantiateMsg { pub governance_address: String, pub rewards_denom: String, - pub params: Params, } #[cw_serde] @@ -29,8 +31,10 @@ pub struct Params { } #[cw_serde] +#[derive(EnsurePermissions)] pub enum ExecuteMsg { /// Log a specific verifier as participating in a specific event. Verifier weights are ignored + /// This call will error if the pool does not yet exist. /// /// TODO: For batched voting, treating the entire batch as a single event can be problematic. /// A verifier may vote correctly for 9 out of 10 messages in a batch, but the verifier's participation @@ -38,6 +42,7 @@ pub enum ExecuteMsg { /// verifier could choose to record the participation, but then the missed message is not recorded in any way. /// A possible solution to this is to add a weight to each event, where the voting verifier specifies the number /// of messages in a batch as well as the number of messages a particular verifier actually participated in. + #[permission(Any)] RecordParticipation { chain_name: ChainName, event_id: nonempty::String, @@ -45,18 +50,28 @@ pub enum ExecuteMsg { }, /// Distribute rewards up to epoch T - 2 (i.e. if we are currently in epoch 10, distribute all undistributed rewards for epochs 0-8) and send the required number of tokens to each verifier + /// This call will error if the pool does not yet exist. + #[permission(Any)] DistributeRewards { pool_id: PoolId, /// Maximum number of historical epochs for which to distribute rewards, starting with the oldest. If not specified, distribute rewards for 10 epochs. epoch_count: Option, }, - /// Start a new reward pool for the given contract if none exists. Otherwise, add tokens to an existing reward pool. + /// Add tokens to an existing rewards pool. /// Any attached funds with a denom matching the rewards denom are added to the pool. + /// This call will error if the pool does not yet exist. + #[permission(Any)] AddRewards { pool_id: PoolId }, - /// Overwrites the currently stored params. Callable only by governance. - UpdateParams { params: Params }, + /// Overwrites the currently stored params for the specified pool. Callable only by governance. + /// This call will error if the pool does not yet exist. + #[permission(Governance)] + UpdatePoolParams { params: Params, pool_id: PoolId }, + + /// Creates a rewards pool with the specified pool ID and parameters. Callable only by governance. + #[permission(Governance)] + CreatePool { params: Params, pool_id: PoolId }, } #[cw_serde] @@ -65,6 +80,13 @@ pub enum QueryMsg { /// Gets the rewards pool details for the given `pool_id`` #[returns(RewardsPool)] RewardsPool { pool_id: PoolId }, + + /// Gets verifier participation info for a given epoch (or the current epoch if unspecified) and pool. If no participation was recorded, returns None + #[returns(Option)] + VerifierParticipation { + pool_id: PoolId, + epoch_num: Option, + }, } #[cw_serde] @@ -75,3 +97,12 @@ pub struct RewardsPool { pub current_epoch_num: Uint64, pub last_distribution_epoch: Option, } + +#[cw_serde] +pub struct Participation { + pub event_count: u64, + pub participation: HashMap, // maps a verifier address to participation count + pub rewards_by_verifier: HashMap, // maps a verifier address to amount of rewards + pub epoch: Epoch, + pub params: Params, +} diff --git a/contracts/rewards/src/state.rs b/contracts/rewards/src/state.rs index 072301271..e043d1321 100644 --- a/contracts/rewards/src/state.rs +++ b/contracts/rewards/src/state.rs @@ -8,11 +8,26 @@ use cw_storage_plus::{Item, Key, KeyDeserialize, Map, Prefixer, PrimaryKey}; use error_stack::{Result, ResultExt}; use router_api::ChainName; -use crate::{error::ContractError, msg::Params}; +use crate::error::ContractError; +use crate::msg::Params; + +/// Maps a (pool id, epoch number) pair to a tally for that epoch and rewards pool +const TALLIES: Map = Map::new("tallies"); + +/// Maps an (event id, pool id) pair to an Event +const EVENTS: Map<(String, PoolId), Event> = Map::new("events"); + +/// Maps the id to the rewards pool for given chain and contract +const POOLS: Map = Map::new("pools"); + +/// Maps a rewards pool to the epoch number of the most recent epoch for which rewards were distributed. All epochs prior +/// have had rewards distributed already and all epochs after have not yet had rewards distributed for this pool +const WATERMARKS: Map = Map::new("rewards_watermarks"); + +pub const CONFIG: Item = Item::new("config"); #[cw_serde] pub struct Config { - pub governance: Addr, pub rewards_denom: String, } @@ -161,6 +176,13 @@ impl EpochTally { }) .collect() } + + pub fn verifier_participation(&self) -> HashMap { + self.participation + .iter() + .map(|(verifier, participation)| (Addr::unchecked(verifier), *participation)) // Ok to convert unchecked here, since we only store valid addresses + .collect() + } } #[cw_serde] @@ -200,10 +222,10 @@ impl Epoch { if cur_block_height < last_updated_epoch.block_height_started { Err(ContractError::BlockHeightInPast.into()) } else { - let epochs_elapsed = (cur_block_height - .saturating_sub(last_updated_epoch.block_height_started)) - .checked_div(epoch_duration) - .expect("invalid invariant: epoch duration is zero"); + let epochs_elapsed = cur_block_height + .saturating_sub(last_updated_epoch.block_height_started) + .checked_div(epoch_duration) + .expect("invalid invariant: epoch duration is zero"); Ok(Epoch { epoch_num: last_updated_epoch .epoch_num @@ -223,16 +245,10 @@ impl Epoch { pub struct RewardsPool { pub id: PoolId, pub balance: Uint128, + pub params: ParamsSnapshot, } impl RewardsPool { - pub fn new(id: PoolId) -> Self { - RewardsPool { - id, - balance: Uint128::zero(), - } - } - pub fn sub_reward(mut self, reward: Uint128) -> Result { self.balance = self .balance @@ -243,33 +259,22 @@ impl RewardsPool { } } -/// Current rewards parameters, along with when the params were updated -pub const PARAMS: Item = Item::new("params"); - -/// Maps a (pool id, epoch number) pair to a tally for that epoch and rewards pool -const TALLIES: Map = Map::new("tallies"); - -/// Maps an (event id, pool id) pair to an Event -const EVENTS: Map<(String, PoolId), Event> = Map::new("events"); - -/// Maps the id to the rewards pool for given chain and contract -const POOLS: Map = Map::new("pools"); - -/// Maps a rewards pool to the epoch number of the most recent epoch for which rewards were distributed. All epochs prior -/// have had rewards distributed already and all epochs after have not yet had rewards distributed for this pool -const WATERMARKS: Map = Map::new("rewards_watermarks"); - -pub const CONFIG: Item = Item::new("config"); - -pub(crate) fn load_config(storage: &dyn Storage) -> Config { +#[cw_serde] +pub struct RewardsDistribution { + /// Amount of rewards denom each verifier received + pub rewards: HashMap, + /// List of epochs processed for this distribution + pub epochs_processed: Vec, + /// Epoch in which rewards were distributed + pub current_epoch: Epoch, + /// True if there are more rewards to distribute (later epochs that have not yet been distributed but are ready for distribution at the time of calling) + pub can_distribute_more: bool, +} +pub fn load_config(storage: &dyn Storage) -> Config { CONFIG.load(storage).expect("couldn't load config") } -pub(crate) fn load_params(storage: &dyn Storage) -> ParamsSnapshot { - PARAMS.load(storage).expect("params should exist") -} - -pub(crate) fn load_rewards_watermark( +pub fn load_rewards_watermark( storage: &dyn Storage, pool_id: PoolId, ) -> Result, ContractError> { @@ -278,7 +283,7 @@ pub(crate) fn load_rewards_watermark( .change_context(ContractError::LoadRewardsWatermark) } -pub(crate) fn load_event( +pub fn load_event( storage: &dyn Storage, event_id: String, pool_id: PoolId, @@ -288,7 +293,7 @@ pub(crate) fn load_event( .change_context(ContractError::LoadEvent) } -pub(crate) fn load_epoch_tally( +pub fn load_epoch_tally( storage: &dyn Storage, pool_id: PoolId, epoch_num: u64, @@ -298,7 +303,7 @@ pub(crate) fn load_epoch_tally( .change_context(ContractError::LoadEpochTally) } -pub(crate) fn may_load_rewards_pool( +pub fn may_load_rewards_pool( storage: &dyn Storage, pool_id: PoolId, ) -> Result, ContractError> { @@ -307,32 +312,24 @@ pub(crate) fn may_load_rewards_pool( .change_context(ContractError::LoadRewardsPool) } -pub(crate) fn load_rewards_pool_or_new( +pub fn load_rewards_pool( storage: &dyn Storage, pool_id: PoolId, ) -> Result { - may_load_rewards_pool(storage, pool_id.clone()) - .map(|pool| pool.unwrap_or(RewardsPool::new(pool_id))) + may_load_rewards_pool(storage, pool_id.clone())? + .ok_or(ContractError::RewardsPoolNotFound.into()) } -pub(crate) fn load_rewards_pool( +pub fn load_rewards_pool_params( storage: &dyn Storage, pool_id: PoolId, -) -> Result { +) -> Result { may_load_rewards_pool(storage, pool_id.clone())? .ok_or(ContractError::RewardsPoolNotFound.into()) + .map(|pool| pool.params) } -pub(crate) fn save_params( - storage: &mut dyn Storage, - params: &ParamsSnapshot, -) -> Result<(), ContractError> { - PARAMS - .save(storage, params) - .change_context(ContractError::SaveParams) -} - -pub(crate) fn save_rewards_watermark( +pub fn save_rewards_watermark( storage: &mut dyn Storage, pool_id: PoolId, epoch_num: u64, @@ -342,7 +339,7 @@ pub(crate) fn save_rewards_watermark( .change_context(ContractError::SaveRewardsWatermark) } -pub(crate) fn save_event(storage: &mut dyn Storage, event: &Event) -> Result<(), ContractError> { +pub fn save_event(storage: &mut dyn Storage, event: &Event) -> Result<(), ContractError> { EVENTS .save( storage, @@ -352,7 +349,7 @@ pub(crate) fn save_event(storage: &mut dyn Storage, event: &Event) -> Result<(), .change_context(ContractError::SaveEvent) } -pub(crate) fn save_epoch_tally( +pub fn save_epoch_tally( storage: &mut dyn Storage, tally: &EpochTally, ) -> Result<(), ContractError> { @@ -366,7 +363,7 @@ pub(crate) fn save_epoch_tally( .change_context(ContractError::SaveEpochTally) } -pub(crate) fn save_rewards_pool( +pub fn save_rewards_pool( storage: &mut dyn Storage, pool: &RewardsPool, ) -> Result<(), ContractError> { @@ -375,7 +372,42 @@ pub(crate) fn save_rewards_pool( .change_context(ContractError::SaveRewardsPool) } -pub(crate) enum StorageState { +pub fn update_pool_params( + storage: &mut dyn Storage, + pool_id: &PoolId, + updated_params: &ParamsSnapshot, +) -> Result { + POOLS + .update(storage, pool_id.clone(), |pool| match pool { + None => Err(ContractError::RewardsPoolNotFound), + Some(pool) => Ok(RewardsPool { + id: pool_id.to_owned(), + balance: pool.balance, + params: updated_params.to_owned(), + }), + }) + .change_context(ContractError::UpdateRewardsPool) +} + +pub fn pool_exists(storage: &mut dyn Storage, pool_id: &PoolId) -> Result { + POOLS + .may_load(storage, pool_id.to_owned()) + .change_context(ContractError::LoadRewardsPool) + .map(|pool| pool.is_some()) +} + +pub fn current_epoch( + storage: &mut dyn Storage, + pool_id: &PoolId, + cur_block_height: u64, +) -> Result { + Epoch::current( + &load_rewards_pool_params(storage, pool_id.to_owned())?, + cur_block_height, + ) +} + +pub enum StorageState { Existing(T), New(T), } @@ -393,12 +425,16 @@ impl Deref for StorageState { #[cfg(test)] mod test { + use std::collections::HashMap; + + use cosmwasm_std::testing::mock_dependencies; + use cosmwasm_std::{Addr, Uint128, Uint64}; + use router_api::ChainName; + use super::*; use crate::error::ContractError; - use crate::{msg::Params, state::ParamsSnapshot}; - use cosmwasm_std::{testing::mock_dependencies, Addr, Uint128, Uint64}; - use router_api::ChainName; - use std::collections::HashMap; + use crate::msg::Params; + use crate::state::ParamsSnapshot; /// Test that the rewards are /// - distributed evenly to all verifiers that reach quorum @@ -466,12 +502,24 @@ mod test { #[test] fn sub_reward_from_pool() { + let params = ParamsSnapshot { + params: Params { + participation_threshold: (Uint64::new(1), Uint64::new(2)).try_into().unwrap(), + epoch_duration: 100u64.try_into().unwrap(), + rewards_per_epoch: Uint128::from(1000u128).try_into().unwrap(), + }, + created_at: Epoch { + epoch_num: 1, + block_height_started: 1, + }, + }; let pool = RewardsPool { id: PoolId { chain_name: "mock-chain".parse().unwrap(), contract: Addr::unchecked("pool_contract"), }, balance: Uint128::from(100u128), + params, }; let new_pool = pool.sub_reward(Uint128::from(50u128)).unwrap(); assert_eq!(new_pool.balance, Uint128::from(50u128)); @@ -483,41 +531,6 @@ mod test { )); } - #[test] - fn save_and_load_params() { - let mut mock_deps = mock_dependencies(); - let params = ParamsSnapshot { - params: Params { - participation_threshold: (Uint64::new(1), Uint64::new(2)).try_into().unwrap(), - epoch_duration: 100u64.try_into().unwrap(), - rewards_per_epoch: Uint128::from(1000u128).try_into().unwrap(), - }, - created_at: Epoch { - epoch_num: 1, - block_height_started: 1, - }, - }; - // save an initial params, then load it - assert!(save_params(mock_deps.as_mut().storage, ¶ms).is_ok()); - let loaded = load_params(mock_deps.as_ref().storage); - assert_eq!(loaded, params); - - // now store a new params, and check that it was updated - let new_params = ParamsSnapshot { - params: Params { - epoch_duration: 200u64.try_into().unwrap(), - ..params.params - }, - created_at: Epoch { - epoch_num: 2, - block_height_started: 101, - }, - }; - assert!(save_params(mock_deps.as_mut().storage, &new_params).is_ok()); - let loaded = load_params(mock_deps.as_mut().storage); - assert_eq!(loaded, new_params); - } - #[test] fn save_and_load_rewards_watermark() { let mut mock_deps = mock_dependencies(); @@ -700,30 +713,31 @@ mod test { #[test] fn save_and_load_rewards_pool() { + let params = ParamsSnapshot { + params: Params { + participation_threshold: (Uint64::new(1), Uint64::new(2)).try_into().unwrap(), + epoch_duration: 100u64.try_into().unwrap(), + rewards_per_epoch: Uint128::from(1000u128).try_into().unwrap(), + }, + created_at: Epoch { + epoch_num: 1, + block_height_started: 1, + }, + }; let mut mock_deps = mock_dependencies(); let chain_name: ChainName = "mock-chain".parse().unwrap(); - let pool = RewardsPool::new(PoolId::new( - chain_name.clone(), - Addr::unchecked("some contract"), - )); + let pool = RewardsPool { + id: PoolId::new(chain_name.clone(), Addr::unchecked("some contract")), + params, + balance: Uint128::zero(), + }; let res = save_rewards_pool(mock_deps.as_mut().storage, &pool); assert!(res.is_ok()); - let loaded = load_rewards_pool_or_new(mock_deps.as_ref().storage, pool.id.clone()); + let loaded = load_rewards_pool(mock_deps.as_ref().storage, pool.id.clone()); assert!(loaded.is_ok()); assert_eq!(loaded.unwrap(), pool); - - // return new pool when pool is not found - let loaded = load_rewards_pool_or_new( - mock_deps.as_ref().storage, - PoolId { - chain_name: chain_name.clone(), - contract: Addr::unchecked("a different contract"), - }, - ); - assert!(loaded.is_ok()); - assert!(loaded.as_ref().unwrap().balance.is_zero()); } } diff --git a/contracts/router/.cargo/config.toml b/contracts/router/.cargo/config.toml new file mode 100644 index 000000000..af5698e58 --- /dev/null +++ b/contracts/router/.cargo/config.toml @@ -0,0 +1,4 @@ +[alias] +wasm = "build --release --lib --target wasm32-unknown-unknown" +unit-test = "test --lib" +schema = "run --bin schema" diff --git a/contracts/router/Cargo.toml b/contracts/router/Cargo.toml index 49e369b4a..292820849 100644 --- a/contracts/router/Cargo.toml +++ b/contracts/router/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "router" -version = "0.3.2" +version = "1.0.0" rust-version = { workspace = true } -edition = "2021" +edition = { workspace = true } description = "Router contract" exclude = [ @@ -29,12 +29,11 @@ library = [] optimize = """docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/rust-optimizer:0.12.6 + cosmwasm/optimizer:0.16.0 """ [dependencies] -axelar-wasm-std = { workspace = true } -axelar-wasm-std-derive = { workspace = true } +axelar-wasm-std = { workspace = true, features = ["derive"] } cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } cw-storage-plus = { workspace = true } @@ -43,10 +42,12 @@ error-stack = { workspace = true } flagset = { version = "0.4.3", features = ["serde"] } gateway-api = { workspace = true } itertools = { workspace = true } -mockall = "0.11.4" +mockall = { workspace = true } +msgs-derive = { workspace = true } report = { workspace = true } router-api = { workspace = true } serde_json = { workspace = true } +thiserror = { workspace = true } [dev-dependencies] cw-multi-test = "0.15.1" diff --git a/contracts/router/src/bin/schema.rs b/contracts/router/src/bin/schema.rs index ae74ba830..6a15ff65f 100644 --- a/contracts/router/src/bin/schema.rs +++ b/contracts/router/src/bin/schema.rs @@ -1,7 +1,6 @@ use cosmwasm_schema::write_api; -use router_api::msg::{ExecuteMsg, QueryMsg}; - use router::msg::InstantiateMsg; +use router_api::msg::{ExecuteMsg, QueryMsg}; fn main() { write_api! { diff --git a/contracts/router/src/contract.rs b/contracts/router/src/contract.rs index 2f5674c19..9c2f7f688 100644 --- a/contracts/router/src/contract.rs +++ b/contracts/router/src/contract.rs @@ -1,27 +1,34 @@ +use axelar_wasm_std::{address, killswitch, permission_control, FnExt}; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{to_json_binary, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response}; - +use cosmwasm_std::{ + to_json_binary, Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response, Storage, +}; +use router_api::error::Error; use router_api::msg::{ExecuteMsg, QueryMsg}; +use crate::contract::migrations::v0_3_3; use crate::events::RouterInstantiated; use crate::msg::InstantiateMsg; -use crate::state::{Config, RouterStore, Store}; +use crate::state; +use crate::state::{load_chain_by_gateway, load_config, Config}; mod execute; +mod migrations; mod query; -const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); -const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); - +pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); +pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); #[cfg_attr(not(feature = "library"), entry_point)] pub fn migrate( deps: DepsMut, _env: Env, _msg: Empty, -) -> Result { - // any version checks should be done before here +) -> Result { + v0_3_3::migrate(deps.storage)?; + // this needs to be the last thing to do during migration, + // because previous migration steps should check the old version cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; Ok(Response::default()) @@ -33,22 +40,22 @@ pub fn instantiate( _env: Env, _info: MessageInfo, msg: InstantiateMsg, -) -> Result { +) -> Result { cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - let admin = deps.api.addr_validate(&msg.admin_address)?; - let governance = deps.api.addr_validate(&msg.governance_address)?; - let nexus_gateway = deps.api.addr_validate(&msg.nexus_gateway)?; + let admin = address::validate_cosmwasm_address(deps.api, &msg.admin_address)?; + let governance = address::validate_cosmwasm_address(deps.api, &msg.governance_address)?; + let nexus_gateway = address::validate_cosmwasm_address(deps.api, &msg.nexus_gateway)?; + + permission_control::set_admin(deps.storage, &admin)?; + permission_control::set_governance(deps.storage, &governance)?; let config = Config { - admin: admin.clone(), - governance: governance.clone(), nexus_gateway: nexus_gateway.clone(), }; - RouterStore::new(deps.storage) - .save_config(config) - .expect("must save the config"); + state::save_config(deps.storage, &config)?; + killswitch::init(deps.storage, killswitch::State::Disengaged)?; Ok(Response::new().add_event( RouterInstantiated { @@ -66,57 +73,51 @@ pub fn execute( _env: Env, info: MessageInfo, msg: ExecuteMsg, -) -> Result { - let contract = Contract::new(RouterStore::new(deps.storage)); - - match msg { +) -> Result { + match msg.ensure_permissions( + deps.storage, + &info.sender, + find_gateway_address(&info.sender), + )? { ExecuteMsg::RegisterChain { chain, gateway_address, msg_id_format, } => { - execute::require_governance(&deps, info)?; - let gateway_address = deps.api.addr_validate(&gateway_address)?; - execute::register_chain(deps, chain, gateway_address, msg_id_format) + let gateway_address = address::validate_cosmwasm_address(deps.api, &gateway_address)?; + execute::register_chain(deps.storage, chain, gateway_address, msg_id_format) } ExecuteMsg::UpgradeGateway { chain, contract_address, } => { - execute::require_governance(&deps, info)?; - let contract_address = deps.api.addr_validate(&contract_address)?; - execute::upgrade_gateway(deps, chain, contract_address) + let contract_address = address::validate_cosmwasm_address(deps.api, &contract_address)?; + execute::upgrade_gateway(deps.storage, chain, contract_address) } - ExecuteMsg::FreezeChain { chain, direction } => { - execute::require_admin(&deps, info)?; - execute::freeze_chain(deps, chain, direction) + ExecuteMsg::FreezeChains { chains } => execute::freeze_chains(deps.storage, chains), + ExecuteMsg::UnfreezeChains { chains } => execute::unfreeze_chains(deps.storage, chains), + ExecuteMsg::RouteMessages(msgs) => { + Ok(execute::route_messages(deps.storage, info.sender, msgs)?) } - ExecuteMsg::UnfreezeChain { chain, direction } => { - execute::require_admin(&deps, info)?; - execute::unfreeze_chain(deps, chain, direction) - } - ExecuteMsg::RouteMessages(msgs) => Ok(contract.route_messages(info.sender, msgs)?), - } - .map_err(axelar_wasm_std::ContractError::from) + ExecuteMsg::DisableRouting => execute::disable_routing(deps.storage), + ExecuteMsg::EnableRouting => execute::enable_routing(deps.storage), + }? + .then(Ok) } -struct Contract -where - S: Store, -{ - store: S, - #[allow(unused)] - config: Config, -} - -impl Contract -where - S: Store, -{ - pub fn new(store: S) -> Self { - let config = store.load_config().expect("config must be loaded"); - - Self { store, config } +fn find_gateway_address( + sender: &Addr, +) -> impl FnOnce(&dyn Storage, &ExecuteMsg) -> error_stack::Result + '_ { + move |storage, _| { + let nexus_gateway = load_config(storage)?.nexus_gateway; + if nexus_gateway == sender { + Ok(nexus_gateway) + } else { + load_chain_by_gateway(storage, sender)? + .gateway + .address + .then(Ok) + } } } @@ -125,33 +126,38 @@ pub fn query( deps: Deps, _env: Env, msg: QueryMsg, -) -> Result { +) -> Result { match msg { - QueryMsg::GetChainInfo(chain) => to_json_binary(&query::get_chain_info(deps, chain)?), + QueryMsg::ChainInfo(chain) => to_json_binary(&query::chain_info(deps.storage, chain)?), QueryMsg::Chains { start_after, limit } => { to_json_binary(&query::chains(deps, start_after, limit)?) } + QueryMsg::IsEnabled => to_json_binary(&killswitch::is_contract_active(deps.storage)), } - .map_err(axelar_wasm_std::ContractError::from) + .map_err(axelar_wasm_std::error::ContractError::from) } #[cfg(test)] mod test { - use std::{collections::HashMap, str::FromStr}; - - use crate::state::CONFIG; - - use super::*; - - use axelar_wasm_std::msg_id::tx_hash_event_index::HexTxHashAndEventIndex; - use cosmwasm_std::{ - testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, - Addr, CosmosMsg, Empty, OwnedDeps, WasmMsg, + use std::collections::HashMap; + use std::str::FromStr; + + use axelar_wasm_std::err_contains; + use axelar_wasm_std::error::ContractError; + use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; + use cosmwasm_std::testing::{ + mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage, }; + use cosmwasm_std::{from_json, Addr, CosmosMsg, Empty, OwnedDeps, WasmMsg}; + use permission_control::Permission; + use router_api::error::Error; use router_api::{ - error::Error, ChainName, CrossChainId, GatewayDirection, Message, CHAIN_NAME_DELIMITER, + ChainEndpoint, ChainName, CrossChainId, GatewayDirection, Message, FIELD_DELIMITER, }; + use super::*; + use crate::events; + const ADMIN_ADDRESS: &str = "admin"; const GOVERNANCE_ADDRESS: &str = "governance"; const NEXUS_GATEWAY_ADDRESS: &str = "nexus_gateway"; @@ -160,12 +166,17 @@ mod test { fn setup() -> OwnedDeps { let mut deps = mock_dependencies(); - let config = Config { - admin: Addr::unchecked(ADMIN_ADDRESS), - governance: Addr::unchecked(GOVERNANCE_ADDRESS), - nexus_gateway: Addr::unchecked(NEXUS_GATEWAY_ADDRESS), - }; - CONFIG.save(deps.as_mut().storage, &config).unwrap(); + instantiate( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + InstantiateMsg { + admin_address: ADMIN_ADDRESS.to_string(), + governance_address: GOVERNANCE_ADDRESS.to_string(), + nexus_gateway: NEXUS_GATEWAY_ADDRESS.to_string(), + }, + ) + .unwrap(); deps } @@ -196,7 +207,7 @@ mod test { .unwrap(); } - #[allow(clippy::arithmetic_side_effects)] + #[allow(clippy::arithmetic_side_effects, clippy::cast_possible_truncation)] fn generate_messages( src_chain: &Chain, dest_chain: &Chain, @@ -212,10 +223,7 @@ mod test { } .to_string(); msgs.push(Message { - cc_id: CrossChainId { - id: id.parse().unwrap(), - chain: src_chain.chain_name.clone(), - }, + cc_id: CrossChainId::new(src_chain.chain_name.clone(), id).unwrap(), destination_address: "idc".parse().unwrap(), destination_chain: dest_chain.chain_name.clone(), source_address: "idc".parse().unwrap(), @@ -225,20 +233,23 @@ mod test { msgs } - pub fn assert_contract_err_strings_equal( - actual: impl Into, - expected: impl Into, + pub fn assert_contract_err_string_contains( + actual: impl Into, + expected: impl Into, ) { - assert_eq!(actual.into().to_string(), expected.into().to_string()); + assert!(actual + .into() + .to_string() + .contains(&expected.into().to_string())); } pub fn assert_messages_in_cosmos_msg( contract_addr: String, messages: Vec, - cosmos_msg: CosmosMsg, + cosmos_msg: &CosmosMsg, ) { assert_eq!( - CosmosMsg::Wasm(WasmMsg::Execute { + &CosmosMsg::Wasm(WasmMsg::Execute { contract_addr, msg: to_json_binary(&gateway_api::msg::ExecuteMsg::RouteMessages(messages,)) .unwrap(), @@ -248,17 +259,6 @@ mod test { ); } - #[test] - fn migrate_sets_contract_version() { - let mut deps = mock_dependencies(); - - migrate(deps.as_mut(), mock_env(), Empty {}).unwrap(); - - let contract_version = cw2::get_contract_version(deps.as_mut().storage).unwrap(); - assert_eq!(contract_version.contract, "router"); - assert_eq!(contract_version.version, CONTRACT_VERSION); - } - #[test] fn successful_routing() { let mut deps = setup(); @@ -283,7 +283,7 @@ mod test { assert_messages_in_cosmos_msg( polygon.gateway.to_string(), messages.clone(), - res.messages[0].msg.clone(), + &res.messages[0].msg, ); // try to route twice @@ -316,7 +316,58 @@ mod test { ) .unwrap_err(); - assert_contract_err_strings_equal(err, Error::WrongSourceChain); + assert_contract_err_string_contains(err, Error::WrongSourceChain); + } + + #[test] + fn amplifier_messages_must_have_lower_case() { + let mut deps = setup(); + let eth = make_chain("ethereum"); + let polygon = make_chain("polygon"); + + register_chain(deps.as_mut(), ð); + register_chain(deps.as_mut(), &polygon); + + let mut messages = generate_messages(ð, &polygon, &mut 0, 1); + messages + .iter_mut() + .for_each(|msg| msg.cc_id.source_chain = "Ethereum".parse().unwrap()); + + let result = execute( + deps.as_mut(), + mock_env(), + mock_info(eth.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(messages), + ) + .unwrap_err(); + assert!(err_contains!(result.report, Error, Error::WrongSourceChain)); + } + + #[test] + fn nexus_messages_can_have_upper_case() { + let mut deps = setup(); + let eth = make_chain("ethereum"); + let polygon = make_chain("polygon"); + + register_chain(deps.as_mut(), &polygon); + + let mut messages = generate_messages(ð, &polygon, &mut 0, 1); + messages + .iter_mut() + .for_each(|msg| msg.cc_id.source_chain = "Ethereum".parse().unwrap()); + + let result = execute( + deps.as_mut(), + mock_env(), + mock_info(NEXUS_GATEWAY_ADDRESS, &[]), + ExecuteMsg::RouteMessages(messages.clone()), + ); + assert!(result.is_ok()); + assert_messages_in_cosmos_msg( + polygon.gateway.to_string(), + messages, + &result.unwrap().messages[0].msg, + ); } #[test] @@ -375,9 +426,9 @@ mod test { .unwrap() .clone() .into_iter() - .filter(|m| m.cc_id.chain == s.chain_name) + .filter(|m| m.cc_id.source_chain == s.chain_name) .collect::>(), - res.messages[i].msg.clone(), + &res.messages[i].msg, ); } } @@ -399,7 +450,13 @@ mod test { }, ) .unwrap_err(); - assert_contract_err_strings_equal(err, Error::Unauthorized); + assert_contract_err_string_contains( + err, + permission_control::Error::PermissionDenied { + expected: Permission::Governance.into(), + actual: Permission::NoPrivilege.into(), + }, + ); let err = execute( deps.as_mut(), @@ -412,7 +469,13 @@ mod test { }, ) .unwrap_err(); - assert_contract_err_strings_equal(err, Error::Unauthorized); + assert_contract_err_string_contains( + err, + permission_control::Error::PermissionDenied { + expected: Permission::Governance.into(), + actual: Permission::Admin.into(), + }, + ); let res = execute( deps.as_mut(), @@ -430,33 +493,45 @@ mod test { deps.as_mut(), mock_env(), mock_info(UNAUTHORIZED_ADDRESS, &[]), - ExecuteMsg::FreezeChain { - chain: chain.chain_name.clone(), - direction: GatewayDirection::Bidirectional, + ExecuteMsg::FreezeChains { + chains: HashMap::from([( + chain.chain_name.clone(), + GatewayDirection::Bidirectional, + )]), }, ) .unwrap_err(); - assert_contract_err_strings_equal(err, Error::Unauthorized); - let err = execute( + assert_contract_err_string_contains( + err, + permission_control::Error::PermissionDenied { + expected: Permission::Elevated.into(), + actual: Permission::NoPrivilege.into(), + }, + ); + + assert!(execute( deps.as_mut(), mock_env(), mock_info(GOVERNANCE_ADDRESS, &[]), - ExecuteMsg::FreezeChain { - chain: chain.chain_name.clone(), - direction: GatewayDirection::Bidirectional, + ExecuteMsg::FreezeChains { + chains: HashMap::from([( + chain.chain_name.clone(), + GatewayDirection::Bidirectional, + )]), }, ) - .unwrap_err(); - assert_contract_err_strings_equal(err, Error::Unauthorized); + .is_ok()); let res = execute( deps.as_mut(), mock_env(), mock_info(ADMIN_ADDRESS, &[]), - ExecuteMsg::FreezeChain { - chain: chain.chain_name.clone(), - direction: GatewayDirection::Bidirectional, + ExecuteMsg::FreezeChains { + chains: HashMap::from([( + chain.chain_name.clone(), + GatewayDirection::Bidirectional, + )]), }, ); assert!(res.is_ok()); @@ -465,33 +540,35 @@ mod test { deps.as_mut(), mock_env(), mock_info(UNAUTHORIZED_ADDRESS, &[]), - ExecuteMsg::FreezeChain { - chain: chain.chain_name.clone(), - direction: GatewayDirection::None, + ExecuteMsg::FreezeChains { + chains: HashMap::from([(chain.chain_name.clone(), GatewayDirection::None)]), }, ) .unwrap_err(); - assert_contract_err_strings_equal(err, Error::Unauthorized); + assert_contract_err_string_contains( + err, + permission_control::Error::PermissionDenied { + expected: Permission::Elevated.into(), + actual: Permission::NoPrivilege.into(), + }, + ); - let err = execute( + assert!(execute( deps.as_mut(), mock_env(), mock_info(GOVERNANCE_ADDRESS, &[]), - ExecuteMsg::FreezeChain { - chain: chain.chain_name.clone(), - direction: GatewayDirection::None, + ExecuteMsg::FreezeChains { + chains: HashMap::from([(chain.chain_name.clone(), GatewayDirection::None)]), }, ) - .unwrap_err(); - assert_contract_err_strings_equal(err, Error::Unauthorized); + .is_ok()); let res = execute( deps.as_mut(), mock_env(), mock_info(ADMIN_ADDRESS, &[]), - ExecuteMsg::FreezeChain { - chain: chain.chain_name.clone(), - direction: GatewayDirection::None, + ExecuteMsg::FreezeChains { + chains: HashMap::from([(chain.chain_name.clone(), GatewayDirection::None)]), }, ); assert!(res.is_ok()); @@ -509,7 +586,13 @@ mod test { }, ) .unwrap_err(); - assert_contract_err_strings_equal(err, Error::Unauthorized); + assert_contract_err_string_contains( + err, + permission_control::Error::PermissionDenied { + expected: Permission::Governance.into(), + actual: Permission::NoPrivilege.into(), + }, + ); let err = execute( deps.as_mut(), @@ -524,7 +607,13 @@ mod test { }, ) .unwrap_err(); - assert_contract_err_strings_equal(err, Error::Unauthorized); + assert_contract_err_string_contains( + err, + permission_control::Error::PermissionDenied { + expected: Permission::Governance.into(), + actual: Permission::Admin.into(), + }, + ); let res = execute( deps.as_mut(), @@ -549,7 +638,7 @@ mod test { register_chain(deps.as_mut(), ð); register_chain(deps.as_mut(), &polygon); - let new_gateway = Addr::unchecked("new_gateway"); + let new_gateway = Addr::unchecked("new-gateway"); let _ = execute( deps.as_mut(), @@ -575,7 +664,7 @@ mod test { assert_messages_in_cosmos_msg( new_gateway.to_string(), messages.clone(), - res.messages[0].msg.clone(), + &res.messages[0].msg, ); } @@ -587,7 +676,7 @@ mod test { register_chain(deps.as_mut(), ð); register_chain(deps.as_mut(), &polygon); - let new_gateway = Addr::unchecked("new_gateway"); + let new_gateway = Addr::unchecked("new-gateway"); let _ = execute( deps.as_mut(), @@ -608,7 +697,7 @@ mod test { ExecuteMsg::RouteMessages(messages.clone()), ) .unwrap_err(); - assert_contract_err_strings_equal(err, Error::GatewayNotRegistered); + assert_contract_err_string_contains(err, Error::GatewayNotRegistered); let res = execute( deps.as_mut(), @@ -622,7 +711,7 @@ mod test { assert_messages_in_cosmos_msg( eth.gateway.to_string(), messages.clone(), - res.messages[0].msg.clone(), + &res.messages[0].msg, ); } @@ -640,7 +729,12 @@ mod test { ExecuteMsg::RouteMessages(vec![message.clone()]), ) .unwrap_err(); - assert_contract_err_strings_equal(err, Error::GatewayNotRegistered); + assert_contract_err_string_contains( + err, + permission_control::Error::WhitelistNotFound { + sender: eth.gateway.clone(), + }, + ); register_chain(deps.as_mut(), ð); register_chain(deps.as_mut(), &polygon); @@ -674,7 +768,7 @@ mod test { }, ) .unwrap_err(); - assert_contract_err_strings_equal(err, Error::ChainAlreadyExists); + assert_contract_err_string_contains(err, Error::ChainAlreadyExists); // case insensitive let err = execute( @@ -691,17 +785,17 @@ mod test { }, ) .unwrap_err(); - assert_contract_err_strings_equal(err, Error::ChainAlreadyExists); + assert_contract_err_string_contains(err, Error::ChainAlreadyExists); } #[test] fn invalid_chain_name() { - assert_contract_err_strings_equal( - ChainName::from_str(format!("bad{}", CHAIN_NAME_DELIMITER).as_str()).unwrap_err(), + assert_contract_err_string_contains( + ChainName::from_str(format!("bad{}", FIELD_DELIMITER).as_str()).unwrap_err(), Error::InvalidChainName, ); - assert_contract_err_strings_equal( + assert_contract_err_string_contains( ChainName::from_str("").unwrap_err(), Error::InvalidChainName, ); @@ -725,7 +819,7 @@ mod test { }, ) .unwrap_err(); - assert_contract_err_strings_equal(err, Error::GatewayAlreadyRegistered); + assert_contract_err_string_contains(err, Error::GatewayAlreadyRegistered); register_chain(deps.as_mut(), &polygon); let err = execute( @@ -739,7 +833,7 @@ mod test { ) .unwrap_err(); - assert_contract_err_strings_equal(err, Error::GatewayAlreadyRegistered); + assert_contract_err_string_contains(err, Error::GatewayAlreadyRegistered); } #[test] @@ -754,9 +848,8 @@ mod test { deps.as_mut(), mock_env(), mock_info(ADMIN_ADDRESS, &[]), - ExecuteMsg::FreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Incoming, + ExecuteMsg::FreezeChains { + chains: HashMap::from([(polygon.chain_name.clone(), GatewayDirection::Incoming)]), }, ) .unwrap(); @@ -770,7 +863,7 @@ mod test { ExecuteMsg::RouteMessages(vec![message.clone()]), ) .unwrap_err(); - assert_contract_err_strings_equal( + assert_contract_err_string_contains( err, Error::ChainFrozen { chain: polygon.chain_name.clone(), @@ -791,16 +884,15 @@ mod test { assert_messages_in_cosmos_msg( polygon.gateway.to_string(), messages.clone(), - res.messages[0].msg.clone(), + &res.messages[0].msg, ); let res = execute( deps.as_mut(), mock_env(), mock_info(ADMIN_ADDRESS, &[]), - ExecuteMsg::UnfreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Incoming, + ExecuteMsg::UnfreezeChains { + chains: HashMap::from([(polygon.chain_name.clone(), GatewayDirection::Incoming)]), }, ); assert!(res.is_ok()); @@ -828,9 +920,8 @@ mod test { deps.as_mut(), mock_env(), mock_info(ADMIN_ADDRESS, &[]), - ExecuteMsg::FreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Outgoing, + ExecuteMsg::FreezeChains { + chains: HashMap::from([(polygon.chain_name.clone(), GatewayDirection::Outgoing)]), }, ); assert!(res.is_ok()); @@ -844,7 +935,7 @@ mod test { ExecuteMsg::RouteMessages(messages.clone()), ) .unwrap_err(); - assert_contract_err_strings_equal( + assert_contract_err_string_contains( err, Error::ChainFrozen { chain: polygon.chain_name.clone(), @@ -855,9 +946,8 @@ mod test { deps.as_mut(), mock_env(), mock_info(ADMIN_ADDRESS, &[]), - ExecuteMsg::UnfreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Outgoing, + ExecuteMsg::UnfreezeChains { + chains: HashMap::from([(polygon.chain_name.clone(), GatewayDirection::Outgoing)]), }, ); assert!(res.is_ok()); @@ -874,7 +964,7 @@ mod test { assert_messages_in_cosmos_msg( polygon.gateway.to_string(), messages.clone(), - res.messages[0].msg.clone(), + &res.messages[0].msg, ); } @@ -902,9 +992,11 @@ mod test { deps.as_mut(), mock_env(), mock_info(ADMIN_ADDRESS, &[]), - ExecuteMsg::FreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Bidirectional, + ExecuteMsg::FreezeChains { + chains: HashMap::from([( + polygon.chain_name.clone(), + GatewayDirection::Bidirectional, + )]), }, ); assert!(res.is_ok()); @@ -918,7 +1010,7 @@ mod test { ) .unwrap_err(); // can't route to frozen chain - assert_contract_err_strings_equal( + assert_contract_err_string_contains( err, Error::ChainFrozen { chain: polygon.chain_name.clone(), @@ -934,7 +1026,7 @@ mod test { ExecuteMsg::RouteMessages(vec![message.clone()]), ) .unwrap_err(); - assert_contract_err_strings_equal( + assert_contract_err_string_contains( err, Error::ChainFrozen { chain: polygon.chain_name.clone(), @@ -946,9 +1038,11 @@ mod test { deps.as_mut(), mock_env(), mock_info(ADMIN_ADDRESS, &[]), - ExecuteMsg::UnfreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Bidirectional, + ExecuteMsg::UnfreezeChains { + chains: HashMap::from([( + polygon.chain_name.clone(), + GatewayDirection::Bidirectional, + )]), }, ) .unwrap(); @@ -974,6 +1068,114 @@ mod test { assert!(res.is_ok()); } + #[test] + fn freeze_and_unfreeze_all_chains() { + let eth = make_chain("ethereum"); + let polygon = make_chain("polygon"); + let test_case = HashMap::from([ + (eth.chain_name.clone(), GatewayDirection::Bidirectional), + (polygon.chain_name.clone(), GatewayDirection::Bidirectional), + ]); + + let mut deps = setup(); + + register_chain(deps.as_mut(), ð); + register_chain(deps.as_mut(), &polygon); + + let chains = query( + deps.as_ref(), + mock_env(), + QueryMsg::Chains { + start_after: None, + limit: None, + }, + ) + .unwrap() + .then(|chains| from_json::>(&chains)) + .unwrap(); + + for chain in chains.iter() { + assert!(!chain.incoming_frozen() && !chain.outgoing_frozen()) + } + + type Check = fn(&Result) -> bool; // clippy complains without the alias about complex types + + // try sender without permission + let permission_control: Vec<(&str, Check)> = vec![ + (UNAUTHORIZED_ADDRESS, Result::is_err), + (GOVERNANCE_ADDRESS, Result::is_ok), + (ADMIN_ADDRESS, Result::is_ok), + ]; + + for permission_case in permission_control.iter() { + let (sender, result_check) = permission_case; + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(sender, &[]), + ExecuteMsg::FreezeChains { + chains: test_case.clone(), + }, + ); + assert!(result_check(&res)); + } + + let chains = query( + deps.as_ref(), + mock_env(), + QueryMsg::Chains { + start_after: None, + limit: None, + }, + ) + .unwrap() + .then(|chains| from_json::>(&chains)) + .unwrap(); + + for chain in chains.iter() { + assert!(chain.incoming_frozen() && chain.outgoing_frozen()) + } + + // try sender without permission + let permission_control: Vec<(&str, Check)> = vec![ + (UNAUTHORIZED_ADDRESS, Result::is_err), + (GOVERNANCE_ADDRESS, Result::is_ok), + (ADMIN_ADDRESS, Result::is_ok), + ]; + + for permission_case in permission_control.iter() { + let (sender, result_check) = permission_case; + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(sender, &[]), + ExecuteMsg::UnfreezeChains { + chains: HashMap::from([ + (eth.chain_name.clone(), GatewayDirection::Bidirectional), + (polygon.chain_name.clone(), GatewayDirection::Bidirectional), + ]), + }, + ); + assert!(result_check(&res)); + } + + let chains = query( + deps.as_ref(), + mock_env(), + QueryMsg::Chains { + start_after: None, + limit: None, + }, + ) + .unwrap() + .then(|chains| from_json::>(&chains)) + .unwrap(); + + for chain in chains.iter() { + assert!(!chain.incoming_frozen() && !chain.outgoing_frozen()) + } + } + #[test] fn unfreeze_incoming() { let mut deps = setup(); @@ -986,9 +1188,11 @@ mod test { deps.as_mut(), mock_env(), mock_info(ADMIN_ADDRESS, &[]), - ExecuteMsg::FreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Bidirectional, + ExecuteMsg::FreezeChains { + chains: HashMap::from([( + polygon.chain_name.clone(), + GatewayDirection::Bidirectional, + )]), }, ); assert!(res.is_ok()); @@ -1000,9 +1204,8 @@ mod test { deps.as_mut(), mock_env(), mock_info(ADMIN_ADDRESS, &[]), - ExecuteMsg::UnfreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Incoming, + ExecuteMsg::UnfreezeChains { + chains: HashMap::from([(polygon.chain_name.clone(), GatewayDirection::Incoming)]), }, ) .unwrap(); @@ -1026,7 +1229,7 @@ mod test { ) .unwrap_err(); // can't route to the chain - assert_contract_err_strings_equal( + assert_contract_err_string_contains( err, Error::ChainFrozen { chain: polygon.chain_name.clone(), @@ -1046,9 +1249,11 @@ mod test { deps.as_mut(), mock_env(), mock_info(ADMIN_ADDRESS, &[]), - ExecuteMsg::FreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Bidirectional, + ExecuteMsg::FreezeChains { + chains: HashMap::from([( + polygon.chain_name.clone(), + GatewayDirection::Bidirectional, + )]), }, ); assert!(res.is_ok()); @@ -1060,9 +1265,8 @@ mod test { deps.as_mut(), mock_env(), mock_info(ADMIN_ADDRESS, &[]), - ExecuteMsg::UnfreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Outgoing, + ExecuteMsg::UnfreezeChains { + chains: HashMap::from([(polygon.chain_name.clone(), GatewayDirection::Outgoing)]), }, ) .unwrap(); @@ -1076,7 +1280,7 @@ mod test { ExecuteMsg::RouteMessages(vec![message.clone()]), ) .unwrap_err(); - assert_contract_err_strings_equal( + assert_contract_err_string_contains( err, Error::ChainFrozen { chain: polygon.chain_name.clone(), @@ -1106,9 +1310,8 @@ mod test { deps.as_mut(), mock_env(), mock_info(ADMIN_ADDRESS, &[]), - ExecuteMsg::FreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Incoming, + ExecuteMsg::FreezeChains { + chains: HashMap::from([(polygon.chain_name.clone(), GatewayDirection::Incoming)]), }, ) .unwrap(); @@ -1117,9 +1320,8 @@ mod test { deps.as_mut(), mock_env(), mock_info(ADMIN_ADDRESS, &[]), - ExecuteMsg::FreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Outgoing, + ExecuteMsg::FreezeChains { + chains: HashMap::from([(polygon.chain_name.clone(), GatewayDirection::Outgoing)]), }, ) .unwrap(); @@ -1134,7 +1336,7 @@ mod test { ExecuteMsg::RouteMessages(vec![message.clone()]), ) .unwrap_err(); - assert_contract_err_strings_equal( + assert_contract_err_string_contains( err, Error::ChainFrozen { chain: polygon.chain_name.clone(), @@ -1150,7 +1352,7 @@ mod test { ExecuteMsg::RouteMessages(vec![message.clone()]), ) .unwrap_err(); - assert_contract_err_strings_equal( + assert_contract_err_string_contains( err, Error::ChainFrozen { chain: polygon.chain_name.clone(), @@ -1170,9 +1372,8 @@ mod test { deps.as_mut(), mock_env(), mock_info(ADMIN_ADDRESS, &[]), - ExecuteMsg::FreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Outgoing, + ExecuteMsg::FreezeChains { + chains: HashMap::from([(polygon.chain_name.clone(), GatewayDirection::Outgoing)]), }, ) .unwrap(); @@ -1181,9 +1382,8 @@ mod test { deps.as_mut(), mock_env(), mock_info(ADMIN_ADDRESS, &[]), - ExecuteMsg::FreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Incoming, + ExecuteMsg::FreezeChains { + chains: HashMap::from([(polygon.chain_name.clone(), GatewayDirection::Incoming)]), }, ) .unwrap(); @@ -1198,7 +1398,7 @@ mod test { ExecuteMsg::RouteMessages(vec![message.clone()]), ) .unwrap_err(); - assert_contract_err_strings_equal( + assert_contract_err_string_contains( err, Error::ChainFrozen { chain: polygon.chain_name.clone(), @@ -1214,7 +1414,7 @@ mod test { ExecuteMsg::RouteMessages(vec![message.clone()]), ) .unwrap_err(); - assert_contract_err_strings_equal( + assert_contract_err_string_contains( err, Error::ChainFrozen { chain: polygon.chain_name.clone(), @@ -1234,9 +1434,11 @@ mod test { deps.as_mut(), mock_env(), mock_info(ADMIN_ADDRESS, &[]), - ExecuteMsg::FreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Bidirectional, + ExecuteMsg::FreezeChains { + chains: HashMap::from([( + polygon.chain_name.clone(), + GatewayDirection::Bidirectional, + )]), }, ); assert!(res.is_ok()); @@ -1246,9 +1448,8 @@ mod test { deps.as_mut(), mock_env(), mock_info(ADMIN_ADDRESS, &[]), - ExecuteMsg::UnfreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Incoming, + ExecuteMsg::UnfreezeChains { + chains: HashMap::from([(polygon.chain_name.clone(), GatewayDirection::Incoming)]), }, ) .unwrap(); @@ -1258,9 +1459,8 @@ mod test { deps.as_mut(), mock_env(), mock_info(ADMIN_ADDRESS, &[]), - ExecuteMsg::UnfreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Outgoing, + ExecuteMsg::UnfreezeChains { + chains: HashMap::from([(polygon.chain_name.clone(), GatewayDirection::Outgoing)]), }, ) .unwrap(); @@ -1299,9 +1499,11 @@ mod test { deps.as_mut(), mock_env(), mock_info(ADMIN_ADDRESS, &[]), - ExecuteMsg::FreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Bidirectional, + ExecuteMsg::FreezeChains { + chains: HashMap::from([( + polygon.chain_name.clone(), + GatewayDirection::Bidirectional, + )]), }, ); assert!(res.is_ok()); @@ -1311,9 +1513,8 @@ mod test { deps.as_mut(), mock_env(), mock_info(ADMIN_ADDRESS, &[]), - ExecuteMsg::UnfreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Outgoing, + ExecuteMsg::UnfreezeChains { + chains: HashMap::from([(polygon.chain_name.clone(), GatewayDirection::Outgoing)]), }, ) .unwrap(); @@ -1323,9 +1524,8 @@ mod test { deps.as_mut(), mock_env(), mock_info(ADMIN_ADDRESS, &[]), - ExecuteMsg::UnfreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Incoming, + ExecuteMsg::UnfreezeChains { + chains: HashMap::from([(polygon.chain_name.clone(), GatewayDirection::Incoming)]), }, ) .unwrap(); @@ -1364,9 +1564,11 @@ mod test { deps.as_mut(), mock_env(), mock_info(ADMIN_ADDRESS, &[]), - ExecuteMsg::FreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Bidirectional, + ExecuteMsg::FreezeChains { + chains: HashMap::from([( + polygon.chain_name.clone(), + GatewayDirection::Bidirectional, + )]), }, ); assert!(res.is_ok()); @@ -1376,9 +1578,8 @@ mod test { deps.as_mut(), mock_env(), mock_info(ADMIN_ADDRESS, &[]), - ExecuteMsg::UnfreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::None, + ExecuteMsg::UnfreezeChains { + chains: HashMap::from([(polygon.chain_name.clone(), GatewayDirection::None)]), }, ) .unwrap(); @@ -1392,7 +1593,7 @@ mod test { ExecuteMsg::RouteMessages(vec![message.clone()]), ) .unwrap_err(); - assert_contract_err_strings_equal( + assert_contract_err_string_contains( err, Error::ChainFrozen { chain: polygon.chain_name.clone(), @@ -1408,11 +1609,196 @@ mod test { ExecuteMsg::RouteMessages(vec![message.clone()]), ) .unwrap_err(); - assert_contract_err_strings_equal( + assert_contract_err_string_contains( err, Error::ChainFrozen { chain: polygon.chain_name.clone(), }, ); } + + #[test] + fn disable_enable_router() { + let mut deps = setup(); + let eth = make_chain("ethereum"); + let polygon = make_chain("polygon"); + register_chain(deps.as_mut(), ð); + register_chain(deps.as_mut(), &polygon); + + let nonce = &mut 0; + let messages = &generate_messages(ð, &polygon, nonce, 1); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(eth.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(messages.clone()), + ); + + assert!(res.is_ok()); + + let _ = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::DisableRouting {}, + ) + .unwrap(); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(eth.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(messages.clone()), + ); + assert!(res.is_err()); + assert_contract_err_string_contains(res.unwrap_err(), Error::RoutingDisabled); + + let _ = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::EnableRouting {}, + ) + .unwrap(); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(eth.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(messages.clone()), + ); + + assert!(res.is_ok()); + } + + #[test] + fn ensure_correct_permissions_enable_disable_routing() { + let mut deps = setup(); + assert!(execute( + deps.as_mut(), + mock_env(), + mock_info(UNAUTHORIZED_ADDRESS, &[]), + ExecuteMsg::EnableRouting {}, + ) + .is_err()); + assert!(execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::EnableRouting {}, + ) + .is_ok()); + assert!(execute( + deps.as_mut(), + mock_env(), + mock_info(GOVERNANCE_ADDRESS, &[]), + ExecuteMsg::EnableRouting {}, + ) + .is_ok()); + + assert!(execute( + deps.as_mut(), + mock_env(), + mock_info(UNAUTHORIZED_ADDRESS, &[]), + ExecuteMsg::DisableRouting {}, + ) + .is_err()); + assert!(execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::DisableRouting {}, + ) + .is_ok()); + assert!(execute( + deps.as_mut(), + mock_env(), + mock_info(GOVERNANCE_ADDRESS, &[]), + ExecuteMsg::DisableRouting {}, + ) + .is_ok()); + } + + #[test] + fn events_are_emitted_enable_disable_routing() { + let mut deps = setup(); + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::DisableRouting {}, + ) + .unwrap(); + + assert!(res.events.len() == 1); + assert!(res.events.contains(&events::RoutingDisabled.into())); + + // don't emit event if already disabled + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::DisableRouting {}, + ) + .unwrap(); + + assert!(res.events.is_empty()); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::EnableRouting {}, + ) + .unwrap(); + + assert!(res.events.len() == 1); + assert!(res.events.contains(&events::RoutingEnabled.into())); + + // don't emit event if already enabled + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::EnableRouting {}, + ) + .unwrap(); + + assert!(res.events.is_empty()); + } + + #[test] + fn is_enabled() { + let mut deps = mock_dependencies(); + let is_enabled = |deps: Deps| { + from_json::(query(deps, mock_env(), QueryMsg::IsEnabled).unwrap()).unwrap() + }; + assert!(!is_enabled(deps.as_ref())); + + killswitch::init(deps.as_mut().storage, killswitch::State::Engaged).unwrap(); + assert!(!is_enabled(deps.as_ref())); + killswitch::engage(deps.as_mut().storage, events::RoutingDisabled).unwrap(); + assert!(!is_enabled(deps.as_ref())); + killswitch::disengage(deps.as_mut().storage, events::RoutingEnabled).unwrap(); + assert!(is_enabled(deps.as_ref())); + } + + #[test] + fn nexus_can_route_messages() { + let mut deps = setup(); + let eth = make_chain("ethereum"); + let polygon = make_chain("polygon"); + + register_chain(deps.as_mut(), ð); + register_chain(deps.as_mut(), &polygon); + + assert!(execute( + deps.as_mut(), + mock_env(), + mock_info(NEXUS_GATEWAY_ADDRESS, &[]), + ExecuteMsg::RouteMessages(generate_messages(ð, &polygon, &mut 0, 10)), + ) + .is_ok()); + } } diff --git a/contracts/router/src/contract/execute.rs b/contracts/router/src/contract/execute.rs index 0f95d2489..c31016b57 100644 --- a/contracts/router/src/contract/execute.rs +++ b/contracts/router/src/contract/execute.rs @@ -1,31 +1,31 @@ +use std::collections::HashMap; use std::vec; +use axelar_wasm_std::flagset::FlagSet; +use axelar_wasm_std::killswitch; use axelar_wasm_std::msg_id::{self, MessageIdFormat}; -use cosmwasm_std::{to_json_binary, Addr, DepsMut, MessageInfo, Response, StdResult, WasmMsg}; -use error_stack::{report, ResultExt}; +use cosmwasm_std::{to_json_binary, Addr, Event, Response, StdResult, Storage, WasmMsg}; +use error_stack::{ensure, report, ResultExt}; use itertools::Itertools; - -use axelar_wasm_std::flagset::FlagSet; use router_api::error::Error; use router_api::{ChainEndpoint, ChainName, Gateway, GatewayDirection, Message}; use crate::events::{ ChainFrozen, ChainRegistered, ChainUnfrozen, GatewayInfo, GatewayUpgraded, MessageRouted, }; -use crate::state::{chain_endpoints, Store, CONFIG}; - -use super::Contract; +use crate::state::{chain_endpoints, Config}; +use crate::{events, state}; pub fn register_chain( - deps: DepsMut, + storage: &mut dyn Storage, name: ChainName, gateway: Addr, msg_id_format: MessageIdFormat, ) -> Result { - if find_chain_for_gateway(&deps, &gateway)?.is_some() { + if find_chain_for_gateway(storage, &gateway)?.is_some() { return Err(Error::GatewayAlreadyRegistered); } - chain_endpoints().update(deps.storage, name.clone(), |chain| match chain { + chain_endpoints().update(storage, name.clone(), |chain| match chain { Some(_) => Err(Error::ChainAlreadyExists), None => Ok(ChainEndpoint { name: name.clone(), @@ -40,25 +40,24 @@ pub fn register_chain( } pub fn find_chain_for_gateway( - deps: &DepsMut, + storage: &dyn Storage, contract_address: &Addr, ) -> StdResult> { - #[allow(deprecated)] chain_endpoints() .idx .gateway - .find_chain(deps, contract_address) + .load_chain_by_gateway(storage, contract_address) } pub fn upgrade_gateway( - deps: DepsMut, + storage: &mut dyn Storage, chain: ChainName, contract_address: Addr, ) -> Result { - if find_chain_for_gateway(&deps, &contract_address)?.is_some() { + if find_chain_for_gateway(storage, &contract_address)?.is_some() { return Err(Error::GatewayAlreadyRegistered); } - chain_endpoints().update(deps.storage, chain.clone(), |chain| match chain { + chain_endpoints().update(storage, chain.clone(), |chain| match chain { None => Err(Error::ChainNotFound), Some(mut chain) => { chain.gateway.address = contract_address.clone(); @@ -76,63 +75,77 @@ pub fn upgrade_gateway( )) } -pub fn freeze_chain( - deps: DepsMut, +fn freeze_specific_chain( + storage: &mut dyn Storage, chain: ChainName, direction: GatewayDirection, -) -> Result { - chain_endpoints().update(deps.storage, chain.clone(), |chain| match chain { +) -> Result { + chain_endpoints().update(storage, chain.clone(), |chain| match chain { None => Err(Error::ChainNotFound), Some(mut chain) => { *chain.frozen_status |= direction; Ok(chain) } })?; - Ok(Response::new().add_event( - ChainFrozen { - name: chain, - direction, - } - .into(), - )) + + Ok(ChainFrozen { + name: chain, + direction, + }) +} + +pub fn freeze_chains( + storage: &mut dyn Storage, + chains: HashMap, +) -> Result { + let events: Vec<_> = chains + .into_iter() + .map(|(chain, direction)| freeze_specific_chain(storage, chain, direction)) + .map_ok(Event::from) + .try_collect()?; + + Ok(Response::new().add_events(events)) } #[allow(clippy::arithmetic_side_effects)] // flagset operations don't cause under/overflows -pub fn unfreeze_chain( - deps: DepsMut, +fn unfreeze_specific_chain( + storage: &mut dyn Storage, chain: ChainName, direction: GatewayDirection, -) -> Result { - chain_endpoints().update(deps.storage, chain.clone(), |chain| match chain { +) -> Result { + chain_endpoints().update(storage, chain.clone(), |chain| match chain { None => Err(Error::ChainNotFound), Some(mut chain) => { *chain.frozen_status -= direction; Ok(chain) } })?; - Ok(Response::new().add_event( - ChainUnfrozen { - name: chain, - direction, - } - .into(), - )) + + Ok(ChainUnfrozen { + name: chain, + direction, + }) } -pub fn require_admin(deps: &DepsMut, info: MessageInfo) -> Result<(), Error> { - let config = CONFIG.load(deps.storage)?; - if config.admin != info.sender { - return Err(Error::Unauthorized); - } - Ok(()) +pub fn unfreeze_chains( + storage: &mut dyn Storage, + chains: HashMap, +) -> Result { + let events: Vec<_> = chains + .into_iter() + .map(|(chain, direction)| unfreeze_specific_chain(storage, chain, direction)) + .map_ok(Event::from) + .try_collect()?; + + Ok(Response::new().add_events(events)) } -pub fn require_governance(deps: &DepsMut, info: MessageInfo) -> Result<(), Error> { - let config = CONFIG.load(deps.storage)?; - if config.governance != info.sender { - return Err(Error::Unauthorized); - } - Ok(()) +pub fn disable_routing(storage: &mut dyn Storage) -> Result { + killswitch::engage(storage, events::RoutingDisabled).map_err(|err| err.into()) +} + +pub fn enable_routing(storage: &mut dyn Storage) -> Result { + killswitch::disengage(storage, events::RoutingEnabled).map_err(|err| err.into()) } fn verify_msg_ids( @@ -140,113 +153,111 @@ fn verify_msg_ids( expected_format: &MessageIdFormat, ) -> Result<(), error_stack::Report> { msgs.iter() - .try_for_each(|msg| msg_id::verify_msg_id(&msg.cc_id.id, expected_format)) + .try_for_each(|msg| msg_id::verify_msg_id(&msg.cc_id.message_id, expected_format)) .change_context(Error::InvalidMessageId) } -impl Contract -where - S: Store, -{ - fn validate_msgs( - &self, - sender: &Addr, - msgs: Vec, - ) -> error_stack::Result, Error> { - // If sender is the nexus gateway, we cannot validate the source chain - // because the source chain is registered in the core nexus module. - // All messages received from the nexus gateway must adhere to the - // HexTxHashAndEventIndex message ID format. - if sender == self.config.nexus_gateway { - verify_msg_ids(&msgs, &MessageIdFormat::HexTxHashAndEventIndex)?; - return Ok(msgs); - } +fn validate_msgs( + storage: &dyn Storage, + config: Config, + sender: &Addr, + msgs: Vec, +) -> error_stack::Result, Error> { + // If sender is the nexus gateway, we cannot validate the source chain + // because the source chain is registered in the core nexus module. + // All messages received from the nexus gateway must adhere to the + // HexTxHashAndEventIndex message ID format. + if sender == config.nexus_gateway { + verify_msg_ids(&msgs, &MessageIdFormat::HexTxHashAndEventIndex)?; + return Ok(msgs); + } - let source_chain = self - .store - .load_chain_by_gateway(sender)? - .ok_or(Error::GatewayNotRegistered)?; - if source_chain.incoming_frozen() { - return Err(report!(Error::ChainFrozen { - chain: source_chain.name, - })); - } + let source_chain = state::load_chain_by_gateway(storage, sender)?; + if source_chain.incoming_frozen() { + return Err(report!(Error::ChainFrozen { + chain: source_chain.name, + })); + } - if msgs.iter().any(|msg| msg.cc_id.chain != source_chain.name) { - return Err(report!(Error::WrongSourceChain)); - } + if msgs + .iter() + .any(|msg| msg.cc_id.source_chain != source_chain.name) + { + return Err(report!(Error::WrongSourceChain)); + } - verify_msg_ids(&msgs, &source_chain.msg_id_format)?; + verify_msg_ids(&msgs, &source_chain.msg_id_format)?; - Ok(msgs) - } + Ok(msgs) +} - pub fn route_messages( - self, - sender: Addr, - msgs: Vec, - ) -> error_stack::Result { - let msgs = self.validate_msgs(&sender, msgs)?; - - let wasm_msgs = msgs - .iter() - .group_by(|msg| msg.destination_chain.to_owned()) - .into_iter() - .map(|(destination_chain, msgs)| { - let gateway = match self.store.load_chain_by_chain_name(&destination_chain)? { - Some(destination_chain) if destination_chain.outgoing_frozen() => { - return Err(report!(Error::ChainFrozen { - chain: destination_chain.name, - })); - } - Some(destination_chain) => destination_chain.gateway.address, - // messages with unknown destination chains are routed to - // the nexus gateway if the sender is not the nexus gateway - // itself - None if sender != self.config.nexus_gateway => { - self.config.nexus_gateway.clone() - } - _ => return Err(report!(Error::ChainNotFound)), - }; - - Ok(WasmMsg::Execute { - contract_addr: gateway.to_string(), - msg: to_json_binary(&gateway_api::msg::ExecuteMsg::RouteMessages( - msgs.cloned().collect(), - )) - .expect("must serialize message"), - funds: vec![], - }) +pub fn route_messages( + storage: &dyn Storage, + sender: Addr, + msgs: Vec, +) -> error_stack::Result { + ensure!( + killswitch::is_contract_active(storage), + Error::RoutingDisabled + ); + + let config = state::load_config(storage)?; + + let msgs = validate_msgs(storage, config.clone(), &sender, msgs)?; + + let wasm_msgs = msgs + .iter() + .group_by(|msg| msg.destination_chain.to_owned()) + .into_iter() + .map(|(destination_chain, msgs)| { + let gateway = match state::load_chain_by_chain_name(storage, &destination_chain)? { + Some(destination_chain) if destination_chain.outgoing_frozen() => { + return Err(report!(Error::ChainFrozen { + chain: destination_chain.name, + })); + } + Some(destination_chain) => destination_chain.gateway.address, + // messages with unknown destination chains are routed to + // the nexus gateway if the sender is not the nexus gateway + // itself + None if sender != config.nexus_gateway => config.nexus_gateway.clone(), + _ => return Err(report!(Error::ChainNotFound)), + }; + + Ok(WasmMsg::Execute { + contract_addr: gateway.to_string(), + msg: to_json_binary(&gateway_api::msg::ExecuteMsg::RouteMessages( + msgs.cloned().collect(), + )) + .expect("must serialize message"), + funds: vec![], }) - .collect::, _>>()?; + }) + .collect::, _>>()?; - Ok(Response::new() - .add_messages(wasm_msgs) - .add_events(msgs.into_iter().map(|msg| MessageRouted { msg }.into()))) - } + Ok(Response::new() + .add_messages(wasm_msgs) + .add_events(msgs.into_iter().map(|msg| MessageRouted { msg }.into()))) } #[cfg(test)] mod test { - use axelar_wasm_std::msg_id::tx_hash_event_index::HexTxHashAndEventIndex; - use cosmwasm_std::Addr; - use mockall::predicate; - use rand::{Rng, RngCore}; + use std::collections::HashMap; use axelar_wasm_std::flagset::FlagSet; - use cosmwasm_std::testing::mock_dependencies; - use cosmwasm_std::Storage; + use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; + use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; + use cosmwasm_std::{Addr, Storage}; + use rand::{random, RngCore}; use router_api::error::Error; use router_api::{ChainEndpoint, ChainName, CrossChainId, Gateway, GatewayDirection, Message}; + use super::{freeze_chains, unfreeze_chains}; + use crate::contract::execute::route_messages; + use crate::contract::instantiate; use crate::events::{ChainFrozen, ChainUnfrozen}; + use crate::msg::InstantiateMsg; use crate::state::chain_endpoints; - use crate::{ - contract::Contract, - state::{Config, MockStore}, - }; - - use super::{freeze_chain, unfreeze_chain}; fn rand_message(source_chain: ChainName, destination_chain: ChainName) -> Message { let mut bytes = [0; 32]; @@ -254,7 +265,7 @@ mod test { let id = HexTxHashAndEventIndex { tx_hash: bytes, - event_index: rand::thread_rng().gen::(), + event_index: random::(), } .to_string(); @@ -270,10 +281,7 @@ mod test { rand::thread_rng().fill_bytes(&mut payload_hash); Message { - cc_id: CrossChainId { - chain: source_chain, - id: id.parse().unwrap(), - }, + cc_id: CrossChainId::new(source_chain, id).unwrap(), source_address, destination_chain, destination_address, @@ -283,49 +291,50 @@ mod test { #[test] fn route_messages_with_not_registered_source_chain() { - let config = Config { - admin: Addr::unchecked("admin"), - governance: Addr::unchecked("governance"), - nexus_gateway: Addr::unchecked("nexus_gateway"), - }; let sender = Addr::unchecked("sender"); let source_chain: ChainName = "ethereum".parse().unwrap(); let destination_chain = "bitcoin".parse().unwrap(); - let mut store = MockStore::new(); - store - .expect_load_config() - .returning(move || Ok(config.clone())); - store - .expect_load_chain_by_gateway() - .once() - .with(predicate::eq(sender.clone())) - .return_once(|_| Ok(None)); - - let contract = Contract::new(store); + let mut deps = mock_dependencies(); + instantiate( + deps.as_mut(), + mock_env(), + mock_info("admin", &[]), + InstantiateMsg { + admin_address: "admin".to_string(), + governance_address: "governance".to_string(), + nexus_gateway: "nexus_gateway".to_string(), + }, + ) + .unwrap(); - assert!(contract - .route_messages(sender, vec![rand_message(source_chain, destination_chain)]) - .is_err_and(move |err| { - matches!(err.current_context(), Error::GatewayNotRegistered) - })); + assert!(route_messages( + deps.as_mut().storage, + sender, + vec![rand_message(source_chain, destination_chain)] + ) + .is_err_and(move |err| { matches!(err.current_context(), Error::GatewayNotRegistered) })); } #[test] fn route_messages_with_frozen_source_chain() { - let config = Config { - admin: Addr::unchecked("admin"), - governance: Addr::unchecked("governance"), - nexus_gateway: Addr::unchecked("nexus_gateway"), - }; let sender = Addr::unchecked("sender"); let source_chain: ChainName = "ethereum".parse().unwrap(); let destination_chain = "bitcoin".parse().unwrap(); - let mut store = MockStore::new(); - store - .expect_load_config() - .returning(move || Ok(config.clone())); + let mut deps = mock_dependencies(); + instantiate( + deps.as_mut(), + mock_env(), + mock_info("admin", &[]), + InstantiateMsg { + admin_address: "admin".to_string(), + governance_address: "governance".to_string(), + nexus_gateway: "nexus_gateway".to_string(), + }, + ) + .unwrap(); + let chain_endpoint = ChainEndpoint { name: source_chain.clone(), gateway: Gateway { @@ -334,36 +343,39 @@ mod test { frozen_status: FlagSet::from(GatewayDirection::Incoming), msg_id_format: axelar_wasm_std::msg_id::MessageIdFormat::HexTxHashAndEventIndex, }; - store - .expect_load_chain_by_gateway() - .once() - .with(predicate::eq(sender.clone())) - .return_once(|_| Ok(Some(chain_endpoint))); - - let contract = Contract::new(store); + chain_endpoints() + .save(deps.as_mut().storage, source_chain.clone(), &chain_endpoint) + .unwrap(); - assert!(contract - .route_messages(sender, vec![rand_message(source_chain.clone(), destination_chain)]) - .is_err_and(move |err| { - matches!(err.current_context(), Error::ChainFrozen { chain } if *chain == source_chain) - })); + assert!(route_messages( + deps.as_mut().storage, + sender, + vec![rand_message(source_chain.clone(), destination_chain)] + ) + .is_err_and(move |err| { + matches!(err.current_context(), Error::ChainFrozen { chain } if *chain == source_chain) + })); } #[test] fn route_messages_with_wrong_source_chain() { - let config = Config { - admin: Addr::unchecked("admin"), - governance: Addr::unchecked("governance"), - nexus_gateway: Addr::unchecked("nexus_gateway"), - }; let sender = Addr::unchecked("sender"); let source_chain: ChainName = "ethereum".parse().unwrap(); let destination_chain = "bitcoin".parse().unwrap(); - let mut store = MockStore::new(); - store - .expect_load_config() - .returning(move || Ok(config.clone())); + let mut deps = mock_dependencies(); + instantiate( + deps.as_mut(), + mock_env(), + mock_info("admin", &[]), + InstantiateMsg { + admin_address: "admin".to_string(), + governance_address: "governance".to_string(), + nexus_gateway: "nexus_gateway".to_string(), + }, + ) + .unwrap(); + let chain_endpoint = ChainEndpoint { name: source_chain.clone(), gateway: Gateway { @@ -372,37 +384,36 @@ mod test { frozen_status: FlagSet::from(GatewayDirection::None), msg_id_format: axelar_wasm_std::msg_id::MessageIdFormat::HexTxHashAndEventIndex, }; - store - .expect_load_chain_by_gateway() - .once() - .with(predicate::eq(sender.clone())) - .return_once(|_| Ok(Some(chain_endpoint))); - - let contract = Contract::new(store); - - assert!(contract - .route_messages( - sender, - vec![rand_message("polygon".parse().unwrap(), destination_chain)] - ) - .is_err_and(|err| { matches!(err.current_context(), Error::WrongSourceChain) })); + chain_endpoints() + .save(deps.as_mut().storage, source_chain.clone(), &chain_endpoint) + .unwrap(); + + assert!(route_messages( + deps.as_mut().storage, + sender, + vec![rand_message("polygon".parse().unwrap(), destination_chain)] + ) + .is_err_and(|err| { matches!(err.current_context(), Error::WrongSourceChain) })); } #[test] fn route_messages_with_frozen_destination_chain() { - let config = Config { - admin: Addr::unchecked("admin"), - governance: Addr::unchecked("governance"), - nexus_gateway: Addr::unchecked("nexus_gateway"), - }; let sender = Addr::unchecked("sender"); let source_chain: ChainName = "ethereum".parse().unwrap(); let destination_chain: ChainName = "bitcoin".parse().unwrap(); - let mut store = MockStore::new(); - store - .expect_load_config() - .returning(move || Ok(config.clone())); + let mut deps = mock_dependencies(); + instantiate( + deps.as_mut(), + mock_env(), + mock_info("admin", &[]), + InstantiateMsg { + admin_address: "admin".to_string(), + governance_address: "governance".to_string(), + nexus_gateway: "nexus_gateway".to_string(), + }, + ) + .unwrap(); let source_chain_endpoint = ChainEndpoint { name: source_chain.clone(), gateway: Gateway { @@ -411,29 +422,30 @@ mod test { frozen_status: FlagSet::from(GatewayDirection::None), msg_id_format: axelar_wasm_std::msg_id::MessageIdFormat::HexTxHashAndEventIndex, }; - store - .expect_load_chain_by_gateway() - .once() - .with(predicate::eq(sender.clone())) - .return_once(|_| Ok(Some(source_chain_endpoint))); + chain_endpoints() + .save( + deps.as_mut().storage, + source_chain.clone(), + &source_chain_endpoint, + ) + .unwrap(); let destination_chain_endpoint = ChainEndpoint { name: destination_chain.clone(), gateway: Gateway { - address: sender.clone(), + address: Addr::unchecked("destination"), }, frozen_status: FlagSet::from(GatewayDirection::Bidirectional), msg_id_format: axelar_wasm_std::msg_id::MessageIdFormat::HexTxHashAndEventIndex, }; - store - .expect_load_chain_by_chain_name() - .once() - .with(predicate::eq(destination_chain.clone())) - .return_once(|_| Ok(Some(destination_chain_endpoint))); - - let contract = Contract::new(store); + chain_endpoints() + .save( + deps.as_mut().storage, + destination_chain.clone(), + &destination_chain_endpoint, + ) + .unwrap(); - assert!(contract - .route_messages(sender, vec![rand_message(source_chain, destination_chain.clone())]) + assert!(route_messages(deps.as_mut().storage, sender, vec![rand_message(source_chain, destination_chain.clone())]) .is_err_and(move |err| { matches!(err.current_context(), Error::ChainFrozen { chain } if *chain == destination_chain) })); @@ -441,19 +453,23 @@ mod test { #[test] fn route_messages_from_non_nexus_with_invalid_message_id() { - let config = Config { - admin: Addr::unchecked("admin"), - governance: Addr::unchecked("governance"), - nexus_gateway: Addr::unchecked("nexus_gateway"), - }; let sender = Addr::unchecked("sender"); let source_chain: ChainName = "ethereum".parse().unwrap(); let destination_chain: ChainName = "bitcoin".parse().unwrap(); - let mut store = MockStore::new(); - store - .expect_load_config() - .returning(move || Ok(config.clone())); + let mut deps = mock_dependencies(); + instantiate( + deps.as_mut(), + mock_env(), + mock_info("admin", &[]), + InstantiateMsg { + admin_address: "admin".to_string(), + governance_address: "governance".to_string(), + nexus_gateway: "nexus_gateway".to_string(), + }, + ) + .unwrap(); + let source_chain_endpoint = ChainEndpoint { name: source_chain.clone(), gateway: Gateway { @@ -462,61 +478,64 @@ mod test { frozen_status: FlagSet::from(GatewayDirection::None), msg_id_format: axelar_wasm_std::msg_id::MessageIdFormat::HexTxHashAndEventIndex, }; - store - .expect_load_chain_by_gateway() - .once() - .with(predicate::eq(sender.clone())) - .return_once(|_| Ok(Some(source_chain_endpoint))); - - let contract = Contract::new(store); - - let mut msg = rand_message(source_chain, destination_chain.clone()); - msg.cc_id.id = "foobar".try_into().unwrap(); - assert!(contract - .route_messages(sender, vec![msg]) + chain_endpoints() + .save( + deps.as_mut().storage, + source_chain.clone(), + &source_chain_endpoint, + ) + .unwrap(); + + let mut msg = rand_message(source_chain.clone(), destination_chain.clone()); + msg.cc_id = CrossChainId::new(source_chain, "foobar").unwrap(); + assert!(route_messages(deps.as_mut().storage, sender, vec![msg]) .is_err_and(move |err| { matches!(err.current_context(), Error::InvalidMessageId) })); } #[test] fn route_messages_from_nexus_with_invalid_message_id() { - let config = Config { - admin: Addr::unchecked("admin"), - governance: Addr::unchecked("governance"), - nexus_gateway: Addr::unchecked("nexus_gateway"), - }; - let sender = config.nexus_gateway.clone(); + let sender = Addr::unchecked("nexus_gateway"); let source_chain: ChainName = "ethereum".parse().unwrap(); let destination_chain: ChainName = "bitcoin".parse().unwrap(); - let mut store = MockStore::new(); - store - .expect_load_config() - .returning(move || Ok(config.clone())); - - let contract = Contract::new(store); + let mut deps = mock_dependencies(); + instantiate( + deps.as_mut(), + mock_env(), + mock_info("admin", &[]), + InstantiateMsg { + admin_address: "admin".to_string(), + governance_address: "governance".to_string(), + nexus_gateway: "nexus_gateway".to_string(), + }, + ) + .unwrap(); - let mut msg = rand_message(source_chain, destination_chain.clone()); - msg.cc_id.id = "foobar".try_into().unwrap(); - assert!(contract - .route_messages(sender, vec![msg]) + let mut msg = rand_message(source_chain.clone(), destination_chain.clone()); + msg.cc_id = CrossChainId::new(source_chain, "foobar").unwrap(); + assert!(route_messages(deps.as_mut().storage, sender, vec![msg]) .is_err_and(move |err| { matches!(err.current_context(), Error::InvalidMessageId) })); } #[test] fn route_messages_from_non_nexus_with_incorrect_message_id_format() { - let config = Config { - admin: Addr::unchecked("admin"), - governance: Addr::unchecked("governance"), - nexus_gateway: Addr::unchecked("nexus_gateway"), - }; let sender = Addr::unchecked("sender"); let source_chain: ChainName = "ethereum".parse().unwrap(); let destination_chain: ChainName = "bitcoin".parse().unwrap(); - let mut store = MockStore::new(); - store - .expect_load_config() - .returning(move || Ok(config.clone())); + let mut deps = mock_dependencies(); + instantiate( + deps.as_mut(), + mock_env(), + mock_info("admin", &[]), + InstantiateMsg { + admin_address: "admin".to_string(), + governance_address: "governance".to_string(), + nexus_gateway: "nexus_gateway".to_string(), + }, + ) + .unwrap(); + let source_chain_endpoint = ChainEndpoint { name: source_chain.clone(), gateway: Gateway { @@ -525,43 +544,50 @@ mod test { frozen_status: FlagSet::from(GatewayDirection::None), msg_id_format: axelar_wasm_std::msg_id::MessageIdFormat::Base58TxDigestAndEventIndex, }; - store - .expect_load_chain_by_gateway() - .once() - .with(predicate::eq(sender.clone())) - .return_once(|_| Ok(Some(source_chain_endpoint))); - - let contract = Contract::new(store); - - let mut msg = rand_message(source_chain, destination_chain.clone()); - msg.cc_id.id = HexTxHashAndEventIndex { - tx_hash: [0; 32], - event_index: 0, - } - .to_string() - .try_into() + chain_endpoints() + .save( + deps.as_mut().storage, + source_chain.clone(), + &source_chain_endpoint, + ) + .unwrap(); + + let mut msg = rand_message(source_chain.clone(), destination_chain.clone()); + msg.cc_id = CrossChainId::new( + source_chain, + HexTxHashAndEventIndex { + tx_hash: [0; 32], + event_index: 0, + } + .to_string() + .as_str(), + ) .unwrap(); - assert!(contract - .route_messages(sender, vec![msg]) + + assert!(route_messages(deps.as_mut().storage, sender, vec![msg]) .is_err_and(move |err| { matches!(err.current_context(), Error::InvalidMessageId) })); } #[test] fn route_messages_from_non_nexus_to_non_nexus() { - let config = Config { - admin: Addr::unchecked("admin"), - governance: Addr::unchecked("governance"), - nexus_gateway: Addr::unchecked("nexus_gateway"), - }; let sender = Addr::unchecked("sender"); let source_chain: ChainName = "ethereum".parse().unwrap(); let destination_chain_1: ChainName = "bitcoin".parse().unwrap(); let destination_chain_2: ChainName = "polygon".parse().unwrap(); - let mut store = MockStore::new(); - store - .expect_load_config() - .returning(move || Ok(config.clone())); + let mut deps = mock_dependencies(); + instantiate( + deps.as_mut(), + mock_env(), + mock_info("admin", &[]), + InstantiateMsg { + admin_address: "admin".to_string(), + governance_address: "governance".to_string(), + nexus_gateway: "nexus_gateway".to_string(), + }, + ) + .unwrap(); + let source_chain_endpoint = ChainEndpoint { name: source_chain.clone(), gateway: Gateway { @@ -570,69 +596,77 @@ mod test { frozen_status: FlagSet::from(GatewayDirection::None), msg_id_format: axelar_wasm_std::msg_id::MessageIdFormat::HexTxHashAndEventIndex, }; - store - .expect_load_chain_by_gateway() - .once() - .with(predicate::eq(sender.clone())) - .return_once(|_| Ok(Some(source_chain_endpoint))); + chain_endpoints() + .save( + deps.as_mut().storage, + source_chain.clone(), + &source_chain_endpoint, + ) + .unwrap(); let destination_chain_endpoint_1 = ChainEndpoint { name: destination_chain_1.clone(), gateway: Gateway { - address: sender.clone(), + address: Addr::unchecked("destination_1"), }, frozen_status: FlagSet::from(GatewayDirection::None), msg_id_format: axelar_wasm_std::msg_id::MessageIdFormat::HexTxHashAndEventIndex, }; - store - .expect_load_chain_by_chain_name() - .once() - .with(predicate::eq(destination_chain_1.clone())) - .return_once(|_| Ok(Some(destination_chain_endpoint_1))); + chain_endpoints() + .save( + deps.as_mut().storage, + destination_chain_1.clone(), + &destination_chain_endpoint_1, + ) + .unwrap(); let destination_chain_endpoint_2 = ChainEndpoint { name: destination_chain_2.clone(), gateway: Gateway { - address: sender.clone(), + address: Addr::unchecked("destination_2"), }, frozen_status: FlagSet::from(GatewayDirection::None), msg_id_format: axelar_wasm_std::msg_id::MessageIdFormat::HexTxHashAndEventIndex, }; - store - .expect_load_chain_by_chain_name() - .once() - .with(predicate::eq(destination_chain_2.clone())) - .return_once(|_| Ok(Some(destination_chain_endpoint_2))); - - let contract = Contract::new(store); - - assert!(contract - .route_messages( - sender, - vec![ - rand_message(source_chain.clone(), destination_chain_1.clone()), - rand_message(source_chain.clone(), destination_chain_1.clone()), - rand_message(source_chain.clone(), destination_chain_1.clone()), - rand_message(source_chain.clone(), destination_chain_2.clone()), - ] + chain_endpoints() + .save( + deps.as_mut().storage, + destination_chain_2.clone(), + &destination_chain_endpoint_2, ) - .is_ok_and(|res| { res.messages.len() == 2 })); + .unwrap(); + + assert!(route_messages( + deps.as_mut().storage, + sender, + vec![ + rand_message(source_chain.clone(), destination_chain_1.clone()), + rand_message(source_chain.clone(), destination_chain_1.clone()), + rand_message(source_chain.clone(), destination_chain_1.clone()), + rand_message(source_chain.clone(), destination_chain_2.clone()), + ] + ) + .is_ok_and(|res| { res.messages.len() == 2 })); } #[test] fn route_messages_from_nexus_to_registered_chains() { - let config = Config { - admin: Addr::unchecked("admin"), - governance: Addr::unchecked("governance"), - nexus_gateway: Addr::unchecked("nexus_gateway"), - }; - let sender = config.nexus_gateway.clone(); + let sender = Addr::unchecked("nexus_gateway"); let source_chain: ChainName = "ethereum".parse().unwrap(); let destination_chain_1: ChainName = "bitcoin".parse().unwrap(); let destination_chain_2: ChainName = "polygon".parse().unwrap(); - let mut store = MockStore::new(); - store - .expect_load_config() - .returning(move || Ok(config.clone())); + let mut deps = mock_dependencies(); + instantiate( + deps.as_mut(), + mock_env(), + mock_info("admin", &[]), + InstantiateMsg { + admin_address: "admin".to_string(), + governance_address: "governance".to_string(), + nexus_gateway: "nexus_gateway".to_string(), + }, + ) + .unwrap(); + let destination_chain_endpoint_1 = ChainEndpoint { name: destination_chain_1.clone(), gateway: Gateway { @@ -641,11 +675,13 @@ mod test { frozen_status: FlagSet::from(GatewayDirection::None), msg_id_format: axelar_wasm_std::msg_id::MessageIdFormat::HexTxHashAndEventIndex, }; - store - .expect_load_chain_by_chain_name() - .once() - .with(predicate::eq(destination_chain_1.clone())) - .return_once(|_| Ok(Some(destination_chain_endpoint_1))); + chain_endpoints() + .save( + deps.as_mut().storage, + destination_chain_1.clone(), + &destination_chain_endpoint_1, + ) + .unwrap(); let destination_chain_endpoint_2 = ChainEndpoint { name: destination_chain_2.clone(), gateway: Gateway { @@ -654,76 +690,76 @@ mod test { frozen_status: FlagSet::from(GatewayDirection::None), msg_id_format: axelar_wasm_std::msg_id::MessageIdFormat::HexTxHashAndEventIndex, }; - store - .expect_load_chain_by_chain_name() - .once() - .with(predicate::eq(destination_chain_2.clone())) - .return_once(|_| Ok(Some(destination_chain_endpoint_2))); - - let contract = Contract::new(store); - - assert!(contract - .route_messages( - sender, - vec![ - rand_message(source_chain.clone(), destination_chain_1.clone()), - rand_message(source_chain.clone(), destination_chain_1.clone()), - rand_message(source_chain.clone(), destination_chain_1.clone()), - rand_message(source_chain.clone(), destination_chain_2.clone()), - ] + chain_endpoints() + .save( + deps.as_mut().storage, + destination_chain_2.clone(), + &destination_chain_endpoint_2, ) - .is_ok_and(|res| { res.messages.len() == 2 })); + .unwrap(); + + assert!(route_messages( + deps.as_mut().storage, + sender, + vec![ + rand_message(source_chain.clone(), destination_chain_1.clone()), + rand_message(source_chain.clone(), destination_chain_1.clone()), + rand_message(source_chain.clone(), destination_chain_1.clone()), + rand_message(source_chain.clone(), destination_chain_2.clone()), + ] + ) + .is_ok_and(|res| { res.messages.len() == 2 })); } #[test] fn route_messages_from_nexus_to_non_registered_chains() { - let config = Config { - admin: Addr::unchecked("admin"), - governance: Addr::unchecked("governance"), - nexus_gateway: Addr::unchecked("nexus_gateway"), - }; - let sender = config.nexus_gateway.clone(); + let sender = Addr::unchecked("nexus_gateway"); let source_chain: ChainName = "ethereum".parse().unwrap(); let destination_chain: ChainName = "bitcoin".parse().unwrap(); - let mut store = MockStore::new(); - store - .expect_load_config() - .returning(move || Ok(config.clone())); - store - .expect_load_chain_by_chain_name() - .once() - .with(predicate::eq(destination_chain.clone())) - .return_once(|_| Ok(None)); - - let contract = Contract::new(store); - - assert!(contract - .route_messages( - sender, - vec![rand_message( - source_chain.clone(), - destination_chain.clone() - )] - ) - .is_err_and(|err| { matches!(err.current_context(), Error::ChainNotFound) })); + let mut deps = mock_dependencies(); + instantiate( + deps.as_mut(), + mock_env(), + mock_info("admin", &[]), + InstantiateMsg { + admin_address: "admin".to_string(), + governance_address: "governance".to_string(), + nexus_gateway: "nexus_gateway".to_string(), + }, + ) + .unwrap(); + + assert!(route_messages( + deps.as_mut().storage, + sender, + vec![rand_message( + source_chain.clone(), + destination_chain.clone() + )] + ) + .is_err_and(|err| { matches!(err.current_context(), Error::ChainNotFound) })); } #[test] fn route_messages_from_registered_chain_to_nexus() { - let config = Config { - admin: Addr::unchecked("admin"), - governance: Addr::unchecked("governance"), - nexus_gateway: Addr::unchecked("nexus_gateway"), - }; let sender = Addr::unchecked("sender"); let source_chain: ChainName = "ethereum".parse().unwrap(); let destination_chain: ChainName = "bitcoin".parse().unwrap(); - let mut store = MockStore::new(); - store - .expect_load_config() - .returning(move || Ok(config.clone())); + let mut deps = mock_dependencies(); + instantiate( + deps.as_mut(), + mock_env(), + mock_info("admin", &[]), + InstantiateMsg { + admin_address: "admin".to_string(), + governance_address: "governance".to_string(), + nexus_gateway: "nexus_gateway".to_string(), + }, + ) + .unwrap(); + let source_chain_endpoint = ChainEndpoint { name: source_chain.clone(), gateway: Gateway { @@ -732,28 +768,23 @@ mod test { frozen_status: FlagSet::from(GatewayDirection::None), msg_id_format: axelar_wasm_std::msg_id::MessageIdFormat::HexTxHashAndEventIndex, }; - store - .expect_load_chain_by_gateway() - .once() - .with(predicate::eq(sender.clone())) - .return_once(|_| Ok(Some(source_chain_endpoint))); - store - .expect_load_chain_by_chain_name() - .once() - .with(predicate::eq(destination_chain.clone())) - .return_once(|_| Ok(None)); - - let contract = Contract::new(store); - - assert!(contract - .route_messages( - sender, - vec![rand_message( - source_chain.clone(), - destination_chain.clone() - )] + chain_endpoints() + .save( + deps.as_mut().storage, + source_chain.clone(), + &source_chain_endpoint, ) - .is_ok_and(|res| { res.messages.len() == 1 })); + .unwrap(); + + assert!(route_messages( + deps.as_mut().storage, + sender, + vec![rand_message( + source_chain.clone(), + destination_chain.clone() + )] + ) + .is_ok_and(|res| { res.messages.len() == 1 })); } #[test] @@ -777,8 +808,16 @@ mod test { .unwrap(); // freezing twice produces same result - freeze_chain(deps.as_mut(), chain.clone(), GatewayDirection::Incoming).unwrap(); - freeze_chain(deps.as_mut(), chain.clone(), GatewayDirection::Incoming).unwrap(); + freeze_chains( + deps.as_mut().storage, + HashMap::from([(chain.clone(), GatewayDirection::Incoming)]), + ) + .unwrap(); + freeze_chains( + deps.as_mut().storage, + HashMap::from([(chain.clone(), GatewayDirection::Incoming)]), + ) + .unwrap(); assert_chain_endpoint_frozen_status( deps.as_mut().storage, @@ -786,16 +825,14 @@ mod test { FlagSet::from(GatewayDirection::Incoming), ); - freeze_chain( - deps.as_mut(), - chain.clone(), - GatewayDirection::Bidirectional, + freeze_chains( + deps.as_mut().storage, + HashMap::from([(chain.clone(), GatewayDirection::Bidirectional)]), ) .unwrap(); - freeze_chain( - deps.as_mut(), - chain.clone(), - GatewayDirection::Bidirectional, + freeze_chains( + deps.as_mut().storage, + HashMap::from([(chain.clone(), GatewayDirection::Bidirectional)]), ) .unwrap(); @@ -806,8 +843,16 @@ mod test { ); // unfreezing twice produces same result - unfreeze_chain(deps.as_mut(), chain.clone(), GatewayDirection::Outgoing).unwrap(); - unfreeze_chain(deps.as_mut(), chain.clone(), GatewayDirection::Outgoing).unwrap(); + unfreeze_chains( + deps.as_mut().storage, + HashMap::from([(chain.clone(), GatewayDirection::Outgoing)]), + ) + .unwrap(); + unfreeze_chains( + deps.as_mut().storage, + HashMap::from([(chain.clone(), GatewayDirection::Outgoing)]), + ) + .unwrap(); assert_chain_endpoint_frozen_status( deps.as_mut().storage, @@ -815,16 +860,14 @@ mod test { FlagSet::from(GatewayDirection::Incoming), ); - unfreeze_chain( - deps.as_mut(), - chain.clone(), - GatewayDirection::Bidirectional, + unfreeze_chains( + deps.as_mut().storage, + HashMap::from([(chain.clone(), GatewayDirection::Bidirectional)]), ) .unwrap(); - unfreeze_chain( - deps.as_mut(), - chain.clone(), - GatewayDirection::Bidirectional, + unfreeze_chains( + deps.as_mut().storage, + HashMap::from([(chain.clone(), GatewayDirection::Bidirectional)]), ) .unwrap(); @@ -855,7 +898,11 @@ mod test { ) .unwrap(); - let res = freeze_chain(deps.as_mut(), chain.clone(), GatewayDirection::Incoming).unwrap(); + let res = freeze_chains( + deps.as_mut().storage, + HashMap::from([(chain.clone(), GatewayDirection::Incoming)]), + ) + .unwrap(); assert_eq!(res.events.len(), 1); assert!(res.events.contains( @@ -866,7 +913,11 @@ mod test { .into() )); - let res = unfreeze_chain(deps.as_mut(), chain.clone(), GatewayDirection::Incoming).unwrap(); + let res = unfreeze_chains( + deps.as_mut().storage, + HashMap::from([(chain.clone(), GatewayDirection::Incoming)]), + ) + .unwrap(); assert_eq!(res.events.len(), 1); assert!(res.events.contains( diff --git a/contracts/router/src/contract/migrations/mod.rs b/contracts/router/src/contract/migrations/mod.rs new file mode 100644 index 000000000..ebcb4aab1 --- /dev/null +++ b/contracts/router/src/contract/migrations/mod.rs @@ -0,0 +1 @@ +pub mod v0_3_3; diff --git a/contracts/router/src/contract/migrations/v0_3_3.rs b/contracts/router/src/contract/migrations/v0_3_3.rs new file mode 100644 index 000000000..86c849b26 --- /dev/null +++ b/contracts/router/src/contract/migrations/v0_3_3.rs @@ -0,0 +1,233 @@ +#![allow(deprecated)] + +use axelar_wasm_std::error::ContractError; +use axelar_wasm_std::{killswitch, permission_control}; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, StdResult, Storage}; +use cw_storage_plus::Item; +use router_api::error::Error; + +use crate::contract::CONTRACT_NAME; +use crate::state; + +const BASE_VERSION: &str = "0.3.3"; + +pub fn migrate(storage: &mut dyn Storage) -> Result<(), ContractError> { + cw2::assert_contract_version(storage, CONTRACT_NAME, BASE_VERSION)?; + + set_generalized_permission_control(storage)?; + set_router_state(storage)?; + Ok(()) +} + +#[deprecated(since = "0.3.3", note = "only used during migration")] +#[cw_serde] +struct Config { + pub admin: Addr, + pub governance: Addr, + pub nexus_gateway: Addr, +} + +fn set_generalized_permission_control(storage: &mut dyn Storage) -> Result<(), Error> { + let old_config = CONFIG.load(storage)?; + permission_control::set_admin(storage, &old_config.admin) + .and_then(|_| permission_control::set_governance(storage, &old_config.governance)) + .map_err(Error::from)?; + + let new_config = &state::Config { + nexus_gateway: old_config.nexus_gateway, + }; + state::CONFIG.save(storage, new_config)?; + Ok(()) +} + +fn set_router_state(storage: &mut dyn Storage) -> StdResult<()> { + killswitch::init(storage, killswitch::State::Disengaged) +} + +#[deprecated(since = "0.3.3", note = "only used during migration")] +const CONFIG: Item = Item::new("config"); + +#[cfg(test)] +mod test { + use std::collections::HashMap; + + use axelar_wasm_std::error::ContractError; + use axelar_wasm_std::killswitch; + use axelar_wasm_std::msg_id::MessageIdFormat; + use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; + use cosmwasm_std::{DepsMut, Env, MessageInfo, Response}; + use router_api::msg::ExecuteMsg; + + use crate::contract::migrations::v0_3_3; + use crate::contract::migrations::v0_3_3::BASE_VERSION; + use crate::contract::{execute, CONTRACT_NAME}; + use crate::events::RouterInstantiated; + use crate::msg::InstantiateMsg; + use crate::state; + + #[test] + fn migrate_checks_contract_version() { + let mut deps = mock_dependencies(); + let _ = instantiate_0_3_3_contract(deps.as_mut()).unwrap(); + cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, "something wrong").unwrap(); + + assert!(v0_3_3::migrate(deps.as_mut().storage).is_err()); + + cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, BASE_VERSION).unwrap(); + + assert!(v0_3_3::migrate(deps.as_mut().storage).is_ok()); + } + + #[test] + fn config_gets_migrated() { + let mut deps = mock_dependencies(); + let instantiate_msg = instantiate_0_3_3_contract(deps.as_mut()).unwrap(); + + assert!(v0_3_3::CONFIG.load(deps.as_mut().storage).is_ok()); + assert!(state::CONFIG.load(deps.as_mut().storage).is_err()); + + assert!(v0_3_3::migrate(deps.as_mut().storage).is_ok()); + + assert!(v0_3_3::CONFIG.load(deps.as_mut().storage).is_err()); + let config = state::CONFIG.load(deps.as_mut().storage); + assert!(config.is_ok()); + assert!(config.unwrap().nexus_gateway == instantiate_msg.nexus_gateway); + } + + #[test] + fn router_is_enabled() { + let mut deps = mock_dependencies(); + let _ = instantiate_0_3_3_contract(deps.as_mut()).unwrap(); + + assert!(v0_3_3::migrate(deps.as_mut().storage).is_ok()); + + assert!(killswitch::is_contract_active(deps.as_mut().storage)); + } + + #[test] + fn migration() { + let mut deps = mock_dependencies(); + let instantiate_msg = instantiate_0_3_3_contract(deps.as_mut()).unwrap(); + + let msg = ExecuteMsg::RegisterChain { + chain: "chain".parse().unwrap(), + gateway_address: "gateway".parse().unwrap(), + msg_id_format: MessageIdFormat::HexTxHashAndEventIndex, + }; + + // before migration no address should be able to execute this message + + assert!(execute( + deps.as_mut(), + mock_env(), + mock_info(&instantiate_msg.admin_address, &[]), + msg.clone(), + ) + .is_err()); + + assert!(execute( + deps.as_mut(), + mock_env(), + mock_info(&instantiate_msg.governance_address, &[]), + msg.clone(), + ) + .is_err()); + + assert!(v0_3_3::migrate(&mut deps.storage).is_ok()); + + // after migration only governance should be able to register a chain + assert!(execute( + deps.as_mut(), + mock_env(), + mock_info(&instantiate_msg.admin_address, &[]), + msg.clone(), + ) + .is_err()); + + assert!(execute( + deps.as_mut(), + mock_env(), + mock_info(&instantiate_msg.governance_address, &[]), + msg.clone(), + ) + .is_ok()); + + // check that both admin and governance permissions are set correctly + + let msg = ExecuteMsg::UnfreezeChains { + chains: HashMap::new(), + }; + + assert!(execute( + deps.as_mut(), + mock_env(), + mock_info("no_privilege", &[]), + msg.clone(), + ) + .is_err()); + + assert!(execute( + deps.as_mut(), + mock_env(), + mock_info(instantiate_msg.admin_address.as_str(), &[]), + msg.clone(), + ) + .is_ok()); + + assert!(execute( + deps.as_mut(), + mock_env(), + mock_info(instantiate_msg.governance_address.as_str(), &[]), + msg.clone(), + ) + .is_ok()); + } + + fn instantiate_0_3_3_contract(deps: DepsMut) -> Result { + let admin = "admin"; + let governance = "governance"; + let nexus_gateway = "nexus_gateway"; + + let msg = InstantiateMsg { + nexus_gateway: nexus_gateway.to_string(), + admin_address: admin.to_string(), + governance_address: governance.to_string(), + }; + instantiate(deps, mock_env(), mock_info(admin, &[]), msg.clone())?; + Ok(msg) + } + + #[deprecated(since = "0.3.3", note = "only used to test the migration")] + fn instantiate( + deps: DepsMut, + _env: Env, + _info: MessageInfo, + msg: InstantiateMsg, + ) -> Result { + cw2::set_contract_version(deps.storage, CONTRACT_NAME, BASE_VERSION)?; + + let admin = deps.api.addr_validate(&msg.admin_address)?; + let governance = deps.api.addr_validate(&msg.governance_address)?; + let nexus_gateway = deps.api.addr_validate(&msg.nexus_gateway)?; + + let config = v0_3_3::Config { + admin: admin.clone(), + governance: governance.clone(), + nexus_gateway: nexus_gateway.clone(), + }; + + v0_3_3::CONFIG + .save(deps.storage, &config) + .expect("must save the config"); + + Ok(Response::new().add_event( + RouterInstantiated { + admin, + governance, + nexus_gateway, + } + .into(), + )) + } +} diff --git a/contracts/router/src/contract/query.rs b/contracts/router/src/contract/query.rs index ea3589854..d10f23aae 100644 --- a/contracts/router/src/contract/query.rs +++ b/contracts/router/src/contract/query.rs @@ -1,6 +1,5 @@ -use cosmwasm_std::{Deps, Order}; +use cosmwasm_std::{Deps, Order, Storage}; use cw_storage_plus::Bound; - use error_stack::{Result, ResultExt}; use router_api::error::Error; use router_api::{ChainEndpoint, ChainName}; @@ -10,9 +9,9 @@ use crate::state::chain_endpoints; // Pagination limits const DEFAULT_LIMIT: u32 = u32::MAX; -pub fn get_chain_info(deps: Deps, chain: ChainName) -> Result { +pub fn chain_info(storage: &dyn Storage, chain: ChainName) -> Result { chain_endpoints() - .may_load(deps.storage, chain) + .may_load(storage, chain) .change_context(Error::StoreFailure)? .ok_or(Error::ChainNotFound.into()) } @@ -38,19 +37,19 @@ pub fn chains( #[cfg(test)] mod test { use axelar_wasm_std::flagset::FlagSet; - use cosmwasm_std::{testing::mock_dependencies, Addr}; + use cosmwasm_std::testing::mock_dependencies; + use cosmwasm_std::Addr; use router_api::error::Error; use router_api::{ChainEndpoint, ChainName, Gateway, GatewayDirection}; + use super::chain_info; use crate::state::chain_endpoints; - use super::get_chain_info; - #[test] fn should_get_chain_info() { let mut deps = mock_dependencies(); - let chain_name: ChainName = "Ethereum".to_string().try_into().unwrap(); - let chain_info = ChainEndpoint { + let chain_name: ChainName = "Ethereum".try_into().unwrap(); + let endpoint = ChainEndpoint { name: chain_name.clone(), gateway: Gateway { address: Addr::unchecked("some gateway"), @@ -60,18 +59,18 @@ mod test { }; assert!(chain_endpoints() - .save(deps.as_mut().storage, chain_name.clone(), &chain_info) + .save(deps.as_mut().storage, chain_name.clone(), &endpoint) .is_ok()); - let result = get_chain_info(deps.as_ref(), chain_name); + let result = chain_info(deps.as_ref().storage, chain_name); assert!(result.is_ok()); - assert_eq!(result.unwrap(), chain_info); + assert_eq!(result.unwrap(), endpoint); } #[test] fn get_non_existent_chain_info() { let deps = mock_dependencies(); - let chain_name: ChainName = "Ethereum".to_string().try_into().unwrap(); - let result = get_chain_info(deps.as_ref(), chain_name); + let chain_name: ChainName = "Ethereum".try_into().unwrap(); + let result = chain_info(deps.as_ref().storage, chain_name); assert!(result.is_err()); assert_eq!(result.unwrap_err().current_context(), &Error::ChainNotFound); } diff --git a/contracts/router/src/events.rs b/contracts/router/src/events.rs index 0dad9c8be..29c0c85a0 100644 --- a/contracts/router/src/events.rs +++ b/contracts/router/src/events.rs @@ -21,14 +21,6 @@ pub struct GatewayUpgraded { pub gateway: GatewayInfo, } -pub struct GatewayFrozen { - pub gateway: GatewayInfo, -} - -pub struct GatewayUnfrozen { - pub gateway: GatewayInfo, -} - pub struct ChainFrozen { pub name: ChainName, pub direction: GatewayDirection, @@ -43,6 +35,9 @@ pub struct MessageRouted { pub msg: Message, } +pub struct RoutingDisabled; +pub struct RoutingEnabled; + impl From for Event { fn from(other: RouterInstantiated) -> Self { Event::new("router_instantiated") @@ -75,17 +70,15 @@ impl From for Event { } } -impl From for Event { - fn from(other: GatewayFrozen) -> Self { - let attrs: Vec = other.gateway.into(); - Event::new("gateway_frozen").add_attributes(attrs) +impl From for Event { + fn from(_: RoutingDisabled) -> Self { + Event::new("routing_disabled") } } -impl From for Event { - fn from(other: GatewayUnfrozen) -> Self { - let attrs: Vec = other.gateway.into(); - Event::new("gateway_unfrozen").add_attributes(attrs) +impl From for Event { + fn from(_: RoutingEnabled) -> Self { + Event::new("routing_enabled") } } diff --git a/contracts/router/src/state.rs b/contracts/router/src/state.rs index 7b26e9584..ca014106c 100644 --- a/contracts/router/src/state.rs +++ b/contracts/router/src/state.rs @@ -1,73 +1,42 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, DepsMut, Order, StdResult, Storage}; +use cosmwasm_std::{Addr, Order, StdResult, Storage}; use cw_storage_plus::{Index, IndexList, IndexedMap, Item, MultiIndex}; -use error_stack::ResultExt; -use mockall::automock; +use error_stack::{report, ResultExt}; use router_api::error::Error; use router_api::{ChainEndpoint, ChainName}; -#[automock] -pub trait Store { - fn save_config(&mut self, config: Config) -> error_stack::Result<(), Error>; - fn load_config(&self) -> error_stack::Result; - fn load_chain_by_gateway( - &self, - gateway: &Addr, - ) -> error_stack::Result, Error>; - fn load_chain_by_chain_name( - &self, - chain_name: &ChainName, - ) -> error_stack::Result, Error>; +pub fn save_config(storage: &mut dyn Storage, config: &Config) -> error_stack::Result<(), Error> { + CONFIG + .save(storage, config) + .change_context(Error::StoreFailure) } -pub struct RouterStore<'a> { - storage: &'a mut dyn Storage, +pub fn load_config(storage: &dyn Storage) -> error_stack::Result { + CONFIG.load(storage).change_context(Error::StoreFailure) } -impl Store for RouterStore<'_> { - fn save_config(&mut self, config: Config) -> error_stack::Result<(), Error> { - CONFIG - .save(self.storage, &config) - .change_context(Error::StoreFailure) - } - - fn load_config(&self) -> error_stack::Result { - CONFIG - .load(self.storage) - .change_context(Error::StoreFailure) - } - - fn load_chain_by_gateway( - &self, - gateway: &Addr, - ) -> error_stack::Result, Error> { - chain_endpoints() - .idx - .gateway - .load_chain_by_gateway(self.storage, gateway) - .change_context(Error::StoreFailure) - } - - fn load_chain_by_chain_name( - &self, - chain_name: &ChainName, - ) -> error_stack::Result, Error> { - chain_endpoints() - .may_load(self.storage, chain_name.clone()) - .change_context(Error::StoreFailure) - } +pub fn load_chain_by_chain_name( + storage: &dyn Storage, + chain_name: &ChainName, +) -> error_stack::Result, Error> { + chain_endpoints() + .may_load(storage, chain_name.clone()) + .change_context(Error::StoreFailure) } - -impl<'a> RouterStore<'a> { - pub fn new(storage: &'a mut dyn Storage) -> Self { - Self { storage } - } +pub fn load_chain_by_gateway( + storage: &dyn Storage, + gateway: &Addr, +) -> error_stack::Result { + chain_endpoints() + .idx + .gateway + .load_chain_by_gateway(storage, gateway) + .change_context(Error::StoreFailure)? + .ok_or(report!(Error::GatewayNotRegistered)) } #[cw_serde] pub struct Config { - pub admin: Addr, - pub governance: Addr, pub nexus_gateway: Addr, } @@ -88,16 +57,7 @@ impl<'a> GatewayIndex<'a> { GatewayIndex(MultiIndex::new(idx_fn, pk_namespace, idx_namespace)) } - #[deprecated(note = "use load_chain_by_gateway instead")] - pub fn find_chain( - &self, - deps: &DepsMut, - contract_address: &Addr, - ) -> StdResult> { - self.load_chain_by_gateway(deps.storage, contract_address) - } - - fn load_chain_by_gateway( + pub fn load_chain_by_gateway( &self, storage: &dyn Storage, contract_address: &Addr, diff --git a/contracts/service-registry/.cargo/config.toml b/contracts/service-registry/.cargo/config.toml new file mode 100644 index 000000000..af5698e58 --- /dev/null +++ b/contracts/service-registry/.cargo/config.toml @@ -0,0 +1,4 @@ +[alias] +wasm = "build --release --lib --target wasm32-unknown-unknown" +unit-test = "test --lib" +schema = "run --bin schema" diff --git a/contracts/service-registry/Cargo.toml b/contracts/service-registry/Cargo.toml index f61d1f567..08d40c09f 100644 --- a/contracts/service-registry/Cargo.toml +++ b/contracts/service-registry/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "service-registry" -version = "0.3.0" +version = "1.0.0" rust-version = { workspace = true } -edition = "2021" +edition = { workspace = true } description = "Contract handling the service registrations" exclude = [ @@ -29,17 +29,18 @@ library = [] optimize = """docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/rust-optimizer:0.12.6 + cosmwasm/optimizer:0.16.0 """ [dependencies] -axelar-wasm-std = { workspace = true } -axelar-wasm-std-derive = { workspace = true } +axelar-wasm-std = { workspace = true, features = ["derive"] } +coordinator = { workspace = true, features = ["library"] } cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } cw-storage-plus = { workspace = true } cw2 = { workspace = true } error-stack = { workspace = true } +msgs-derive = { workspace = true } report = { workspace = true } router-api = { workspace = true } schemars = "0.8.10" diff --git a/contracts/service-registry/src/bin/schema.rs b/contracts/service-registry/src/bin/schema.rs index 8e54a70e3..85da9dcac 100644 --- a/contracts/service-registry/src/bin/schema.rs +++ b/contracts/service-registry/src/bin/schema.rs @@ -1,5 +1,4 @@ use cosmwasm_schema::write_api; - use service_registry::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; fn main() { diff --git a/contracts/service-registry/src/contract.rs b/contracts/service-registry/src/contract.rs index 67dc56a22..3cd025cec 100644 --- a/contracts/service-registry/src/contract.rs +++ b/contracts/service-registry/src/contract.rs @@ -1,48 +1,35 @@ +use axelar_wasm_std::{address, permission_control, FnExt}; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ to_json_binary, Addr, BankMsg, Binary, Coin, Deps, DepsMut, Empty, Env, MessageInfo, Order, - Response, Uint128, + QueryRequest, Response, Storage, WasmQuery, }; +use error_stack::{bail, Report, ResultExt}; use crate::error::ContractError; -use crate::migrations; use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; -use crate::state::{AuthorizationState, BondingState, Config, Service, Verifier, CONFIG, SERVICES}; +use crate::state::{AuthorizationState, BondingState, Service, Verifier, SERVICES, VERIFIERS}; mod execute; +mod migrations; mod query; const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn migrate( - deps: DepsMut, - _env: Env, - _msg: Empty, -) -> Result { - // any version checks should be done before here - - cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - migrations::v_0_3::migrate(deps).map_err(axelar_wasm_std::ContractError::from) -} - #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( deps: DepsMut, _env: Env, _info: MessageInfo, msg: InstantiateMsg, -) -> Result { +) -> Result { cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - CONFIG.save( - deps.storage, - &Config { - governance: deps.api.addr_validate(&msg.governance_account)?, - }, - )?; + let governance = address::validate_cosmwasm_address(deps.api, &msg.governance_account)?; + permission_control::set_governance(deps.storage, &governance)?; + Ok(Response::default()) } @@ -52,39 +39,35 @@ pub fn execute( env: Env, info: MessageInfo, msg: ExecuteMsg, -) -> Result { - match msg { +) -> Result { + match msg.ensure_permissions(deps.storage, &info.sender, match_verifier(&info.sender))? { ExecuteMsg::RegisterService { service_name, - service_contract, + coordinator_contract, min_num_verifiers, max_num_verifiers, min_verifier_bond, bond_denom, unbonding_period_days, description, - } => { - execute::require_governance(&deps, info)?; - execute::register_service( - deps, - service_name, - service_contract, - min_num_verifiers, - max_num_verifiers, - min_verifier_bond, - bond_denom, - unbonding_period_days, - description, - ) - } + } => execute::register_service( + deps, + service_name, + coordinator_contract, + min_num_verifiers, + max_num_verifiers, + min_verifier_bond, + bond_denom, + unbonding_period_days, + description, + ), ExecuteMsg::AuthorizeVerifiers { verifiers, service_name, } => { - execute::require_governance(&deps, info)?; let verifiers = verifiers .into_iter() - .map(|veriier| deps.api.addr_validate(&veriier)) + .map(|verifier| address::validate_cosmwasm_address(deps.api, &verifier)) .collect::, _>>()?; execute::update_verifier_authorization_status( deps, @@ -97,10 +80,9 @@ pub fn execute( verifiers, service_name, } => { - execute::require_governance(&deps, info)?; let verifiers = verifiers .into_iter() - .map(|verifier| deps.api.addr_validate(&verifier)) + .map(|verifier| address::validate_cosmwasm_address(deps.api, &verifier)) .collect::, _>>()?; execute::update_verifier_authorization_status( deps, @@ -113,10 +95,9 @@ pub fn execute( verifiers, service_name, } => { - execute::require_governance(&deps, info)?; let verifiers = verifiers .into_iter() - .map(|verifier| deps.api.addr_validate(&verifier)) + .map(|verifier| address::validate_cosmwasm_address(deps.api, &verifier)) .collect::, _>>()?; execute::update_verifier_authorization_status( deps, @@ -142,50 +123,81 @@ pub fn execute( ExecuteMsg::ClaimStake { service_name } => { execute::claim_stake(deps, env, info, service_name) } + }? + .then(Ok) +} + +fn match_verifier( + sender: &Addr, +) -> impl FnOnce(&dyn Storage, &ExecuteMsg) -> Result> + '_ +{ + |storage: &dyn Storage, msg: &ExecuteMsg| { + let service_name = match msg { + ExecuteMsg::RegisterChainSupport { service_name, .. } + | ExecuteMsg::DeregisterChainSupport { service_name, .. } => service_name, + _ => bail!(permission_control::Error::WrongVariant), + }; + VERIFIERS + .load(storage, (service_name, sender)) + .map(|verifier| verifier.address) + .change_context(permission_control::Error::Unauthorized) } - .map_err(axelar_wasm_std::ContractError::from) } #[cfg_attr(not(feature = "library"), entry_point)] -pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result { +pub fn query( + deps: Deps, + _env: Env, + msg: QueryMsg, +) -> Result { match msg { - QueryMsg::GetActiveVerifiers { - service_name, - chain_name, - } => to_json_binary(&query::get_active_verifiers( - deps, + QueryMsg::ActiveVerifiers { service_name, chain_name, - )?) - .map_err(|err| err.into()), - QueryMsg::GetVerifier { + } => to_json_binary(&query::active_verifiers(deps, service_name, chain_name)?) + .map_err(|err| err.into()), + QueryMsg::Verifier { service_name, verifier, - } => to_json_binary(&query::get_verifier(deps, service_name, verifier)?) + } => to_json_binary(&query::verifier(deps, service_name, verifier)?) .map_err(|err| err.into()), - QueryMsg::GetService { service_name } => { - to_json_binary(&query::get_service(deps, service_name)?).map_err(|err| err.into()) + QueryMsg::Service { service_name } => { + to_json_binary(&query::service(deps, service_name)?).map_err(|err| err.into()) } } } +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate( + deps: DepsMut, + _env: Env, + _msg: Empty, +) -> Result { + migrations::v0_4_1::migrate(deps.storage)?; + + cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + + Ok(Response::default()) +} + #[cfg(test)] mod test { use std::str::FromStr; - use cosmwasm_std::{ - coins, from_json, - testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, - CosmosMsg, Empty, OwnedDeps, StdResult, + use axelar_wasm_std::error::err_contains; + use axelar_wasm_std::nonempty; + use cosmwasm_std::testing::{ + mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage, }; + use cosmwasm_std::{coins, from_json, CosmosMsg, Empty, OwnedDeps, StdResult, Uint128}; use router_api::ChainName; - use crate::state::{WeightedVerifier, VERIFIER_WEIGHT}; - use super::*; + use crate::state::{WeightedVerifier, VERIFIER_WEIGHT}; const GOVERNANCE_ADDRESS: &str = "governance"; const UNAUTHORIZED_ADDRESS: &str = "unauthorized"; + const COORDINATOR_ADDRESS: &str = "coordinator_address"; const VERIFIER_ADDRESS: &str = "verifier"; const AXL_DENOMINATION: &str = "uaxl"; @@ -202,25 +214,14 @@ mod test { ) .unwrap(); - deps - } - - pub fn assert_contract_err_strings_equal( - actual: impl Into, - expected: impl Into, - ) { - assert_eq!(actual.into().to_string(), expected.into().to_string()); - } - - #[test] - fn migrate_sets_contract_version() { - let mut deps = mock_dependencies(); - - migrate(deps.as_mut(), mock_env(), Empty {}).unwrap(); + deps.querier.update_wasm(move |wq| match wq { + WasmQuery::Smart { contract_addr, .. } if contract_addr == COORDINATOR_ADDRESS => { + Ok(to_json_binary(&true).into()).into() + } + _ => panic!("no mock for this query"), + }); - let contract_version = cw2::get_contract_version(deps.as_mut().storage).unwrap(); - assert_eq!(contract_version.contract, "service-registry"); - assert_eq!(contract_version.version, CONTRACT_VERSION); + deps } #[test] @@ -233,10 +234,10 @@ mod test { mock_info(GOVERNANCE_ADDRESS, &[]), ExecuteMsg::RegisterService { service_name: "validators".into(), - service_contract: Addr::unchecked("nowhere"), + coordinator_contract: Addr::unchecked("nowhere"), min_num_verifiers: 0, max_num_verifiers: Some(100), - min_verifier_bond: Uint128::zero(), + min_verifier_bond: Uint128::one().try_into().unwrap(), bond_denom: AXL_DENOMINATION.into(), unbonding_period_days: 10, description: "Some service".into(), @@ -250,17 +251,21 @@ mod test { mock_info(UNAUTHORIZED_ADDRESS, &[]), ExecuteMsg::RegisterService { service_name: "validators".into(), - service_contract: Addr::unchecked("nowhere"), + coordinator_contract: Addr::unchecked("nowhere"), min_num_verifiers: 0, max_num_verifiers: Some(100), - min_verifier_bond: Uint128::zero(), + min_verifier_bond: Uint128::one().try_into().unwrap(), bond_denom: AXL_DENOMINATION.into(), unbonding_period_days: 10, description: "Some service".into(), }, ) .unwrap_err(); - assert_contract_err_strings_equal(err, ContractError::Unauthorized); + assert!(err_contains!( + err.report, + permission_control::Error, + permission_control::Error::PermissionDenied { .. } + )); } #[test] @@ -274,10 +279,10 @@ mod test { mock_info(GOVERNANCE_ADDRESS, &[]), ExecuteMsg::RegisterService { service_name: service_name.into(), - service_contract: Addr::unchecked("nowhere"), + coordinator_contract: Addr::unchecked("nowhere"), min_num_verifiers: 0, max_num_verifiers: Some(100), - min_verifier_bond: Uint128::zero(), + min_verifier_bond: Uint128::one().try_into().unwrap(), bond_denom: AXL_DENOMINATION.into(), unbonding_period_days: 10, description: "Some service".into(), @@ -306,7 +311,11 @@ mod test { }, ) .unwrap_err(); - assert_contract_err_strings_equal(err, ContractError::Unauthorized); + assert!(err_contains!( + err.report, + permission_control::Error, + permission_control::Error::PermissionDenied { .. } + )); } #[test] @@ -321,10 +330,10 @@ mod test { mock_info(GOVERNANCE_ADDRESS, &[]), ExecuteMsg::RegisterService { service_name: service_name.into(), - service_contract: Addr::unchecked("nowhere"), + coordinator_contract: Addr::unchecked("nowhere"), min_num_verifiers: 0, max_num_verifiers: Some(100), - min_verifier_bond, + min_verifier_bond: min_verifier_bond.try_into().unwrap(), bond_denom: AXL_DENOMINATION.into(), unbonding_period_days: 10, description: "Some service".into(), @@ -357,6 +366,51 @@ mod test { assert!(res.is_ok()); } + #[test] + fn bond_verifier_zero_bond_should_fail() { + let mut deps = setup(); + + let service_name = "validators"; + let min_verifier_bond = Uint128::new(100); + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(GOVERNANCE_ADDRESS, &[]), + ExecuteMsg::RegisterService { + service_name: service_name.into(), + coordinator_contract: Addr::unchecked("nowhere"), + min_num_verifiers: 0, + max_num_verifiers: Some(100), + min_verifier_bond: min_verifier_bond.try_into().unwrap(), + bond_denom: AXL_DENOMINATION.into(), + unbonding_period_days: 10, + description: "Some service".into(), + }, + ); + assert!(res.is_ok()); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(GOVERNANCE_ADDRESS, &[]), + ExecuteMsg::AuthorizeVerifiers { + verifiers: vec![VERIFIER_ADDRESS.into()], + service_name: service_name.into(), + }, + ); + assert!(res.is_ok()); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(VERIFIER_ADDRESS, &[]), + ExecuteMsg::BondVerifier { + service_name: service_name.into(), + }, + ); + assert!(res.is_err()); + } + #[test] fn register_chain_support() { let mut deps = setup(); @@ -369,10 +423,10 @@ mod test { mock_info(GOVERNANCE_ADDRESS, &[]), ExecuteMsg::RegisterService { service_name: service_name.into(), - service_contract: Addr::unchecked("nowhere"), + coordinator_contract: Addr::unchecked("nowhere"), min_num_verifiers: 0, max_num_verifiers: Some(100), - min_verifier_bond, + min_verifier_bond: min_verifier_bond.try_into().unwrap(), bond_denom: AXL_DENOMINATION.into(), unbonding_period_days: 10, description: "Some service".into(), @@ -420,7 +474,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetActiveVerifiers { + QueryMsg::ActiveVerifiers { service_name: service_name.into(), chain_name, }, @@ -434,7 +488,7 @@ mod test { verifier_info: Verifier { address: Addr::unchecked(VERIFIER_ADDRESS), bonding_state: BondingState::Bonded { - amount: min_verifier_bond + amount: min_verifier_bond.try_into().unwrap(), }, authorization_state: AuthorizationState::Authorized, service_name: service_name.into() @@ -447,7 +501,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetActiveVerifiers { + QueryMsg::ActiveVerifiers { service_name: service_name.into(), chain_name: ChainName::from_str("random chain").unwrap(), }, @@ -472,10 +526,10 @@ mod test { mock_info(GOVERNANCE_ADDRESS, &[]), ExecuteMsg::RegisterService { service_name: service_name.into(), - service_contract: Addr::unchecked("nowhere"), + coordinator_contract: Addr::unchecked("nowhere"), min_num_verifiers: 0, max_num_verifiers: Some(100), - min_verifier_bond, + min_verifier_bond: min_verifier_bond.try_into().unwrap(), bond_denom: AXL_DENOMINATION.into(), unbonding_period_days: 10, description: "Some service".into(), @@ -535,7 +589,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetActiveVerifiers { + QueryMsg::ActiveVerifiers { service_name: service_name.into(), chain_name, }, @@ -559,10 +613,10 @@ mod test { mock_info(GOVERNANCE_ADDRESS, &[]), ExecuteMsg::RegisterService { service_name: service_name.into(), - service_contract: Addr::unchecked("nowhere"), + coordinator_contract: Addr::unchecked(COORDINATOR_ADDRESS), min_num_verifiers: 0, max_num_verifiers: Some(100), - min_verifier_bond, + min_verifier_bond: min_verifier_bond.try_into().unwrap(), bond_denom: AXL_DENOMINATION.into(), unbonding_period_days: 10, description: "Some service".into(), @@ -627,7 +681,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetActiveVerifiers { + QueryMsg::ActiveVerifiers { service_name: service_name.into(), chain_name: chain, }, @@ -653,10 +707,10 @@ mod test { mock_info(GOVERNANCE_ADDRESS, &[]), ExecuteMsg::RegisterService { service_name: service_name.into(), - service_contract: Addr::unchecked("nowhere"), + coordinator_contract: Addr::unchecked(COORDINATOR_ADDRESS), min_num_verifiers: 0, max_num_verifiers: Some(100), - min_verifier_bond, + min_verifier_bond: min_verifier_bond.try_into().unwrap(), bond_denom: AXL_DENOMINATION.into(), unbonding_period_days: 10, description: "Some service".into(), @@ -723,7 +777,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetActiveVerifiers { + QueryMsg::ActiveVerifiers { service_name: service_name.into(), chain_name: deregistered_chain, }, @@ -739,7 +793,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetActiveVerifiers { + QueryMsg::ActiveVerifiers { service_name: service_name.into(), chain_name: chain.clone(), }, @@ -753,7 +807,7 @@ mod test { verifier_info: Verifier { address: Addr::unchecked(VERIFIER_ADDRESS), bonding_state: BondingState::Bonded { - amount: min_verifier_bond + amount: min_verifier_bond.try_into().unwrap(), }, authorization_state: AuthorizationState::Authorized, service_name: service_name.into() @@ -771,14 +825,14 @@ mod test { let mut deps = setup(); let service_name = "validators"; - let min_verifier_bond = Uint128::new(100); + let min_verifier_bond: nonempty::Uint128 = Uint128::new(100).try_into().unwrap(); let res = execute( deps.as_mut(), mock_env(), mock_info(GOVERNANCE_ADDRESS, &[]), ExecuteMsg::RegisterService { service_name: service_name.into(), - service_contract: Addr::unchecked("nowhere"), + coordinator_contract: Addr::unchecked(COORDINATOR_ADDRESS), min_num_verifiers: 0, max_num_verifiers: Some(100), min_verifier_bond, @@ -805,7 +859,7 @@ mod test { mock_env(), mock_info( VERIFIER_ADDRESS, - &coins(min_verifier_bond.u128(), AXL_DENOMINATION), + &coins(min_verifier_bond.into_inner().u128(), AXL_DENOMINATION), ), ExecuteMsg::BondVerifier { service_name: service_name.into(), @@ -842,7 +896,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetActiveVerifiers { + QueryMsg::ActiveVerifiers { service_name: service_name.into(), chain_name, }, @@ -873,14 +927,14 @@ mod test { let mut deps = setup(); let service_name = "validators"; - let min_verifier_bond = Uint128::new(100); + let min_verifier_bond: nonempty::Uint128 = Uint128::new(100).try_into().unwrap(); let res = execute( deps.as_mut(), mock_env(), mock_info(GOVERNANCE_ADDRESS, &[]), ExecuteMsg::RegisterService { service_name: service_name.into(), - service_contract: Addr::unchecked("nowhere"), + coordinator_contract: Addr::unchecked(COORDINATOR_ADDRESS), min_num_verifiers: 0, max_num_verifiers: Some(100), min_verifier_bond, @@ -907,7 +961,7 @@ mod test { mock_env(), mock_info( VERIFIER_ADDRESS, - &coins(min_verifier_bond.u128(), AXL_DENOMINATION), + &coins(min_verifier_bond.into_inner().u128(), AXL_DENOMINATION), ), ExecuteMsg::BondVerifier { service_name: service_name.into(), @@ -954,7 +1008,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetActiveVerifiers { + QueryMsg::ActiveVerifiers { service_name: service_name.into(), chain_name, }, @@ -985,14 +1039,14 @@ mod test { let mut deps = setup(); let service_name = "validators"; - let min_verifier_bond = Uint128::new(100); + let min_verifier_bond: nonempty::Uint128 = Uint128::new(100).try_into().unwrap(); let res = execute( deps.as_mut(), mock_env(), mock_info(GOVERNANCE_ADDRESS, &[]), ExecuteMsg::RegisterService { service_name: service_name.into(), - service_contract: Addr::unchecked("nowhere"), + coordinator_contract: Addr::unchecked(COORDINATOR_ADDRESS), min_num_verifiers: 0, max_num_verifiers: Some(100), min_verifier_bond, @@ -1019,7 +1073,7 @@ mod test { mock_env(), mock_info( VERIFIER_ADDRESS, - &coins(min_verifier_bond.u128(), AXL_DENOMINATION), + &coins(min_verifier_bond.into_inner().u128(), AXL_DENOMINATION), ), ExecuteMsg::BondVerifier { service_name: service_name.into(), @@ -1043,7 +1097,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetActiveVerifiers { + QueryMsg::ActiveVerifiers { service_name: service_name.into(), chain_name, }, @@ -1061,14 +1115,14 @@ mod test { let mut deps = setup(); let service_name = "validators"; - let min_verifier_bond = Uint128::new(100); + let min_verifier_bond: nonempty::Uint128 = Uint128::new(100).try_into().unwrap(); let res = execute( deps.as_mut(), mock_env(), mock_info(GOVERNANCE_ADDRESS, &[]), ExecuteMsg::RegisterService { service_name: service_name.into(), - service_contract: Addr::unchecked("nowhere"), + coordinator_contract: Addr::unchecked(COORDINATOR_ADDRESS), min_num_verifiers: 0, max_num_verifiers: Some(100), min_verifier_bond, @@ -1117,7 +1171,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetActiveVerifiers { + QueryMsg::ActiveVerifiers { service_name: service_name.into(), chain_name, }, @@ -1135,14 +1189,14 @@ mod test { let mut deps = setup(); let service_name = "validators"; - let min_verifier_bond = Uint128::new(100); + let min_verifier_bond: nonempty::Uint128 = Uint128::new(100).try_into().unwrap(); let res = execute( deps.as_mut(), mock_env(), mock_info(GOVERNANCE_ADDRESS, &[]), ExecuteMsg::RegisterService { service_name: service_name.into(), - service_contract: Addr::unchecked("nowhere"), + coordinator_contract: Addr::unchecked(COORDINATOR_ADDRESS), min_num_verifiers: 0, max_num_verifiers: Some(100), min_verifier_bond, @@ -1165,13 +1219,17 @@ mod test { ) .unwrap_err(); - assert_contract_err_strings_equal(err, ContractError::VerifierNotFound); + assert!(err_contains!( + err.report, + permission_control::Error, + permission_control::Error::WhitelistNotFound { .. } + )); let verifiers: Vec = from_json( query( deps.as_ref(), mock_env(), - QueryMsg::GetActiveVerifiers { + QueryMsg::ActiveVerifiers { service_name: service_name.into(), chain_name, }, @@ -1201,7 +1259,11 @@ mod test { ) .unwrap_err(); - assert_contract_err_strings_equal(err, ContractError::ServiceNotFound); + assert!(err_contains!( + err.report, + permission_control::Error, + permission_control::Error::WhitelistNotFound { .. } + )); } #[test] @@ -1209,14 +1271,14 @@ mod test { let mut deps = setup(); let service_name = "validators"; - let min_verifier_bond = Uint128::new(100); + let min_verifier_bond: nonempty::Uint128 = Uint128::new(100).try_into().unwrap(); let res = execute( deps.as_mut(), mock_env(), mock_info(GOVERNANCE_ADDRESS, &[]), ExecuteMsg::RegisterService { service_name: service_name.into(), - service_contract: Addr::unchecked("nowhere"), + coordinator_contract: Addr::unchecked(COORDINATOR_ADDRESS), min_num_verifiers: 0, max_num_verifiers: Some(100), min_verifier_bond, @@ -1243,7 +1305,7 @@ mod test { mock_env(), mock_info( VERIFIER_ADDRESS, - &coins(min_verifier_bond.u128(), AXL_DENOMINATION), + &coins(min_verifier_bond.into_inner().u128(), AXL_DENOMINATION), ), ExecuteMsg::BondVerifier { service_name: service_name.into(), @@ -1277,7 +1339,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetActiveVerifiers { + QueryMsg::ActiveVerifiers { service_name: service_name.into(), chain_name, }, @@ -1293,14 +1355,14 @@ mod test { let mut deps = setup(); let service_name = "validators"; - let min_verifier_bond = Uint128::new(100); + let min_verifier_bond: nonempty::Uint128 = Uint128::new(100).try_into().unwrap(); let res = execute( deps.as_mut(), mock_env(), mock_info(GOVERNANCE_ADDRESS, &[]), ExecuteMsg::RegisterService { service_name: service_name.into(), - service_contract: Addr::unchecked("nowhere"), + coordinator_contract: Addr::unchecked(COORDINATOR_ADDRESS), min_num_verifiers: 0, max_num_verifiers: Some(100), min_verifier_bond, @@ -1316,7 +1378,7 @@ mod test { mock_env(), mock_info( VERIFIER_ADDRESS, - &coins(min_verifier_bond.u128(), "funnydenom"), + &coins(min_verifier_bond.into_inner().u128(), "funnydenom"), ), ExecuteMsg::BondVerifier { service_name: service_name.into(), @@ -1324,7 +1386,11 @@ mod test { ) .unwrap_err(); - assert_contract_err_strings_equal(err, ContractError::WrongDenom); + assert!(err_contains!( + err.report, + ContractError, + ContractError::WrongDenom + )); } #[test] @@ -1332,14 +1398,14 @@ mod test { let mut deps = setup(); let service_name = "validators"; - let min_verifier_bond = Uint128::new(100); + let min_verifier_bond: nonempty::Uint128 = Uint128::new(100).try_into().unwrap(); let res = execute( deps.as_mut(), mock_env(), mock_info(GOVERNANCE_ADDRESS, &[]), ExecuteMsg::RegisterService { service_name: service_name.into(), - service_contract: Addr::unchecked("nowhere"), + coordinator_contract: Addr::unchecked(COORDINATOR_ADDRESS), min_num_verifiers: 0, max_num_verifiers: Some(100), min_verifier_bond, @@ -1355,7 +1421,7 @@ mod test { mock_env(), mock_info( VERIFIER_ADDRESS, - &coins(min_verifier_bond.u128(), AXL_DENOMINATION), + &coins(min_verifier_bond.into_inner().u128(), AXL_DENOMINATION), ), ExecuteMsg::BondVerifier { service_name: service_name.into(), @@ -1379,7 +1445,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetActiveVerifiers { + QueryMsg::ActiveVerifiers { service_name: service_name.into(), chain_name, }, @@ -1395,14 +1461,14 @@ mod test { let mut deps = setup(); let service_name = "validators"; - let min_verifier_bond = Uint128::new(100); + let min_verifier_bond: nonempty::Uint128 = Uint128::new(100).try_into().unwrap(); let res = execute( deps.as_mut(), mock_env(), mock_info(GOVERNANCE_ADDRESS, &[]), ExecuteMsg::RegisterService { service_name: service_name.into(), - service_contract: Addr::unchecked("nowhere"), + coordinator_contract: Addr::unchecked(COORDINATOR_ADDRESS), min_num_verifiers: 0, max_num_verifiers: Some(100), min_verifier_bond, @@ -1429,7 +1495,7 @@ mod test { mock_env(), mock_info( VERIFIER_ADDRESS, - &coins(min_verifier_bond.u128() / 2, AXL_DENOMINATION), + &coins(min_verifier_bond.into_inner().u128() / 2, AXL_DENOMINATION), ), ExecuteMsg::BondVerifier { service_name: service_name.into(), @@ -1453,7 +1519,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetActiveVerifiers { + QueryMsg::ActiveVerifiers { service_name: service_name.into(), chain_name, }, @@ -1469,14 +1535,14 @@ mod test { let mut deps = setup(); let service_name = "validators"; - let min_verifier_bond = Uint128::new(100); + let min_verifier_bond: nonempty::Uint128 = Uint128::new(100).try_into().unwrap(); let res = execute( deps.as_mut(), mock_env(), mock_info(GOVERNANCE_ADDRESS, &[]), ExecuteMsg::RegisterService { service_name: service_name.into(), - service_contract: Addr::unchecked("nowhere"), + coordinator_contract: Addr::unchecked(COORDINATOR_ADDRESS), min_num_verifiers: 0, max_num_verifiers: Some(100), min_verifier_bond, @@ -1492,7 +1558,7 @@ mod test { mock_env(), mock_info( VERIFIER_ADDRESS, - &coins(min_verifier_bond.u128(), AXL_DENOMINATION), + &coins(min_verifier_bond.into_inner().u128(), AXL_DENOMINATION), ), ExecuteMsg::BondVerifier { service_name: service_name.into(), @@ -1527,7 +1593,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetActiveVerifiers { + QueryMsg::ActiveVerifiers { service_name: service_name.into(), chain_name, }, @@ -1556,14 +1622,14 @@ mod test { let mut deps = setup(); let service_name = "validators"; - let min_verifier_bond = Uint128::new(100); + let min_verifier_bond: nonempty::Uint128 = Uint128::new(100).try_into().unwrap(); let res = execute( deps.as_mut(), mock_env(), mock_info(GOVERNANCE_ADDRESS, &[]), ExecuteMsg::RegisterService { service_name: service_name.into(), - service_contract: Addr::unchecked("nowhere"), + coordinator_contract: Addr::unchecked(COORDINATOR_ADDRESS), min_num_verifiers: 0, max_num_verifiers: Some(100), min_verifier_bond, @@ -1590,7 +1656,7 @@ mod test { mock_env(), mock_info( VERIFIER_ADDRESS, - &coins(min_verifier_bond.u128(), AXL_DENOMINATION), + &coins(min_verifier_bond.into_inner().u128(), AXL_DENOMINATION), ), ExecuteMsg::BondVerifier { service_name: service_name.into(), @@ -1634,7 +1700,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetActiveVerifiers { + QueryMsg::ActiveVerifiers { service_name: service_name.into(), chain_name, }, @@ -1662,7 +1728,7 @@ mod test { fn unbonding_period() { let mut deps = setup(); - let min_verifier_bond = Uint128::new(100); + let min_verifier_bond: nonempty::Uint128 = Uint128::new(100).try_into().unwrap(); let service_name = "validators"; let unbonding_period_days = 1; @@ -1672,7 +1738,7 @@ mod test { mock_info(GOVERNANCE_ADDRESS, &[]), ExecuteMsg::RegisterService { service_name: service_name.into(), - service_contract: Addr::unchecked("nowhere"), + coordinator_contract: Addr::unchecked(COORDINATOR_ADDRESS), min_num_verifiers: 0, max_num_verifiers: Some(100), min_verifier_bond, @@ -1699,7 +1765,7 @@ mod test { mock_env(), mock_info( VERIFIER_ADDRESS, - &coins(min_verifier_bond.u128(), AXL_DENOMINATION), + &coins(min_verifier_bond.into_inner().u128(), AXL_DENOMINATION), ), ExecuteMsg::BondVerifier { service_name: service_name.into(), @@ -1744,7 +1810,7 @@ mod test { assert!(res.is_err()); assert_eq!( res.unwrap_err().to_string(), - axelar_wasm_std::ContractError::from(ContractError::InvalidBondingState( + axelar_wasm_std::error::ContractError::from(ContractError::InvalidBondingState( BondingState::Unbonding { unbonded_at: unbond_request_env.block.time, amount: min_verifier_bond, @@ -1773,27 +1839,28 @@ mod test { res.messages[0].msg, CosmosMsg::Bank(BankMsg::Send { to_address: VERIFIER_ADDRESS.into(), - amount: coins(min_verifier_bond.u128(), AXL_DENOMINATION) + amount: coins(min_verifier_bond.into_inner().u128(), AXL_DENOMINATION) }) ) } #[test] - fn get_active_verifiers_should_not_return_less_than_min() { + #[allow(clippy::cast_possible_truncation)] + fn active_verifiers_should_not_return_less_than_min() { let mut deps = setup(); let verifiers = vec![Addr::unchecked("verifier1"), Addr::unchecked("verifier2")]; let min_num_verifiers = verifiers.len() as u16; let service_name = "validators"; - let min_verifier_bond = Uint128::new(100); + let min_verifier_bond: nonempty::Uint128 = Uint128::new(100).try_into().unwrap(); let _ = execute( deps.as_mut(), mock_env(), mock_info(GOVERNANCE_ADDRESS, &[]), ExecuteMsg::RegisterService { service_name: service_name.into(), - service_contract: Addr::unchecked("nowhere"), + coordinator_contract: Addr::unchecked(COORDINATOR_ADDRESS), min_num_verifiers, max_num_verifiers: Some(100), min_verifier_bond, @@ -1822,7 +1889,7 @@ mod test { let res = query( deps.as_ref(), mock_env(), - QueryMsg::GetActiveVerifiers { + QueryMsg::ActiveVerifiers { service_name: service_name.into(), chain_name: chain_name.clone(), }, @@ -1834,7 +1901,7 @@ mod test { mock_env(), mock_info( verifier.as_str(), - &coins(min_verifier_bond.u128(), AXL_DENOMINATION), + &coins(min_verifier_bond.into_inner().u128(), AXL_DENOMINATION), ), ExecuteMsg::BondVerifier { service_name: service_name.into(), @@ -1859,7 +1926,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetActiveVerifiers { + QueryMsg::ActiveVerifiers { service_name: service_name.into(), chain_name: chain_name.clone(), }, @@ -1882,7 +1949,7 @@ mod test { let res = query( deps.as_ref(), mock_env(), - QueryMsg::GetActiveVerifiers { + QueryMsg::ActiveVerifiers { service_name: service_name.into(), chain_name: chain_name.clone(), }, @@ -1896,7 +1963,7 @@ mod test { // register a service let service_name = "validators"; - let min_verifier_bond = Uint128::new(100); + let min_verifier_bond: nonempty::Uint128 = Uint128::new(100).try_into().unwrap(); let unbonding_period_days = 10; let res = execute( deps.as_mut(), @@ -1904,7 +1971,7 @@ mod test { mock_info(GOVERNANCE_ADDRESS, &[]), ExecuteMsg::RegisterService { service_name: service_name.into(), - service_contract: Addr::unchecked("service contract"), + coordinator_contract: Addr::unchecked(COORDINATOR_ADDRESS), min_num_verifiers: 0, max_num_verifiers: Some(100), min_verifier_bond, @@ -1922,7 +1989,7 @@ mod test { mock_env(), mock_info( verifier1.as_str(), - &coins(min_verifier_bond.u128(), AXL_DENOMINATION), + &coins(min_verifier_bond.into_inner().u128(), AXL_DENOMINATION), ), ExecuteMsg::BondVerifier { service_name: service_name.into(), @@ -1952,7 +2019,11 @@ mod test { }, ) .unwrap_err(); - assert_contract_err_strings_equal(err, ContractError::VerifierJailed); + assert!(err_contains!( + err.report, + ContractError, + ContractError::VerifierJailed + )); // given a verifier passed unbonding period let verifier2 = Addr::unchecked("verifier-2"); @@ -1963,7 +2034,7 @@ mod test { mock_env(), mock_info( verifier2.as_str(), - &coins(min_verifier_bond.u128(), AXL_DENOMINATION), + &coins(min_verifier_bond.into_inner().u128(), AXL_DENOMINATION), ), ExecuteMsg::BondVerifier { service_name: service_name.into(), @@ -1988,7 +2059,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetVerifier { + QueryMsg::Verifier { service_name: service_name.into(), verifier: verifier2.to_string(), }, @@ -2034,6 +2105,10 @@ mod test { }, ) .unwrap_err(); - assert_contract_err_strings_equal(err, ContractError::VerifierJailed); + assert!(err_contains!( + err.report, + ContractError, + ContractError::VerifierJailed + )); } } diff --git a/contracts/service-registry/src/contract/execute.rs b/contracts/service-registry/src/contract/execute.rs index 32e34a116..eb3bba305 100644 --- a/contracts/service-registry/src/contract/execute.rs +++ b/contracts/service-registry/src/contract/execute.rs @@ -1,25 +1,17 @@ -use crate::state::{self, Verifier}; -use crate::state::{AuthorizationState, VERIFIERS}; +use axelar_wasm_std::nonempty; use router_api::ChainName; use super::*; - -pub fn require_governance(deps: &DepsMut, info: MessageInfo) -> Result<(), ContractError> { - let config = CONFIG.load(deps.storage)?; - if config.governance != info.sender { - return Err(ContractError::Unauthorized); - } - Ok(()) -} +use crate::state::{self, AuthorizationState, Verifier, VERIFIERS}; #[allow(clippy::too_many_arguments)] pub fn register_service( deps: DepsMut, service_name: String, - service_contract: Addr, + coordinator_contract: Addr, min_num_verifiers: u16, max_num_verifiers: Option, - min_verifier_bond: Uint128, + min_verifier_bond: nonempty::Uint128, bond_denom: String, unbonding_period_days: u16, description: String, @@ -33,7 +25,7 @@ pub fn register_service( match service { None => Ok(Service { name: service_name, - service_contract, + coordinator_contract, min_num_verifiers, max_num_verifiers, min_verifier_bond, @@ -93,14 +85,17 @@ pub fn bond_verifier( .may_load(deps.storage, &service_name)? .ok_or(ContractError::ServiceNotFound)?; - let bond = if !info.funds.is_empty() { - info.funds - .iter() - .find(|coin| coin.denom == service.bond_denom) - .ok_or(ContractError::WrongDenom)? - .amount + let bond: Option = if !info.funds.is_empty() { + Some( + info.funds + .iter() + .find(|coin| coin.denom == service.bond_denom) + .ok_or(ContractError::WrongDenom)? + .amount + .try_into()?, + ) } else { - Uint128::zero() // sender can rebond currently unbonding funds by just sending no new funds + None // sender can rebond currently unbonding funds by just sending no new funds }; VERIFIERS.update( @@ -108,10 +103,12 @@ pub fn bond_verifier( (&service_name.clone(), &info.sender.clone()), |sw| -> Result { match sw { - Some(verifier) => Ok(verifier.add_bond(bond)?), + Some(verifier) => Ok(verifier.bond(bond)?), None => Ok(Verifier { address: info.sender, - bonding_state: BondingState::Bonded { amount: bond }, + bonding_state: BondingState::Bonded { + amount: bond.ok_or(ContractError::NoFundsToBond)?, + }, authorization_state: AuthorizationState::NotAuthorized, service_name, }), @@ -132,11 +129,12 @@ pub fn register_chains_support( .may_load(deps.storage, &service_name)? .ok_or(ContractError::ServiceNotFound)?; - VERIFIERS - .may_load(deps.storage, (&service_name, &info.sender))? - .ok_or(ContractError::VerifierNotFound)?; - - state::register_chains_support(deps.storage, service_name.clone(), chains, info.sender)?; + state::register_chains_support( + deps.storage, + service_name.clone(), + chains.clone(), + info.sender.clone(), + )?; Ok(Response::new()) } @@ -151,10 +149,6 @@ pub fn deregister_chains_support( .may_load(deps.storage, &service_name)? .ok_or(ContractError::ServiceNotFound)?; - VERIFIERS - .may_load(deps.storage, (&service_name, &info.sender))? - .ok_or(ContractError::VerifierNotFound)?; - state::deregister_chains_support(deps.storage, service_name.clone(), chains, info.sender)?; Ok(Response::new()) @@ -166,7 +160,7 @@ pub fn unbond_verifier( info: MessageInfo, service_name: String, ) -> Result { - SERVICES + let service = SERVICES .may_load(deps.storage, &service_name)? .ok_or(ContractError::ServiceNotFound)?; @@ -174,9 +168,15 @@ pub fn unbond_verifier( .may_load(deps.storage, (&service_name, &info.sender))? .ok_or(ContractError::VerifierNotFound)?; - let can_unbond = true; // TODO: actually query the service to determine this value + let query = coordinator::msg::QueryMsg::ReadyToUnbond { + worker_address: verifier.address.clone(), + }; + let ready_to_unbond = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { + contract_addr: service.coordinator_contract.into(), + msg: to_json_binary(&query)?, + }))?; - let verifier = verifier.unbond(can_unbond, env.block.time)?; + let verifier = verifier.unbond(ready_to_unbond, env.block.time)?; VERIFIERS.save(deps.storage, (&service_name, &info.sender), &verifier)?; @@ -206,8 +206,8 @@ pub fn claim_stake( to_address: info.sender.into(), amount: [Coin { denom: service.bond_denom, - amount: released_bond, + amount: released_bond.into(), }] - .to_vec(), // TODO: isolate coins + .to_vec(), })) } diff --git a/contracts/service-registry/src/contract/migrations/mod.rs b/contracts/service-registry/src/contract/migrations/mod.rs new file mode 100644 index 000000000..080e953e7 --- /dev/null +++ b/contracts/service-registry/src/contract/migrations/mod.rs @@ -0,0 +1 @@ +pub mod v0_4_1; diff --git a/contracts/service-registry/src/contract/migrations/v0_4_1.rs b/contracts/service-registry/src/contract/migrations/v0_4_1.rs new file mode 100644 index 000000000..b06e781c8 --- /dev/null +++ b/contracts/service-registry/src/contract/migrations/v0_4_1.rs @@ -0,0 +1,117 @@ +#![allow(deprecated)] +use axelar_wasm_std::error::ContractError; +use axelar_wasm_std::permission_control; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, StdResult, Storage}; +use cw_storage_plus::Item; + +use crate::contract::CONTRACT_NAME; + +const BASE_VERSION: &str = "0.4.1"; +pub fn migrate(storage: &mut dyn Storage) -> Result<(), ContractError> { + cw2::assert_contract_version(storage, CONTRACT_NAME, BASE_VERSION)?; + + let config = CONFIG.load(storage)?; + delete_config(storage); + migrate_permission_control(storage, config)?; + + Ok(()) +} + +fn migrate_permission_control(storage: &mut dyn Storage, config: Config) -> StdResult<()> { + permission_control::set_governance(storage, &config.governance) +} + +fn delete_config(storage: &mut dyn Storage) { + CONFIG.remove(storage) +} + +#[cw_serde] +#[deprecated(since = "0.4.1", note = "Only used during migrations")] +pub struct Config { + pub governance: Addr, +} + +#[deprecated(since = "0.4.1", note = "Only used during migrations")] +pub const CONFIG: Item = Item::new("config"); + +#[cfg(test)] +mod tests { + use axelar_wasm_std::permission_control; + use axelar_wasm_std::permission_control::Permission; + use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; + use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, Response}; + + use crate::contract::migrations::v0_4_1; + use crate::contract::CONTRACT_NAME; + use crate::msg::InstantiateMsg; + + const GOVERNANCE: &str = "governance"; + + #[test] + fn migrate_checks_contract_version() { + let mut deps = mock_dependencies(); + instantiate_contract(deps.as_mut()); + + cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, "something wrong").unwrap(); + + assert!(v0_4_1::migrate(deps.as_mut().storage).is_err()); + + cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, v0_4_1::BASE_VERSION) + .unwrap(); + + assert!(v0_4_1::migrate(deps.as_mut().storage).is_ok()); + } + #[test] + fn migrate_to_permission_control() { + let mut deps = mock_dependencies(); + instantiate_contract(deps.as_mut()); + + assert!(v0_4_1::migrate(deps.as_mut().storage).is_ok()); + + assert!( + permission_control::sender_role(&deps.storage, &Addr::unchecked(GOVERNANCE)) + .unwrap() + .contains(Permission::Governance) + ); + } + + #[test] + fn migrate_deletes_config() { + let mut deps = mock_dependencies(); + instantiate_contract(deps.as_mut()); + + assert!(v0_4_1::migrate(deps.as_mut().storage).is_ok()); + + assert!(v0_4_1::CONFIG.load(&deps.storage).is_err()) + } + + fn instantiate_contract(deps: DepsMut) { + instantiate( + deps, + mock_env(), + mock_info("admin", &[]), + InstantiateMsg { + governance_account: GOVERNANCE.to_string(), + }, + ) + .unwrap(); + } + #[deprecated(since = "0.4.1", note = "Only used to test the migration")] + fn instantiate( + deps: DepsMut, + _env: Env, + _info: MessageInfo, + msg: InstantiateMsg, + ) -> Result { + cw2::set_contract_version(deps.storage, CONTRACT_NAME, v0_4_1::BASE_VERSION)?; + + v0_4_1::CONFIG.save( + deps.storage, + &v0_4_1::Config { + governance: deps.api.addr_validate(&msg.governance_account)?, + }, + )?; + Ok(Response::default()) + } +} diff --git a/contracts/service-registry/src/contract/query.rs b/contracts/service-registry/src/contract/query.rs index 051f6615b..8e48a3b5a 100644 --- a/contracts/service-registry/src/contract/query.rs +++ b/contracts/service-registry/src/contract/query.rs @@ -1,10 +1,9 @@ use router_api::ChainName; -use crate::state::{WeightedVerifier, VERIFIERS, VERIFIERS_PER_CHAIN, VERIFIER_WEIGHT}; - use super::*; +use crate::state::{WeightedVerifier, VERIFIERS, VERIFIERS_PER_CHAIN, VERIFIER_WEIGHT}; -pub fn get_active_verifiers( +pub fn active_verifiers( deps: Deps, service_name: String, chain_name: ChainName, @@ -37,20 +36,24 @@ pub fn get_active_verifiers( } } -pub fn get_verifier( +pub fn verifier( deps: Deps, service_name: String, verifier: String, -) -> Result { +) -> Result { VERIFIERS .may_load( deps.storage, - (&service_name, &deps.api.addr_validate(&verifier)?), + ( + &service_name, + &address::validate_cosmwasm_address(deps.api, &verifier)?, + ), )? - .ok_or(ContractError::VerifierNotFound) + .ok_or(ContractError::VerifierNotFound)? + .then(Ok) } -pub fn get_service(deps: Deps, service_name: String) -> Result { +pub fn service(deps: Deps, service_name: String) -> Result { SERVICES .may_load(deps.storage, &service_name)? .ok_or(ContractError::ServiceNotFound) diff --git a/contracts/service-registry/src/error.rs b/contracts/service-registry/src/error.rs index ad86e6b44..2a524370a 100644 --- a/contracts/service-registry/src/error.rs +++ b/contracts/service-registry/src/error.rs @@ -1,5 +1,4 @@ -use axelar_wasm_std::nonempty; -use axelar_wasm_std_derive::IntoContractError; +use axelar_wasm_std::{nonempty, IntoContractError}; use cosmwasm_std::{OverflowError, StdError}; use thiserror::Error; @@ -30,6 +29,8 @@ pub enum ContractError { VerifierNotFound, #[error("invalid bonding state `{0:?}` for this operation")] InvalidBondingState(BondingState), + #[error("no attached funds to bond")] + NoFundsToBond, #[error("not enough verifiers")] NotEnoughVerifiers, #[error("verifier is jailed")] diff --git a/contracts/service-registry/src/helpers.rs b/contracts/service-registry/src/helpers.rs index 7247c3fdf..8ba4bfa8f 100644 --- a/contracts/service-registry/src/helpers.rs +++ b/contracts/service-registry/src/helpers.rs @@ -1,8 +1,7 @@ +use cosmwasm_std::{to_json_binary, Addr, CosmosMsg, StdResult, WasmMsg}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use cosmwasm_std::{to_json_binary, Addr, CosmosMsg, StdResult, WasmMsg}; - use crate::msg::ExecuteMsg; /// ServiceRegistry is a wrapper around Addr that provides a lot of helpers diff --git a/contracts/service-registry/src/lib.rs b/contracts/service-registry/src/lib.rs index f379c0cbc..d439e2a24 100644 --- a/contracts/service-registry/src/lib.rs +++ b/contracts/service-registry/src/lib.rs @@ -1,8 +1,11 @@ pub mod contract; pub mod error; pub mod helpers; -mod migrations; pub mod msg; -pub mod state; +mod state; + +pub use state::{ + AuthorizationState, BondingState, Service, Verifier, WeightedVerifier, VERIFIER_WEIGHT, +}; pub use crate::error::ContractError; diff --git a/contracts/service-registry/src/migrations/mod.rs b/contracts/service-registry/src/migrations/mod.rs deleted file mode 100644 index 194e2b5c7..000000000 --- a/contracts/service-registry/src/migrations/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod v_0_3; diff --git a/contracts/service-registry/src/migrations/v_0_3.rs b/contracts/service-registry/src/migrations/v_0_3.rs deleted file mode 100644 index bc429b37d..000000000 --- a/contracts/service-registry/src/migrations/v_0_3.rs +++ /dev/null @@ -1,250 +0,0 @@ -//! Migrate includes: -//! - rename min_num_workers, max_num_workers and min_worker_bond fields in 'Service` struct -//! - rename WORKERS_PER_CHAIN to VERIFIERS_PER_CHAIN -//! - rename WORKERS to VERIFIERS - -use cosmwasm_std::{DepsMut, Order, Response, Storage}; - -use crate::error::ContractError; -use crate::state::{Service, Verifier, SERVICES, VERIFIERS, VERIFIERS_PER_CHAIN}; - -mod v0_2_state { - use cosmwasm_schema::cw_serde; - use cosmwasm_std::{Addr, Uint128}; - use cw_storage_plus::Map; - use router_api::ChainName; - - use crate::state::{AuthorizationState, BondingState}; - - #[cw_serde] - pub struct Service { - pub name: String, - pub service_contract: Addr, - pub min_num_workers: u16, - pub max_num_workers: Option, - pub min_worker_bond: Uint128, - pub bond_denom: String, - pub unbonding_period_days: u16, - pub description: String, - } - - #[cw_serde] - pub struct Worker { - pub address: Addr, - pub bonding_state: BondingState, - pub authorization_state: AuthorizationState, - pub service_name: String, - } - - type ServiceName = str; - type WorkerAddress = Addr; - - pub const SERVICES: Map<&ServiceName, Service> = Map::new("services"); - pub const WORKERS_PER_CHAIN: Map<(&ServiceName, &ChainName, &WorkerAddress), ()> = - Map::new("workers_per_chain"); - pub const WORKERS: Map<(&ServiceName, &WorkerAddress), Worker> = Map::new("workers"); -} - -pub fn migrate(deps: DepsMut) -> Result { - migrate_services(deps.storage)?; - migrate_workers_per_chain(deps.storage)?; - migrate_workers(deps.storage)?; - - Ok(Response::new()) -} - -fn migrate_services(store: &mut dyn Storage) -> Result<(), ContractError> { - let keys_and_services = v0_2_state::SERVICES - .range(store, None, None, Order::Ascending) - .map(|result| { - result.map(|(service_name, service)| { - ( - service_name, - Service { - name: service.name, - service_contract: service.service_contract, - min_num_verifiers: service.min_num_workers, - max_num_verifiers: service.max_num_workers, - min_verifier_bond: service.min_worker_bond, - bond_denom: service.bond_denom, - unbonding_period_days: service.unbonding_period_days, - description: service.description, - }, - ) - }) - }) - .collect::, _>>()?; - - for (service_name, new_service) in keys_and_services { - SERVICES.save(store, &service_name, &new_service)?; - } - - Ok(()) -} - -fn migrate_workers_per_chain(store: &mut dyn Storage) -> Result<(), ContractError> { - let keys_and_value_pairs = v0_2_state::WORKERS_PER_CHAIN - .range(store, None, None, Order::Ascending) - .collect::, _>>()?; - - for ((service_name, chain_name, verifier_address), _) in keys_and_value_pairs { - let key = (service_name.as_str(), &chain_name, &verifier_address); - VERIFIERS_PER_CHAIN.save(store, key, &())?; - v0_2_state::WORKERS_PER_CHAIN.remove(store, key); - } - - Ok(()) -} - -fn migrate_workers(store: &mut dyn Storage) -> Result<(), ContractError> { - let keys_and_workers = v0_2_state::WORKERS - .range(store, None, None, Order::Ascending) - .collect::, _>>()?; - - for ((service_name, verifier_address), worker) in keys_and_workers { - let key = (service_name.as_str(), &verifier_address); - let verifier = Verifier { - address: worker.address, - bonding_state: worker.bonding_state, - authorization_state: worker.authorization_state, - service_name: worker.service_name, - }; - VERIFIERS.save(store, key, &verifier)?; - v0_2_state::WORKERS.remove(store, key); - } - - Ok(()) -} - -#[cfg(test)] -mod test { - use cosmwasm_std::{testing::mock_dependencies, Addr, Storage, Uint128}; - - use router_api::ChainName; - - use crate::{ - migrations::v_0_3::{migrate, v0_2_state}, - state::{ - AuthorizationState, BondingState, Service, SERVICES, VERIFIERS, VERIFIERS_PER_CHAIN, - }, - }; - - #[test] - #[allow(clippy::arithmetic_side_effects)] - fn migration() { - let mut deps = mock_dependencies(); - - let service = v0_2_state::Service { - name: "service".to_string(), - service_contract: Addr::unchecked("service_contract"), - min_num_workers: 1, - max_num_workers: Some(2), - min_worker_bond: 100u128.into(), - bond_denom: "uaxl".to_string(), - unbonding_period_days: 10, - description: "description".to_string(), - }; - - let verifiers = vec![ - Addr::unchecked("verifier1"), - Addr::unchecked("verifier2"), - Addr::unchecked("verifier3"), - Addr::unchecked("verifier4"), - ]; - - let chains: Vec = vec![ - "chain1".parse().unwrap(), - "chain2".parse().unwrap(), - "chain3".parse().unwrap(), - "chain4".parse().unwrap(), - ]; - - set_up_v0_2_state( - &mut deps.storage, - service.clone(), - verifiers.clone(), - chains.clone(), - ); - - migrate(deps.as_mut()).unwrap(); - - // verify new state - assert_eq!( - SERVICES.load(&deps.storage, &service.name).unwrap(), - Service { - name: service.name, - service_contract: service.service_contract, - min_num_verifiers: service.min_num_workers, - max_num_verifiers: service.max_num_workers, - min_verifier_bond: service.min_worker_bond, - bond_denom: service.bond_denom, - unbonding_period_days: service.unbonding_period_days, - description: service.description, - } - ); - - assert_eq!( - VERIFIERS_PER_CHAIN - .range(&deps.storage, None, None, cosmwasm_std::Order::Ascending) - .count(), - chains.len() * verifiers.len() - ); - - assert_eq!( - VERIFIERS - .range(&deps.storage, None, None, cosmwasm_std::Order::Ascending) - .count(), - verifiers.len() - ); - } - - #[allow(clippy::arithmetic_side_effects)] - fn set_up_v0_2_state( - store: &mut dyn Storage, - service: v0_2_state::Service, - verifiers: Vec, - chains: Vec, - ) { - v0_2_state::SERVICES - .save(store, &service.name, &service) - .unwrap(); - - for verifier in verifiers.iter() { - for chain in chains.iter() { - v0_2_state::WORKERS_PER_CHAIN - .save(store, (&service.name, chain, &verifier), &()) - .unwrap(); - } - - let worker = v0_2_state::Worker { - address: verifier.clone(), - bonding_state: BondingState::Bonded { - amount: Uint128::from(1000000u128), - }, - authorization_state: AuthorizationState::Authorized, - service_name: service.name.clone(), - }; - v0_2_state::WORKERS - .save(store, (&service.name, verifier), &worker) - .unwrap(); - } - - // verify state set up correctly - assert_eq!( - v0_2_state::SERVICES.load(store, &service.name).unwrap(), - service - ); - assert_eq!( - v0_2_state::WORKERS_PER_CHAIN - .range(store, None, None, cosmwasm_std::Order::Ascending) - .count(), - chains.len() * verifiers.len() - ); - assert_eq!( - v0_2_state::WORKERS - .range(store, None, None, cosmwasm_std::Order::Ascending) - .count(), - verifiers.len() - ); - } -} diff --git a/contracts/service-registry/src/msg.rs b/contracts/service-registry/src/msg.rs index bf0e3a6b1..e3c6b2ed6 100644 --- a/contracts/service-registry/src/msg.rs +++ b/contracts/service-registry/src/msg.rs @@ -1,5 +1,7 @@ +use axelar_wasm_std::nonempty; use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{Addr, Uint128}; +use cosmwasm_std::Addr; +use msgs_derive::EnsurePermissions; use router_api::ChainName; #[cw_serde] @@ -8,74 +10,83 @@ pub struct InstantiateMsg { } #[cw_serde] +#[derive(EnsurePermissions)] pub enum ExecuteMsg { - // Can only be called by governance account + /// Can only be called by governance account + #[permission(Governance)] RegisterService { service_name: String, - service_contract: Addr, + coordinator_contract: Addr, min_num_verifiers: u16, max_num_verifiers: Option, - min_verifier_bond: Uint128, + min_verifier_bond: nonempty::Uint128, bond_denom: String, unbonding_period_days: u16, // number of days to wait after starting unbonding before allowed to claim stake description: String, }, - // Authorizes verifiers to join a service. Can only be called by governance account. Verifiers must still bond sufficient stake to participate. + /// Authorizes verifiers to join a service. Can only be called by governance account. Verifiers must still bond sufficient stake to participate. + #[permission(Governance)] AuthorizeVerifiers { verifiers: Vec, service_name: String, }, - // Revoke authorization for specified verifiers. Can only be called by governance account. Verifiers bond remains unchanged + /// Revoke authorization for specified verifiers. Can only be called by governance account. Verifiers bond remains unchanged + #[permission(Governance)] UnauthorizeVerifiers { verifiers: Vec, service_name: String, }, - // Jail verifiers. Can only be called by governance account. Jailed verifiers are not allowed to unbond or claim stake. + /// Jail verifiers. Can only be called by governance account. Jailed verifiers are not allowed to unbond or claim stake. + #[permission(Governance)] JailVerifiers { verifiers: Vec, service_name: String, }, - // Register support for the specified chains. Called by the verifier. + /// Register support for the specified chains. Called by the verifier. + #[permission(Specific(verifier))] RegisterChainSupport { service_name: String, chains: Vec, }, - // Deregister support for the specified chains. Called by the verifier. + /// Deregister support for the specified chains. Called by the verifier. + #[permission(Specific(verifier))] DeregisterChainSupport { service_name: String, chains: Vec, }, - // Locks up any funds sent with the message as stake. Called by the verifier. - BondVerifier { - service_name: String, - }, - // Initiates unbonding of staked funds. Called by the verifier. - UnbondVerifier { - service_name: String, - }, - // Claim previously staked funds that have finished unbonding. Called by the verifier. - ClaimStake { - service_name: String, - }, + /// Locks up any funds sent with the message as stake. Marks the sender as a potential verifier that can be authorized. + #[permission(Any)] + BondVerifier { service_name: String }, + /// Initiates unbonding of staked funds for the sender. + #[permission(Any)] + UnbondVerifier { service_name: String }, + /// Claim previously staked funds that have finished unbonding for the sender. + #[permission(Any)] + ClaimStake { service_name: String }, } #[cw_serde] #[derive(QueryResponses)] pub enum QueryMsg { #[returns(Vec)] - GetActiveVerifiers { + ActiveVerifiers { service_name: String, chain_name: ChainName, }, #[returns(crate::state::Service)] - GetService { service_name: String }, + Service { service_name: String }, #[returns(crate::state::Verifier)] - GetVerifier { + Verifier { service_name: String, verifier: String, }, } + +#[cw_serde] +pub struct MigrateMsg { + pub coordinator_contract: Addr, +} diff --git a/contracts/service-registry/src/state.rs b/contracts/service-registry/src/state.rs index 4004c556e..cf1de2cd4 100644 --- a/contracts/service-registry/src/state.rs +++ b/contracts/service-registry/src/state.rs @@ -1,29 +1,21 @@ +use axelar_wasm_std::nonempty; +use axelar_wasm_std::snapshot::Participant; use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, Storage, Timestamp, Uint128}; +use cw_storage_plus::Map; use router_api::ChainName; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use cosmwasm_std::{Addr, Storage, Timestamp, Uint128}; -use cw_storage_plus::{Item, Map}; - -#[cw_serde] -pub struct Config { - pub governance: Addr, -} - -pub const CONFIG: Item = Item::new("config"); - -use axelar_wasm_std::{nonempty, snapshot::Participant}; - use crate::ContractError; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] pub struct Service { pub name: String, - pub service_contract: Addr, + pub coordinator_contract: Addr, pub min_num_verifiers: u16, pub max_num_verifiers: Option, - pub min_verifier_bond: Uint128, + pub min_verifier_bond: nonempty::Uint128, pub bond_denom: String, // should be set to a duration longer than the voting period for governance proposals, // otherwise a verifier could bail before they get penalized @@ -40,27 +32,25 @@ pub struct Verifier { } impl Verifier { - pub fn add_bond(self, to_add: Uint128) -> Result { - let amount = match self.bonding_state { + pub fn bond(self, to_add: Option) -> Result { + let amount: nonempty::Uint128 = match self.bonding_state { BondingState::Bonded { amount } | BondingState::RequestedUnbonding { amount } | BondingState::Unbonding { amount, unbonded_at: _, } => amount - .checked_add(to_add) - .map_err(ContractError::Overflow)?, - BondingState::Unbonded => to_add, + .into_inner() + .checked_add(to_add.map(Uint128::from).unwrap_or(Uint128::zero())) + .map_err(ContractError::Overflow)? + .try_into()?, + BondingState::Unbonded => to_add.ok_or(ContractError::NoFundsToBond)?, }; - if amount.is_zero() { - Err(ContractError::InvalidBondingState(self.bonding_state)) - } else { - Ok(Self { - bonding_state: BondingState::Bonded { amount }, - ..self - }) - } + Ok(Self { + bonding_state: BondingState::Bonded { amount }, + ..self + }) } pub fn unbond(self, can_unbond: bool, time: Timestamp) -> Result { @@ -92,7 +82,7 @@ impl Verifier { self, time: Timestamp, unbonding_period_days: u64, - ) -> Result<(Self, Uint128), ContractError> { + ) -> Result<(Self, nonempty::Uint128), ContractError> { if self.authorization_state == AuthorizationState::Jailed { return Err(ContractError::VerifierJailed); } @@ -134,13 +124,13 @@ impl From for Participant { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] pub enum BondingState { Bonded { - amount: Uint128, + amount: nonempty::Uint128, }, RequestedUnbonding { - amount: Uint128, + amount: nonempty::Uint128, }, Unbonding { - amount: Uint128, + amount: nonempty::Uint128, unbonded_at: Timestamp, }, Unbonded, @@ -189,9 +179,12 @@ pub fn deregister_chains_support( #[cfg(test)] mod tests { - use super::*; + use std::str::FromStr; + use std::vec; + use cosmwasm_std::testing::mock_dependencies; - use std::{str::FromStr, vec}; + + use super::*; #[test] fn register_single_verifier_chain_single_call_success() { @@ -327,18 +320,18 @@ mod tests { let verifier = Verifier { address: Addr::unchecked("verifier"), bonding_state: BondingState::Bonded { - amount: Uint128::from(100u32), + amount: Uint128::from(100u32).try_into().unwrap(), }, authorization_state: AuthorizationState::Authorized, service_name: "validators".to_string(), }; - let res = verifier.add_bond(Uint128::from(200u32)); + let res = verifier.bond(Some(Uint128::from(200u32).try_into().unwrap())); assert!(res.is_ok()); assert_eq!( res.unwrap().bonding_state, BondingState::Bonded { - amount: Uint128::from(300u32) + amount: Uint128::from(300u32).try_into().unwrap() } ); } @@ -348,18 +341,18 @@ mod tests { let verifier = Verifier { address: Addr::unchecked("verifier"), bonding_state: BondingState::RequestedUnbonding { - amount: Uint128::from(100u32), + amount: Uint128::from(100u32).try_into().unwrap(), }, authorization_state: AuthorizationState::Authorized, service_name: "validators".to_string(), }; - let res = verifier.add_bond(Uint128::from(200u32)); + let res = verifier.bond(Some(Uint128::from(200u32).try_into().unwrap())); assert!(res.is_ok()); assert_eq!( res.unwrap().bonding_state, BondingState::Bonded { - amount: Uint128::from(300u32) + amount: Uint128::from(300u32).try_into().unwrap() } ); } @@ -369,19 +362,19 @@ mod tests { let verifier = Verifier { address: Addr::unchecked("verifier"), bonding_state: BondingState::Unbonding { - amount: Uint128::from(100u32), + amount: Uint128::from(100u32).try_into().unwrap(), unbonded_at: Timestamp::from_nanos(0), }, authorization_state: AuthorizationState::Authorized, service_name: "validators".to_string(), }; - let res = verifier.add_bond(Uint128::from(200u32)); + let res = verifier.bond(Some(Uint128::from(200u32).try_into().unwrap())); assert!(res.is_ok()); assert_eq!( res.unwrap().bonding_state, BondingState::Bonded { - amount: Uint128::from(300u32) + amount: Uint128::from(300u32).try_into().unwrap() } ); } @@ -395,12 +388,12 @@ mod tests { service_name: "validators".to_string(), }; - let res = verifier.add_bond(Uint128::from(200u32)); + let res = verifier.bond(Some(Uint128::from(200u32).try_into().unwrap())); assert!(res.is_ok()); assert_eq!( res.unwrap().bonding_state, BondingState::Bonded { - amount: Uint128::from(200u32) + amount: Uint128::from(200u32).try_into().unwrap() } ); } @@ -416,12 +409,28 @@ mod tests { service_name: "validators".to_string(), }; - let res = verifier.add_bond(Uint128::from(0u32)); + let res = verifier.bond(None); assert!(res.is_err()); - assert_eq!( - res.unwrap_err(), - ContractError::InvalidBondingState(bonding_state) - ); + assert_eq!(res.unwrap_err(), ContractError::NoFundsToBond); + } + #[test] + fn test_zero_bond_rebond() { + let amount = nonempty::Uint128::try_from(100u128).unwrap(); + let bonding_state = BondingState::Unbonding { + amount, + unbonded_at: Timestamp::from_nanos(0), + }; + + let verifier = Verifier { + address: Addr::unchecked("verifier"), + bonding_state: bonding_state.clone(), + authorization_state: AuthorizationState::Authorized, + service_name: "validators".to_string(), + }; + + let res = verifier.bond(None); + assert!(res.is_ok()); + assert_eq!(res.unwrap().bonding_state, BondingState::Bonded { amount }); } #[test] @@ -429,7 +438,7 @@ mod tests { let verifier = Verifier { address: Addr::unchecked("verifier"), bonding_state: BondingState::Bonded { - amount: Uint128::from(100u32), + amount: Uint128::from(100u32).try_into().unwrap(), }, authorization_state: AuthorizationState::Authorized, service_name: "validators".to_string(), @@ -441,7 +450,7 @@ mod tests { assert_eq!( res.unwrap().bonding_state, BondingState::Unbonding { - amount: Uint128::from(100u32), + amount: Uint128::from(100u32).try_into().unwrap(), unbonded_at } ); @@ -452,7 +461,7 @@ mod tests { let verifier = Verifier { address: Addr::unchecked("verifier"), bonding_state: BondingState::Bonded { - amount: Uint128::from(100u32), + amount: Uint128::from(100u32).try_into().unwrap(), }, authorization_state: AuthorizationState::Authorized, service_name: "validators".to_string(), @@ -464,7 +473,7 @@ mod tests { assert_eq!( res.unwrap().bonding_state, BondingState::RequestedUnbonding { - amount: Uint128::from(100u32) + amount: Uint128::from(100u32).try_into().unwrap() } ); } @@ -474,7 +483,7 @@ mod tests { let verifier = Verifier { address: Addr::unchecked("verifier"), bonding_state: BondingState::RequestedUnbonding { - amount: Uint128::from(100u32), + amount: Uint128::from(100u32).try_into().unwrap(), }, authorization_state: AuthorizationState::Authorized, service_name: "validators".to_string(), @@ -486,7 +495,7 @@ mod tests { assert_eq!( res.unwrap().bonding_state, BondingState::Unbonding { - amount: Uint128::from(100u32), + amount: Uint128::from(100u32).try_into().unwrap(), unbonded_at } ); @@ -497,7 +506,7 @@ mod tests { let verifier = Verifier { address: Addr::unchecked("verifier"), bonding_state: BondingState::RequestedUnbonding { - amount: Uint128::from(100u32), + amount: Uint128::from(100u32).try_into().unwrap(), }, authorization_state: AuthorizationState::Authorized, service_name: "validators".to_string(), @@ -509,7 +518,7 @@ mod tests { assert_eq!( res.unwrap().bonding_state, BondingState::RequestedUnbonding { - amount: Uint128::from(100u32) + amount: Uint128::from(100u32).try_into().unwrap(), } ); } @@ -517,7 +526,7 @@ mod tests { #[test] fn test_unbonding_unbond() { let bonding_state = BondingState::Unbonding { - amount: Uint128::from(100u32), + amount: Uint128::from(100u32).try_into().unwrap(), unbonded_at: Timestamp::from_nanos(0), }; @@ -572,7 +581,7 @@ mod tests { #[test] fn test_bonded_claim_stake() { let bonding_state = BondingState::Bonded { - amount: Uint128::from(100u32), + amount: Uint128::from(100u32).try_into().unwrap(), }; let verifier = Verifier { address: Addr::unchecked("verifier"), @@ -599,7 +608,7 @@ mod tests { #[test] fn test_requested_unbonding_claim_stake() { let bonding_state = BondingState::RequestedUnbonding { - amount: Uint128::from(100u32), + amount: Uint128::from(100u32).try_into().unwrap(), }; let verifier = Verifier { address: Addr::unchecked("verifier"), @@ -626,7 +635,7 @@ mod tests { #[test] fn test_unbonding_claim_stake() { let bonding_state = BondingState::Unbonding { - amount: Uint128::from(100u32), + amount: Uint128::from(100u32).try_into().unwrap(), unbonded_at: Timestamp::from_nanos(0), }; let verifier = Verifier { @@ -649,7 +658,10 @@ mod tests { let (verifier, amount) = res.unwrap(); assert_eq!( (verifier.bonding_state, amount), - (BondingState::Unbonded, Uint128::from(100u32)) + ( + BondingState::Unbonded, + Uint128::from(100u32).try_into().unwrap() + ) ); } @@ -683,7 +695,7 @@ mod tests { let verifier = Verifier { address: Addr::unchecked("verifier"), bonding_state: BondingState::Bonded { - amount: Uint128::from(100u32), + amount: Uint128::from(100u32).try_into().unwrap(), }, authorization_state: AuthorizationState::Jailed, service_name: "validators".to_string(), @@ -699,7 +711,7 @@ mod tests { let verifier = Verifier { address: Addr::unchecked("verifier"), bonding_state: BondingState::Unbonding { - amount: Uint128::from(100u32), + amount: Uint128::from(100u32).try_into().unwrap(), unbonded_at: Timestamp::from_nanos(0), }, authorization_state: AuthorizationState::Jailed, diff --git a/contracts/voting-verifier/.cargo/config b/contracts/voting-verifier/.cargo/config.toml similarity index 100% rename from contracts/voting-verifier/.cargo/config rename to contracts/voting-verifier/.cargo/config.toml diff --git a/contracts/voting-verifier/Cargo.toml b/contracts/voting-verifier/Cargo.toml index 99f3e6835..494ec3f18 100644 --- a/contracts/voting-verifier/Cargo.toml +++ b/contracts/voting-verifier/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "voting-verifier" -version = "0.4.0" +version = "1.0.0" rust-version = { workspace = true } -edition = "2021" +edition = { workspace = true } description = "Voting verifier contract" exclude = [ @@ -29,18 +29,19 @@ library = [] optimize = """docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/rust-optimizer:0.12.6 + cosmwasm/optimizer:0.16.0 """ [dependencies] -axelar-wasm-std = { workspace = true } -axelar-wasm-std-derive = { workspace = true } +axelar-wasm-std = { workspace = true, features = ["derive"] } client = { workspace = true } cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } cw-storage-plus = { workspace = true } cw2 = { workspace = true } error-stack = { workspace = true } +itertools = { workspace = true } +msgs-derive = { workspace = true } multisig = { workspace = true, features = ["library"] } report = { workspace = true } rewards = { workspace = true, features = ["library"] } @@ -50,6 +51,7 @@ service-registry = { workspace = true, features = ["library"] } thiserror = { workspace = true } [dev-dependencies] +alloy-primitives = { version = "0.7.7", features = ["getrandom"] } cw-multi-test = "0.15.1" integration-tests = { workspace = true } multisig = { workspace = true, features = ["test", "library"] } diff --git a/contracts/voting-verifier/src/bin/schema.rs b/contracts/voting-verifier/src/bin/schema.rs index 6f6a8eb3f..d6bf17803 100644 --- a/contracts/voting-verifier/src/bin/schema.rs +++ b/contracts/voting-verifier/src/bin/schema.rs @@ -1,5 +1,4 @@ use cosmwasm_schema::write_api; - use voting_verifier::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; fn main() { diff --git a/contracts/voting-verifier/src/client.rs b/contracts/voting-verifier/src/client.rs index d0ac78f41..a8162ef4d 100644 --- a/contracts/voting-verifier/src/client.rs +++ b/contracts/voting-verifier/src/client.rs @@ -1,15 +1,12 @@ +use axelar_wasm_std::vec::VecExt; +use axelar_wasm_std::voting::{PollId, Vote}; +use axelar_wasm_std::{nonempty, MajorityThreshold, VerificationStatus}; use cosmwasm_std::{Addr, WasmMsg}; use error_stack::ResultExt; - -use axelar_wasm_std::{ - nonempty, - voting::{PollId, Vote}, - MajorityThreshold, VerificationStatus, -}; use multisig::verifier_set::VerifierSet; use router_api::Message; -use crate::msg::{ExecuteMsg, MessageStatus, Poll, QueryMsg}; +use crate::msg::{ExecuteMsg, MessageStatus, PollResponse, QueryMsg}; type Result = error_stack::Result; @@ -31,10 +28,9 @@ pub struct Client<'a> { impl<'a> Client<'a> { pub fn verify_messages(&self, messages: Vec) -> Option { - ignore_empty(messages).map(|messages| { - self.client - .execute(&ExecuteMsg::VerifyMessages { messages }) - }) + messages + .to_none_if_empty() + .map(|messages| self.client.execute(&ExecuteMsg::VerifyMessages(messages))) } pub fn vote(&self, poll_id: PollId, votes: Vec) -> WasmMsg { @@ -62,9 +58,9 @@ impl<'a> Client<'a> { }) } - pub fn poll(&self, poll_id: PollId) -> Result { + pub fn poll(&self, poll_id: PollId) -> Result { self.client - .query(&QueryMsg::GetPoll { poll_id }) + .query(&QueryMsg::Poll { poll_id }) .change_context_lazy(|| Error::QueryVotingVerifier(self.client.address.clone())) } @@ -73,53 +69,38 @@ impl<'a> Client<'a> { [] => Ok(vec![]), _ => self .client - .query(&QueryMsg::GetMessagesStatus { messages }) + .query(&QueryMsg::MessagesStatus(messages)) .change_context_lazy(|| Error::QueryVotingVerifier(self.client.address.clone())), } } pub fn verifier_set_status(&self, new_verifier_set: VerifierSet) -> Result { self.client - .query(&QueryMsg::GetVerifierSetStatus { new_verifier_set }) + .query(&QueryMsg::VerifierSetStatus(new_verifier_set)) .change_context_lazy(|| Error::QueryVotingVerifier(self.client.address.clone())) } pub fn current_threshold(&self) -> Result { self.client - .query(&QueryMsg::GetCurrentThreshold) + .query(&QueryMsg::CurrentThreshold) .change_context_lazy(|| Error::QueryVotingVerifier(self.client.address.clone())) } } -// TODO: unify across contract clients -fn ignore_empty(msgs: Vec) -> Option> { - if msgs.is_empty() { - None - } else { - Some(msgs) - } -} - #[cfg(test)] mod test { use std::collections::BTreeMap; - use axelar_wasm_std::{ - msg_id::tx_hash_event_index::HexTxHashAndEventIndex, Threshold, VerificationStatus, - }; - use cosmwasm_std::{ - from_json, - testing::{mock_dependencies, mock_env, mock_info, MockQuerier}, - Addr, DepsMut, QuerierWrapper, Uint128, Uint64, WasmQuery, - }; + use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; + use axelar_wasm_std::{Threshold, VerificationStatus}; + use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockQuerier}; + use cosmwasm_std::{from_json, Addr, DepsMut, QuerierWrapper, Uint128, Uint64, WasmQuery}; use multisig::verifier_set::VerifierSet; use router_api::{CrossChainId, Message}; - use crate::{ - contract::{instantiate, query}, - msg::{InstantiateMsg, MessageStatus, QueryMsg}, - Client, - }; + use crate::contract::{instantiate, query}; + use crate::msg::{InstantiateMsg, MessageStatus, QueryMsg}; + use crate::Client; #[test] fn query_messages_status() { @@ -127,32 +108,32 @@ mod test { let client: Client = client::Client::new(QuerierWrapper::new(&querier), addr).into(); let msg_1 = Message { - cc_id: CrossChainId { - chain: "eth".parse().unwrap(), - id: HexTxHashAndEventIndex { + cc_id: CrossChainId::new( + "eth", + HexTxHashAndEventIndex { tx_hash: [0; 32], event_index: 0, } .to_string() - .parse() - .unwrap(), - }, + .as_str(), + ) + .unwrap(), source_address: "0x1234".parse().unwrap(), destination_address: "0x5678".parse().unwrap(), destination_chain: "eth".parse().unwrap(), payload_hash: [0; 32], }; let msg_2 = Message { - cc_id: CrossChainId { - chain: "eth".parse().unwrap(), - id: HexTxHashAndEventIndex { + cc_id: CrossChainId::new( + "eth", + HexTxHashAndEventIndex { tx_hash: [1; 32], event_index: 0, } .to_string() - .parse() - .unwrap(), - }, + .as_str(), + ) + .unwrap(), source_address: "0x4321".parse().unwrap(), destination_address: "0x8765".parse().unwrap(), destination_chain: "eth".parse().unwrap(), @@ -229,11 +210,12 @@ mod test { .unwrap() .try_into() .unwrap(), - block_expiry: 100, + block_expiry: 100.try_into().unwrap(), confirmation_height: 10, source_chain: "source-chain".parse().unwrap(), - rewards_address: "rewards".to_string(), + rewards_address: "rewards".try_into().unwrap(), msg_id_format: axelar_wasm_std::msg_id::MessageIdFormat::HexTxHashAndEventIndex, + address_format: axelar_wasm_std::address::AddressFormat::Eip55, }; instantiate(deps, env, info.clone(), msg.clone()).unwrap(); diff --git a/contracts/voting-verifier/src/contract.rs b/contracts/voting-verifier/src/contract.rs index 17ab9a429..805b4f423 100644 --- a/contracts/voting-verifier/src/contract.rs +++ b/contracts/voting-verifier/src/contract.rs @@ -1,3 +1,4 @@ +use axelar_wasm_std::{address, permission_control}; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ @@ -5,9 +6,13 @@ use cosmwasm_std::{ StdResult, }; +use crate::contract::migrations::v0_5_0; use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; use crate::state::{Config, CONFIG}; -use crate::{execute, query}; + +mod execute; +mod migrations; +mod query; const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -18,20 +23,26 @@ pub fn instantiate( _env: Env, _info: MessageInfo, msg: InstantiateMsg, -) -> Result { +) -> Result { cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + let governance = address::validate_cosmwasm_address(deps.api, &msg.governance_address)?; + permission_control::set_governance(deps.storage, &governance)?; + let config = Config { - governance: deps.api.addr_validate(&msg.governance_address)?, service_name: msg.service_name, - service_registry_contract: deps.api.addr_validate(&msg.service_registry_address)?, + service_registry_contract: address::validate_cosmwasm_address( + deps.api, + &msg.service_registry_address, + )?, source_gateway_address: msg.source_gateway_address, voting_threshold: msg.voting_threshold, block_expiry: msg.block_expiry, confirmation_height: msg.confirmation_height, source_chain: msg.source_chain, - rewards_contract: deps.api.addr_validate(&msg.rewards_address)?, + rewards_contract: address::validate_cosmwasm_address(deps.api, &msg.rewards_address)?, msg_id_format: msg.msg_id_format, + address_format: msg.address_format, }; CONFIG.save(deps.storage, &config)?; @@ -45,39 +56,43 @@ pub fn execute( env: Env, info: MessageInfo, msg: ExecuteMsg, -) -> Result { - match msg { - ExecuteMsg::VerifyMessages { messages } => execute::verify_messages(deps, env, messages), - ExecuteMsg::Vote { poll_id, votes } => execute::vote(deps, env, info, poll_id, votes), - ExecuteMsg::EndPoll { poll_id } => execute::end_poll(deps, env, poll_id), +) -> Result { + match msg.ensure_permissions(deps.storage, &info.sender)? { + ExecuteMsg::VerifyMessages(messages) => Ok(execute::verify_messages(deps, env, messages)?), + ExecuteMsg::Vote { poll_id, votes } => Ok(execute::vote(deps, env, info, poll_id, votes)?), + ExecuteMsg::EndPoll { poll_id } => Ok(execute::end_poll(deps, env, poll_id)?), ExecuteMsg::VerifyVerifierSet { message_id, new_verifier_set, - } => execute::verify_verifier_set(deps, env, message_id, new_verifier_set), + } => Ok(execute::verify_verifier_set( + deps, + env, + &message_id, + new_verifier_set, + )?), ExecuteMsg::UpdateVotingThreshold { new_voting_threshold, - } => { - execute::require_governance(&deps, info.sender)?; - execute::update_voting_threshold(deps, new_voting_threshold) - } + } => Ok(execute::update_voting_threshold( + deps, + new_voting_threshold, + )?), } - .map_err(axelar_wasm_std::ContractError::from) } #[cfg_attr(not(feature = "library"), entry_point)] -pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { +pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::GetPoll { poll_id: _ } => { - todo!() + QueryMsg::Poll { poll_id } => { + to_json_binary(&query::poll_response(deps, env.block.height, poll_id)?) } - QueryMsg::GetMessagesStatus { messages } => { - to_json_binary(&query::messages_status(deps, &messages)?) + QueryMsg::MessagesStatus(messages) => { + to_json_binary(&query::messages_status(deps, &messages, env.block.height)?) } - QueryMsg::GetVerifierSetStatus { new_verifier_set } => { - to_json_binary(&query::verifier_set_status(deps, &new_verifier_set)?) - } - QueryMsg::GetCurrentThreshold => to_json_binary(&query::voting_threshold(deps)?), + QueryMsg::VerifierSetStatus(new_verifier_set) => to_json_binary( + &query::verifier_set_status(deps, &new_verifier_set, env.block.height)?, + ), + QueryMsg::CurrentThreshold => to_json_binary(&query::voting_threshold(deps)?), } } @@ -86,44 +101,39 @@ pub fn migrate( deps: DepsMut, _env: Env, _msg: Empty, -) -> Result { - // any version checks should be done before here - - cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; +) -> Result { + v0_5_0::migrate(deps.storage)?; Ok(Response::default()) } #[cfg(test)] mod test { - use cosmwasm_std::{ - from_json, - testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, - Addr, Empty, Fraction, OwnedDeps, Uint128, Uint64, WasmQuery, + use axelar_wasm_std::address::AddressFormat; + use axelar_wasm_std::msg_id::{ + Base58SolanaTxSignatureAndEventIndex, Base58TxDigestAndEventIndex, HexTxHash, + HexTxHashAndEventIndex, MessageIdFormat, }; - use sha3::{Digest, Keccak256}; - + use axelar_wasm_std::voting::Vote; use axelar_wasm_std::{ - msg_id::{ - base_58_event_index::Base58TxDigestAndEventIndex, - tx_hash_event_index::HexTxHashAndEventIndex, MessageIdFormat, - }, - nonempty, - voting::Vote, - MajorityThreshold, Threshold, VerificationStatus, + err_contains, nonempty, MajorityThreshold, Threshold, VerificationStatus, }; - use multisig::{ - key::KeyType, - test::common::{build_verifier_set, ecdsa_test_data}, + use cosmwasm_std::testing::{ + mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage, }; + use cosmwasm_std::{from_json, Addr, Empty, Fraction, OwnedDeps, Uint128, Uint64, WasmQuery}; + use multisig::key::KeyType; + use multisig::test::common::{build_verifier_set, ecdsa_test_data}; use router_api::{ChainName, CrossChainId, Message}; - use service_registry::state::{ + use service_registry::{ AuthorizationState, BondingState, Verifier, WeightedVerifier, VERIFIER_WEIGHT, }; - - use crate::{error::ContractError, events::TxEventConfirmation, msg::MessageStatus}; + use sha3::{Digest, Keccak256, Keccak512}; use super::*; + use crate::error::ContractError; + use crate::events::TxEventConfirmation; + use crate::msg::MessageStatus; const SENDER: &str = "sender"; const SERVICE_REGISTRY_ADDRESS: &str = "service_registry_address"; @@ -136,17 +146,13 @@ mod test { "source-chain".parse().unwrap() } - fn governance() -> Addr { - Addr::unchecked(GOVERNANCE) - } - fn initial_voting_threshold() -> MajorityThreshold { Threshold::try_from((2, 3)).unwrap().try_into().unwrap() } fn assert_contract_err_strings_equal( - actual: impl Into, - expected: impl Into, + actual: impl Into, + expected: impl Into, ) { assert_eq!(actual.into().to_string(), expected.into().to_string()); } @@ -157,7 +163,7 @@ mod test { verifiers.push(Verifier { address: Addr::unchecked(format!("addr{}", i)), bonding_state: BondingState::Bonded { - amount: Uint128::from(100u128), + amount: Uint128::from(100u128).try_into().unwrap(), }, authorization_state: AuthorizationState::Authorized, service_name: SERVICE_NAME.parse().unwrap(), @@ -172,19 +178,25 @@ mod test { ) -> OwnedDeps { let mut deps = mock_dependencies(); - let config = Config { - governance: governance(), - service_name: SERVICE_NAME.parse().unwrap(), - service_registry_contract: Addr::unchecked(SERVICE_REGISTRY_ADDRESS), - source_gateway_address: "source_gateway_address".parse().unwrap(), - voting_threshold: initial_voting_threshold(), - block_expiry: POLL_BLOCK_EXPIRY, - confirmation_height: 100, - source_chain: source_chain(), - rewards_contract: Addr::unchecked(REWARDS_ADDRESS), - msg_id_format: msg_id_format.clone(), - }; - CONFIG.save(deps.as_mut().storage, &config).unwrap(); + instantiate( + deps.as_mut(), + mock_env(), + mock_info("admin", &[]), + InstantiateMsg { + governance_address: GOVERNANCE.parse().unwrap(), + service_registry_address: SERVICE_REGISTRY_ADDRESS.parse().unwrap(), + service_name: SERVICE_NAME.parse().unwrap(), + source_gateway_address: "source_gateway_address".parse().unwrap(), + voting_threshold: initial_voting_threshold(), + block_expiry: POLL_BLOCK_EXPIRY.try_into().unwrap(), + confirmation_height: 100, + source_chain: source_chain(), + rewards_address: REWARDS_ADDRESS.parse().unwrap(), + msg_id_format: msg_id_format.clone(), + address_format: AddressFormat::Eip55, + }, + ) + .unwrap(); deps.querier.update_wasm(move |wq| match wq { WasmQuery::Smart { contract_addr, .. } if contract_addr == SERVICE_REGISTRY_ADDRESS => { @@ -207,19 +219,33 @@ mod test { deps } - fn message_id(id: &str, index: u64, msg_id_format: &MessageIdFormat) -> nonempty::String { - let tx_hash = Keccak256::digest(id.as_bytes()).into(); + fn message_id(id: &str, index: u32, msg_id_format: &MessageIdFormat) -> nonempty::String { match msg_id_format { MessageIdFormat::HexTxHashAndEventIndex => HexTxHashAndEventIndex { - tx_hash, - event_index: index as u32, + tx_hash: Keccak256::digest(id.as_bytes()).into(), + event_index: index, } .to_string() .parse() .unwrap(), MessageIdFormat::Base58TxDigestAndEventIndex => Base58TxDigestAndEventIndex { - tx_digest: tx_hash, - event_index: index as u32, + tx_digest: Keccak256::digest(id.as_bytes()).into(), + event_index: index, + } + .to_string() + .parse() + .unwrap(), + MessageIdFormat::Base58SolanaTxSignatureAndEventIndex => { + Base58SolanaTxSignatureAndEventIndex { + raw_signature: Keccak512::digest(id.as_bytes()).into(), + event_index: index, + } + .to_string() + .parse() + .unwrap() + } + MessageIdFormat::HexTxHash => HexTxHash { + tx_hash: Keccak256::digest(id.as_bytes()).into(), } .to_string() .parse() @@ -227,16 +253,17 @@ mod test { } } - fn messages(len: u64, msg_id_format: &MessageIdFormat) -> Vec { + fn messages(len: u32, msg_id_format: &MessageIdFormat) -> Vec { (0..len) .map(|i| Message { - cc_id: CrossChainId { - chain: source_chain(), - id: message_id("id", i, msg_id_format), - }, - source_address: format!("source_address{i}").parse().unwrap(), + cc_id: CrossChainId::new(source_chain(), message_id("id", i, msg_id_format)) + .unwrap(), + source_address: alloy_primitives::Address::random() + .to_string() + .try_into() + .unwrap(), destination_chain: format!("destination-chain{i}").parse().unwrap(), - destination_address: format!("destination_address{i}").parse().unwrap(), + destination_address: format!("destination-address{i}").parse().unwrap(), payload_hash: [0; 32], }) .collect() @@ -256,49 +283,33 @@ mod test { .collect() } - #[test] - fn migrate_sets_contract_version() { - let msg_id_format = MessageIdFormat::HexTxHashAndEventIndex; - let verifiers = verifiers(2); - let mut deps = setup(verifiers.clone(), &msg_id_format); - - migrate(deps.as_mut(), mock_env(), Empty {}).unwrap(); - - let contract_version = cw2::get_contract_version(deps.as_mut().storage).unwrap(); - assert_eq!(contract_version.contract, "voting-verifier"); - assert_eq!(contract_version.version, CONTRACT_VERSION); - } - #[test] fn should_fail_if_messages_are_not_from_same_source() { let msg_id_format = MessageIdFormat::HexTxHashAndEventIndex; let verifiers = verifiers(2); let mut deps = setup(verifiers.clone(), &msg_id_format); - let msg = ExecuteMsg::VerifyMessages { - messages: vec![ - Message { - cc_id: CrossChainId { - chain: source_chain(), - id: message_id("id", 1, &msg_id_format), - }, - source_address: "source_address1".parse().unwrap(), - destination_chain: "destination-chain1".parse().unwrap(), - destination_address: "destination_address1".parse().unwrap(), - payload_hash: [0; 32], - }, - Message { - cc_id: CrossChainId { - chain: "other-chain".parse().unwrap(), - id: message_id("id", 2, &msg_id_format), - }, - source_address: "source_address2".parse().unwrap(), - destination_chain: "destination-chain2".parse().unwrap(), - destination_address: "destination_address2".parse().unwrap(), - payload_hash: [0; 32], - }, - ], - }; + let msg = ExecuteMsg::VerifyMessages(vec![ + Message { + cc_id: CrossChainId::new(source_chain(), message_id("id", 1, &msg_id_format)) + .unwrap(), + source_address: alloy_primitives::Address::random() + .to_string() + .parse() + .unwrap(), + destination_chain: "destination-chain1".parse().unwrap(), + destination_address: "destination-address1".parse().unwrap(), + payload_hash: [0; 32], + }, + Message { + cc_id: CrossChainId::new("other-chain", message_id("id", 2, &msg_id_format)) + .unwrap(), + source_address: "source-address2".parse().unwrap(), + destination_chain: "destination-chain2".parse().unwrap(), + destination_address: "destination-address2".parse().unwrap(), + payload_hash: [0; 32], + }, + ]); let err = execute(deps.as_mut(), mock_env(), mock_info(SENDER, &[]), msg).unwrap_err(); assert_contract_err_strings_equal(err, ContractError::SourceChainMismatch(source_chain())); } @@ -310,13 +321,15 @@ mod test { let mut deps = setup(verifiers.clone(), &msg_id_format); let mut messages = messages(1, &MessageIdFormat::HexTxHashAndEventIndex); - let msg_id = "foobar"; - messages[0].cc_id.id = msg_id.parse().unwrap(); + messages[0].cc_id = CrossChainId::new(source_chain(), "foobar").unwrap(); - let msg = ExecuteMsg::VerifyMessages { messages }; + let msg = ExecuteMsg::VerifyMessages(messages); let err = execute(deps.as_mut(), mock_env(), mock_info(SENDER, &[]), msg).unwrap_err(); - assert_contract_err_strings_equal(err, ContractError::InvalidMessageID(msg_id.to_string())); + assert_contract_err_strings_equal( + err, + ContractError::InvalidMessageID("foobar".to_string()), + ); } #[test] @@ -326,14 +339,12 @@ mod test { let mut deps = setup(verifiers.clone(), &msg_id_format); let messages = messages(1, &MessageIdFormat::Base58TxDigestAndEventIndex); - let msg = ExecuteMsg::VerifyMessages { - messages: messages.clone(), - }; + let msg = ExecuteMsg::VerifyMessages(messages.clone()); let err = execute(deps.as_mut(), mock_env(), mock_info(SENDER, &[]), msg).unwrap_err(); assert_contract_err_strings_equal( err, - ContractError::InvalidMessageID(messages[0].cc_id.id.to_string()), + ContractError::InvalidMessageID(messages[0].cc_id.message_id.to_string()), ); } @@ -344,14 +355,12 @@ mod test { let mut deps = setup(verifiers.clone(), &msg_id_format); let messages = messages(1, &MessageIdFormat::HexTxHashAndEventIndex); - let msg = ExecuteMsg::VerifyMessages { - messages: messages.clone(), - }; + let msg = ExecuteMsg::VerifyMessages(messages.clone()); let err = execute(deps.as_mut(), mock_env(), mock_info(SENDER, &[]), msg).unwrap_err(); assert_contract_err_strings_equal( err, - ContractError::InvalidMessageID(messages[0].cc_id.id.to_string()), + ContractError::InvalidMessageID(messages[0].cc_id.message_id.to_string()), ); } @@ -362,15 +371,15 @@ mod test { let mut deps = setup(verifiers.clone(), &msg_id_format); let messages_count = 5; let messages_in_progress = 3; - let messages = messages(messages_count as u64, &msg_id_format); + let messages = messages(messages_count as u32, &msg_id_format); execute( deps.as_mut(), mock_env(), mock_info(SENDER, &[]), - ExecuteMsg::VerifyMessages { - messages: messages[0..messages_in_progress].to_vec(), // verify a subset of the messages - }, + ExecuteMsg::VerifyMessages( + messages[0..messages_in_progress].to_vec(), // verify a subset of the messages + ), ) .unwrap(); @@ -378,9 +387,9 @@ mod test { deps.as_mut(), mock_env(), mock_info(SENDER, &[]), - ExecuteMsg::VerifyMessages { - messages: messages.clone(), // verify all messages including the ones from previous execution - }, + ExecuteMsg::VerifyMessages( + messages.clone(), // verify all messages including the ones from previous execution + ), ) .unwrap(); @@ -407,10 +416,7 @@ mod test { .iter() .cloned() .map(|e| { - ( - e, - &axelar_wasm_std::msg_id::MessageIdFormat::HexTxHashAndEventIndex, - ) + (e, &MessageIdFormat::HexTxHashAndEventIndex) .try_into() .unwrap() }) @@ -426,9 +432,7 @@ mod test { let mut deps = setup(verifiers.clone(), &msg_id_format); let messages = messages(5, &msg_id_format); - let msg = ExecuteMsg::VerifyMessages { - messages: messages.clone(), - }; + let msg = ExecuteMsg::VerifyMessages(messages.clone()); execute( deps.as_mut(), mock_env(), @@ -437,24 +441,12 @@ mod test { ) .unwrap(); - execute( - deps.as_mut(), - mock_env_expired(), - mock_info(SENDER, &[]), - ExecuteMsg::EndPoll { - poll_id: Uint64::one().into(), - }, - ) - .unwrap(); - // confirm it was not verified let status: Vec = from_json( query( deps.as_ref(), - mock_env(), - QueryMsg::GetMessagesStatus { - messages: messages.clone(), - }, + mock_env_expired(), + QueryMsg::MessagesStatus(messages.clone()), ) .unwrap(), ) @@ -465,7 +457,13 @@ mod test { ); // retries same message - let res = execute(deps.as_mut(), mock_env(), mock_info(SENDER, &[]), msg).unwrap(); + let res = execute( + deps.as_mut(), + mock_env_expired(), + mock_info(SENDER, &[]), + msg, + ) + .unwrap(); let actual: Vec = serde_json::from_str( &res.events @@ -488,10 +486,7 @@ mod test { let expected = messages .into_iter() .map(|e| { - ( - e, - &axelar_wasm_std::msg_id::MessageIdFormat::HexTxHashAndEventIndex, - ) + (e, &MessageIdFormat::HexTxHashAndEventIndex) .try_into() .unwrap() }) @@ -510,9 +505,7 @@ mod test { // 1. First verification - let msg_verify = ExecuteMsg::VerifyMessages { - messages: messages.clone(), - }; + let msg_verify = ExecuteMsg::VerifyMessages(messages.clone()); let res = execute( deps.as_mut(), @@ -566,10 +559,8 @@ mod test { let res: Vec = from_json( query( deps.as_ref(), - mock_env(), - QueryMsg::GetMessagesStatus { - messages: messages.clone(), - }, + mock_env_expired(), + QueryMsg::MessagesStatus(messages.clone()), ) .unwrap(), ) @@ -595,7 +586,7 @@ mod test { let res = execute( deps.as_mut(), - mock_env(), + mock_env_expired(), mock_info(SENDER, &[]), msg_verify, ); @@ -604,10 +595,8 @@ mod test { let res: Vec = from_json( query( deps.as_ref(), - mock_env(), - QueryMsg::GetMessagesStatus { - messages: messages.clone(), - }, + mock_env_expired(), + QueryMsg::MessagesStatus(messages.clone()), ) .unwrap(), ) @@ -638,9 +627,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetMessagesStatus { - messages: messages.clone(), - }, + QueryMsg::MessagesStatus(messages.clone()), ) .unwrap(), ) @@ -664,9 +651,7 @@ mod test { deps.as_mut(), mock_env(), mock_info(SENDER, &[]), - ExecuteMsg::VerifyMessages { - messages: messages.clone(), - }, + ExecuteMsg::VerifyMessages(messages.clone()), ) .unwrap(); @@ -674,9 +659,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetMessagesStatus { - messages: messages.clone(), - }, + QueryMsg::MessagesStatus(messages.clone()), ) .unwrap(), ) @@ -688,7 +671,7 @@ mod test { } #[test] - fn should_query_status_failed_to_verify_when_no_consensus_and_poll_ended() { + fn should_query_status_failed_to_verify_when_no_consensus_and_poll_expired() { let msg_id_format = MessageIdFormat::HexTxHashAndEventIndex; let verifiers = verifiers(2); let mut deps = setup(verifiers.clone(), &msg_id_format); @@ -700,30 +683,15 @@ mod test { deps.as_mut(), mock_env(), mock_info(SENDER, &[]), - ExecuteMsg::VerifyMessages { - messages: messages.clone(), - }, - ) - .unwrap(); - - // end poll - execute( - deps.as_mut(), - mock_env_expired(), - mock_info(SENDER, &[]), - ExecuteMsg::EndPoll { - poll_id: Uint64::one().into(), - }, + ExecuteMsg::VerifyMessages(messages.clone()), ) .unwrap(); let statuses: Vec = from_json( query( deps.as_ref(), - mock_env(), - QueryMsg::GetMessagesStatus { - messages: messages.clone(), - }, + mock_env_expired(), + QueryMsg::MessagesStatus(messages.clone()), ) .unwrap(), ) @@ -764,9 +732,7 @@ mod test { deps.as_mut(), mock_env(), mock_info(SENDER, &[]), - ExecuteMsg::VerifyMessages { - messages: messages.clone(), - }, + ExecuteMsg::VerifyMessages(messages.clone()), ) .unwrap(); @@ -798,12 +764,10 @@ mod test { // check status corresponds to votes let statuses: Vec = from_json( - &query( + query( deps.as_ref(), mock_env(), - QueryMsg::GetMessagesStatus { - messages: messages.clone(), - }, + QueryMsg::MessagesStatus(messages.clone()), ) .unwrap(), ) @@ -830,9 +794,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetVerifierSetStatus { - new_verifier_set: verifier_set.clone(), - }, + QueryMsg::VerifierSetStatus(verifier_set.clone()), ) .unwrap(), ) @@ -882,9 +844,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetVerifierSetStatus { - new_verifier_set: verifier_set.clone(), - }, + QueryMsg::VerifierSetStatus(verifier_set.clone()), ) .unwrap(), ) @@ -937,9 +897,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetVerifierSetStatus { - new_verifier_set: verifier_set.clone(), - }, + QueryMsg::VerifierSetStatus(verifier_set.clone()), ) .unwrap(), ) @@ -992,9 +950,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetVerifierSetStatus { - new_verifier_set: verifier_set.clone(), - }, + QueryMsg::VerifierSetStatus(verifier_set.clone()), ) .unwrap(), ) @@ -1039,9 +995,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetVerifierSetStatus { - new_verifier_set: verifier_set.clone(), - }, + QueryMsg::VerifierSetStatus(verifier_set.clone()), ) .unwrap(), ) @@ -1127,7 +1081,7 @@ mod test { ) .unwrap(); - let res = query(deps.as_ref(), mock_env(), QueryMsg::GetCurrentThreshold).unwrap(); + let res = query(deps.as_ref(), mock_env(), QueryMsg::CurrentThreshold).unwrap(); let threshold: MajorityThreshold = from_json(res).unwrap(); assert_eq!(threshold, new_voting_threshold); @@ -1149,15 +1103,13 @@ mod test { deps.as_mut(), mock_env(), mock_info(SENDER, &[]), - ExecuteMsg::VerifyMessages { - messages: messages.clone(), - }, + ExecuteMsg::VerifyMessages(messages.clone()), ) .unwrap(); // simulate a majority of verifiers voting for succeeded on chain verifiers.iter().enumerate().for_each(|(i, verifier)| { - if i >= majority as usize { + if i as u64 >= majority { return; } let msg = ExecuteMsg::Vote { @@ -1206,9 +1158,7 @@ mod test { query( deps.as_ref(), mock_env(), - QueryMsg::GetMessagesStatus { - messages: messages.clone(), - }, + QueryMsg::MessagesStatus(messages.clone()), ) .unwrap(), ) @@ -1256,9 +1206,7 @@ mod test { deps.as_mut(), mock_env(), mock_info(SENDER, &[]), - ExecuteMsg::VerifyMessages { - messages: messages.clone(), - }, + ExecuteMsg::VerifyMessages(messages.clone()), ) .unwrap(); @@ -1266,7 +1214,7 @@ mod test { // which is one less than the updated majority. The messages // should not receive enough votes to be considered verified verifiers.iter().enumerate().for_each(|(i, verifier)| { - if i >= old_majority as usize { + if i as u64 >= old_majority { return; } let msg = ExecuteMsg::Vote { @@ -1296,10 +1244,8 @@ mod test { let res: Vec = from_json( query( deps.as_ref(), - mock_env(), - QueryMsg::GetMessagesStatus { - messages: messages.clone(), - }, + mock_env_expired(), + QueryMsg::MessagesStatus(messages.clone()), ) .unwrap(), ) @@ -1312,4 +1258,162 @@ mod test { )] ); } + + #[test] + fn should_emit_event_when_verification_succeeds() { + let msg_id_format = MessageIdFormat::HexTxHashAndEventIndex; + let verifiers = verifiers(3); + let mut deps = setup(verifiers.clone(), &msg_id_format); + let threshold = initial_voting_threshold(); + // this test depends on the threshold being 2/3 + assert_eq!( + threshold, + Threshold::try_from((2, 3)).unwrap().try_into().unwrap() + ); + + let messages = messages(3, &msg_id_format); + + // 1. First verification + + let msg_verify = ExecuteMsg::VerifyMessages(messages.clone()); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(SENDER, &[]), + msg_verify.clone(), + ); + assert!(res.is_ok()); + + // 2. Verifiers cast votes + // The first message reaches quorum after 2 votes, + // The second message reaches quorum after 3 votes, + // The third message never reaches quorum + verifiers.iter().enumerate().for_each(|(i, verifier)| { + let msg = ExecuteMsg::Vote { + poll_id: 1u64.into(), + votes: vec![ + Vote::SucceededOnChain, + if i % 2 == 0 { + Vote::NotFound + } else { + Vote::SucceededOnChain + }, + if i % 3 == 0 { + Vote::NotFound + } else if i % 3 == 1 { + Vote::SucceededOnChain + } else { + Vote::FailedOnChain + }, + ], + }; + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(verifier.address.as_str(), &[]), + msg, + ) + .unwrap(); + + let verify_event = + |res: &Response, expected_message: Message, expected_status: VerificationStatus| { + let mut iter = res.events.iter(); + + let event = iter.find(|event| event.ty == "quorum_reached").unwrap(); + + let msg: Message = serde_json::from_str( + &event + .attributes + .iter() + .find(|attr| attr.key == "content") + .unwrap() + .value, + ) + .unwrap(); + assert_eq!(msg, expected_message); + + let status: VerificationStatus = serde_json::from_str( + &event + .attributes + .iter() + .find(|attr| attr.key == "status") + .unwrap() + .value, + ) + .unwrap(); + assert_eq!(status, expected_status); + + let additional_event = iter.find(|event| event.ty == "quorum_reached"); + assert_eq!(additional_event, None); + }; + + if i == 0 { + let event = res.events.iter().find(|event| event.ty == "quorum_reached"); + assert_eq!(event, None); + } + + if i == 1 { + verify_event( + &res, + messages[0].clone(), + VerificationStatus::SucceededOnSourceChain, + ); + } + + if i == 2 { + verify_event( + &res, + messages[1].clone(), + VerificationStatus::NotFoundOnSourceChain, + ); + } + }); + } + + #[test] + fn should_fail_if_messages_have_invalid_source_address() { + let msg_id_format = MessageIdFormat::HexTxHashAndEventIndex; + let verifiers = verifiers(2); + let mut deps = setup(verifiers.clone(), &msg_id_format); + + let eip55_address = alloy_primitives::Address::random().to_string(); + + let cases = vec![ + eip55_address.to_lowercase(), + eip55_address.to_uppercase(), + // mix + eip55_address + .chars() + .enumerate() + .map(|(i, c)| { + if i % 2 == 0 { + c.to_uppercase().next().unwrap() + } else { + c.to_lowercase().next().unwrap() + } + }) + .collect::(), + ]; + + for case in cases { + let mut messages = messages(1, &MessageIdFormat::HexTxHashAndEventIndex); + messages[0].source_address = case.parse().unwrap(); + let msg = ExecuteMsg::VerifyMessages(messages); + let res = execute(deps.as_mut(), mock_env(), mock_info(SENDER, &[]), msg); + assert!(res.is_err_and(|err| err_contains!( + err.report, + ContractError, + ContractError::InvalidSourceAddress { .. } + ))); + } + + // should not fail if address is valid + let mut messages = messages(1, &MessageIdFormat::HexTxHashAndEventIndex); + messages[0].source_address = eip55_address.parse().unwrap(); + let msg = ExecuteMsg::VerifyMessages(messages); + let res = execute(deps.as_mut(), mock_env(), mock_info(SENDER, &[]), msg); + assert!(res.is_ok()); + } } diff --git a/contracts/voting-verifier/src/execute.rs b/contracts/voting-verifier/src/contract/execute.rs similarity index 54% rename from contracts/voting-verifier/src/execute.rs rename to contracts/voting-verifier/src/contract/execute.rs index 6e4924af0..5e68eb373 100644 --- a/contracts/voting-verifier/src/execute.rs +++ b/contracts/voting-verifier/src/contract/execute.rs @@ -1,35 +1,29 @@ +use std::collections::HashMap; + +use axelar_wasm_std::address::{validate_address, AddressFormat}; +use axelar_wasm_std::utils::TryMapExt; +use axelar_wasm_std::voting::{PollId, PollResults, Vote, WeightedPoll}; +use axelar_wasm_std::{snapshot, MajorityThreshold, VerificationStatus}; use cosmwasm_std::{ - to_json_binary, Addr, Deps, DepsMut, Env, MessageInfo, OverflowError, OverflowOperation, + to_json_binary, Deps, DepsMut, Env, Event, MessageInfo, OverflowError, OverflowOperation, QueryRequest, Response, Storage, WasmMsg, WasmQuery, }; - -use axelar_wasm_std::{ - nonempty, snapshot, - voting::{PollId, Vote, WeightedPoll}, - MajorityThreshold, VerificationStatus, -}; - +use error_stack::{report, Report, ResultExt}; +use itertools::Itertools; use multisig::verifier_set::VerifierSet; use router_api::{ChainName, Message}; -use service_registry::{msg::QueryMsg, state::WeightedVerifier}; +use service_registry::msg::QueryMsg; +use service_registry::WeightedVerifier; +use crate::contract::query::{message_status, verifier_set_status}; +use crate::error::ContractError; use crate::events::{ - PollEnded, PollMetadata, PollStarted, TxEventConfirmation, VerifierSetConfirmation, Voted, + PollEnded, PollMetadata, PollStarted, QuorumReached, TxEventConfirmation, + VerifierSetConfirmation, Voted, +}; +use crate::state::{ + self, poll_messages, poll_verifier_sets, Poll, PollContent, CONFIG, POLLS, POLL_ID, VOTES, }; -use crate::query::verifier_set_status; -use crate::state::{self, Poll, PollContent, POLL_MESSAGES, POLL_VERIFIER_SETS}; -use crate::state::{CONFIG, POLLS, POLL_ID}; -use crate::{error::ContractError, query::message_status}; - -// TODO: this type of function exists in many contracts. Would be better to implement this -// in one place, and then just include it -pub fn require_governance(deps: &DepsMut, sender: Addr) -> Result<(), ContractError> { - let config = CONFIG.load(deps.storage)?; - if config.governance != sender { - return Err(ContractError::Unauthorized); - } - Ok(()) -} pub fn update_voting_threshold( deps: DepsMut, @@ -45,24 +39,24 @@ pub fn update_voting_threshold( pub fn verify_verifier_set( deps: DepsMut, env: Env, - message_id: nonempty::String, + message_id: &str, new_verifier_set: VerifierSet, ) -> Result { - let status = verifier_set_status(deps.as_ref(), &new_verifier_set)?; + let status = verifier_set_status(deps.as_ref(), &new_verifier_set, env.block.height)?; if status.is_confirmed() { return Ok(Response::new()); } let config = CONFIG.load(deps.storage)?; let snapshot = take_snapshot(deps.as_ref(), &config.source_chain)?; - let participants = snapshot.get_participants(); - let expires_at = calculate_expiration(env.block.height, config.block_expiry)?; + let participants = snapshot.participants(); + let expires_at = calculate_expiration(env.block.height, config.block_expiry.into())?; let poll_id = create_verifier_set_poll(deps.storage, expires_at, snapshot)?; - POLL_VERIFIER_SETS.save( + poll_verifier_sets().save( deps.storage, - &new_verifier_set.hash().as_slice().try_into().unwrap(), + &new_verifier_set.hash(), &PollContent::::new(new_verifier_set.clone(), poll_id), )?; @@ -90,26 +84,22 @@ pub fn verify_messages( deps: DepsMut, env: Env, messages: Vec, -) -> Result { +) -> Result> { if messages.is_empty() { Err(ContractError::EmptyMessages)?; } - let source_chain = CONFIG.load(deps.storage)?.source_chain; - - if messages - .iter() - .any(|message| message.cc_id.chain.ne(&source_chain)) - { - Err(ContractError::SourceChainMismatch(source_chain))?; - } + let config = CONFIG.load(deps.storage).map_err(ContractError::from)?; - let config = CONFIG.load(deps.storage)?; - - let messages = messages - .into_iter() - .map(|message| message_status(deps.as_ref(), &message).map(|status| (status, message))) - .collect::, _>>()?; + let messages = messages.try_map(|message| { + validate_source_chain(message, &config.source_chain) + .and_then(|message| validate_source_address(message, &config.address_format)) + .and_then(|message| { + message_status(deps.as_ref(), &message, env.block.height) + .map(|status| (status, message)) + .map_err(Report::from) + }) + })?; let msgs_to_verify: Vec = messages .into_iter() @@ -127,18 +117,20 @@ pub fn verify_messages( return Ok(Response::new()); } - let snapshot = take_snapshot(deps.as_ref(), &msgs_to_verify[0].cc_id.chain)?; - let participants = snapshot.get_participants(); - let expires_at = calculate_expiration(env.block.height, config.block_expiry)?; + let snapshot = take_snapshot(deps.as_ref(), &config.source_chain)?; + let participants = snapshot.participants(); + let expires_at = calculate_expiration(env.block.height, config.block_expiry.into())?; let id = create_messages_poll(deps.storage, expires_at, snapshot, msgs_to_verify.len())?; for (idx, message) in msgs_to_verify.iter().enumerate() { - POLL_MESSAGES.save( - deps.storage, - &message.hash(), - &state::PollContent::::new(message.clone(), id, idx), - )?; + poll_messages() + .save( + deps.storage, + &message.hash(), + &state::PollContent::::new(message.clone(), id, idx), + ) + .map_err(ContractError::from)?; } let messages = msgs_to_verify @@ -162,6 +154,60 @@ pub fn verify_messages( )) } +fn poll_results(poll: &Poll) -> PollResults { + match poll { + Poll::Messages(weighted_poll) => weighted_poll.results(), + Poll::ConfirmVerifierSet(weighted_poll) => weighted_poll.results(), + } +} + +fn make_quorum_event( + vote: Option, + index_in_poll: u32, + poll_id: &PollId, + poll: &Poll, + deps: &DepsMut, +) -> Result, ContractError> { + let status = vote.map(|vote| match vote { + Vote::SucceededOnChain => VerificationStatus::SucceededOnSourceChain, + Vote::FailedOnChain => VerificationStatus::FailedOnSourceChain, + Vote::NotFound => VerificationStatus::NotFoundOnSourceChain, + }); + + match poll { + Poll::Messages(_) => { + let msg = poll_messages() + .idx + .load_message(deps.storage, *poll_id, index_in_poll)? + .expect("message not found in poll"); + + Ok(status.map(|status| { + QuorumReached { + content: msg, + status, + poll_id: *poll_id, + } + .into() + })) + } + Poll::ConfirmVerifierSet(_) => { + let verifier_set = poll_verifier_sets() + .idx + .load_verifier_set(deps.storage, *poll_id)? + .expect("verifier set not found in poll"); + + Ok(status.map(|status| { + QuorumReached { + content: verifier_set, + status, + poll_id: *poll_id, + } + .into() + })) + } + } +} + pub fn vote( deps: DepsMut, env: Env, @@ -171,21 +217,42 @@ pub fn vote( ) -> Result { let poll = POLLS .may_load(deps.storage, poll_id)? - .ok_or(ContractError::PollNotFound)? - .try_map(|poll| { - poll.cast_vote(env.block.height, &info.sender, votes) - .map_err(ContractError::from) - })?; + .ok_or(ContractError::PollNotFound)?; + + let results_before_voting = poll_results(&poll); + let poll = poll.try_map(|poll| { + poll.cast_vote(env.block.height, &info.sender, votes.clone()) + .map_err(ContractError::from) + })?; POLLS.save(deps.storage, poll_id, &poll)?; - Ok(Response::new().add_event( - Voted { - poll_id, - voter: info.sender, - } - .into(), - )) + let results_after_voting = poll_results(&poll); + + let quorum_events = results_after_voting + .difference(results_before_voting) + .expect("failed to substract poll results") + .0 + .into_iter() + .enumerate() + .map(|(index_in_poll, vote)| { + let idx = u32::try_from(index_in_poll) + .expect("the amount of votes should never overflow u32"); + make_quorum_event(vote, idx, &poll_id, &poll, &deps) + }) + .collect::>, _>>()?; + + VOTES.save(deps.storage, (poll_id, info.sender.to_string()), &votes)?; + + Ok(Response::new() + .add_event( + Voted { + poll_id, + voter: info.sender, + } + .into(), + ) + .add_events(quorum_events.into_iter().flatten())) } pub fn end_poll(deps: DepsMut, env: Env, poll_id: PollId) -> Result { @@ -198,8 +265,15 @@ pub fn end_poll(deps: DepsMut, env: Env, poll_id: PollId) -> Result)> = VOTES + .prefix(poll_id) + .range(deps.storage, None, None, cosmwasm_std::Order::Ascending) + .try_collect()?; + let poll_result = match &poll { - Poll::Messages(poll) | Poll::ConfirmVerifierSet(poll) => poll.state(), + Poll::Messages(poll) | Poll::ConfirmVerifierSet(poll) => { + poll.state(HashMap::from_iter(votes)) + } }; // TODO: change rewards contract interface to accept a list of addresses to avoid creating multiple wasm messages @@ -223,7 +297,8 @@ pub fn end_poll(deps: DepsMut, env: Env, poll_id: PollId) -> Result Result Result Config { - Config { - governance, - service_registry_contract: Addr::unchecked("doesn't matter"), - service_name: "validators".to_string().try_into().unwrap(), - source_gateway_address: "0x89e51fA8CA5D66cd220bAed62ED01e8951aa7c40" - .to_string() - .try_into() - .unwrap(), - voting_threshold, - source_chain: "ethereum".to_string().try_into().unwrap(), - block_expiry: 10, - confirmation_height: 2, - rewards_contract: Addr::unchecked("rewards"), - msg_id_format: axelar_wasm_std::msg_id::MessageIdFormat::HexTxHashAndEventIndex, - } +fn validate_source_chain( + message: Message, + source_chain: &ChainName, +) -> Result> { + if message.cc_id.source_chain != *source_chain { + Err(report!(ContractError::SourceChainMismatch( + source_chain.clone() + ))) + } else { + Ok(message) } +} - #[test] - fn require_governance_should_reject_non_governance() { - let mut deps = mock_dependencies(); - let governance = Addr::unchecked("governance"); - CONFIG - .save( - deps.as_mut().storage, - &mock_config( - governance.clone(), - Threshold::try_from((2, 3)).unwrap().try_into().unwrap(), - ), - ) - .unwrap(); - - let res = require_governance(&deps.as_mut(), Addr::unchecked("random")); - assert!(res.is_err()); +fn validate_source_address( + message: Message, + address_format: &AddressFormat, +) -> Result> { + validate_address(&message.source_address, address_format) + .change_context(ContractError::InvalidSourceAddress)?; - let res = require_governance(&deps.as_mut(), governance); - assert!(res.is_ok()); - } + Ok(message) } diff --git a/contracts/voting-verifier/src/contract/migrations/mod.rs b/contracts/voting-verifier/src/contract/migrations/mod.rs new file mode 100644 index 000000000..504cbdb5c --- /dev/null +++ b/contracts/voting-verifier/src/contract/migrations/mod.rs @@ -0,0 +1 @@ +pub mod v0_5_0; diff --git a/contracts/voting-verifier/src/contract/migrations/v0_5_0.rs b/contracts/voting-verifier/src/contract/migrations/v0_5_0.rs new file mode 100644 index 000000000..90d1313ea --- /dev/null +++ b/contracts/voting-verifier/src/contract/migrations/v0_5_0.rs @@ -0,0 +1,266 @@ +#![allow(deprecated)] + +use axelar_wasm_std::address::AddressFormat; +use axelar_wasm_std::error::ContractError; +use axelar_wasm_std::msg_id::MessageIdFormat; +use axelar_wasm_std::{nonempty, permission_control, MajorityThreshold}; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, Attribute, StdResult, Storage}; +use cw_storage_plus::Item; +use router_api::ChainName; + +use crate::contract::{CONTRACT_NAME, CONTRACT_VERSION}; +use crate::state; + +const BASE_VERSION: &str = "0.5.0"; + +pub fn migrate(storage: &mut dyn Storage) -> Result<(), ContractError> { + cw2::assert_contract_version(storage, CONTRACT_NAME, BASE_VERSION)?; + + let config = CONFIG.load(storage)?; + migrate_permission_control(storage, &config.governance)?; + migrate_config(storage, config)?; + + delete_polls(storage); + + cw2::set_contract_version(storage, CONTRACT_NAME, CONTRACT_VERSION)?; + + Ok(()) +} + +fn migrate_config(storage: &mut dyn Storage, config: Config) -> StdResult<()> { + CONFIG.remove(storage); + + let config = state::Config { + service_registry_contract: config.service_registry_contract, + service_name: config.service_name, + source_chain: config.source_chain, + rewards_contract: config.rewards_contract, + block_expiry: config + .block_expiry + .try_into() + .unwrap_or(1.try_into().expect("1 is not zero")), + confirmation_height: config.confirmation_height, + msg_id_format: config.msg_id_format, + source_gateway_address: config.source_gateway_address, + voting_threshold: config.voting_threshold, + address_format: AddressFormat::Eip55, + }; + + state::CONFIG.save(storage, &config) +} + +fn migrate_permission_control(storage: &mut dyn Storage, governance: &Addr) -> StdResult<()> { + permission_control::set_governance(storage, governance) +} + +fn delete_polls(storage: &mut dyn Storage) { + state::POLLS.clear(storage); + state::VOTES.clear(storage); + state::poll_messages().clear(storage); + state::poll_messages().clear(storage); +} + +#[cw_serde] +#[deprecated(since = "0.5.0", note = "only used during migration")] +pub struct Config { + pub governance: Addr, + pub service_registry_contract: Addr, + pub service_name: nonempty::String, + pub source_gateway_address: nonempty::String, + pub voting_threshold: MajorityThreshold, + pub block_expiry: u64, // number of blocks after which a poll expires + pub confirmation_height: u64, + pub source_chain: ChainName, + pub rewards_contract: Addr, + pub msg_id_format: MessageIdFormat, +} +impl From for Vec { + fn from(other: Config) -> Self { + vec![ + ("service_name", other.service_name.to_string()), + ( + "service_registry_contract", + other.service_registry_contract.to_string(), + ), + ( + "source_gateway_address", + other.source_gateway_address.to_string(), + ), + ( + "voting_threshold", + serde_json::to_string(&other.voting_threshold) + .expect("failed to serialize voting_threshold"), + ), + ("block_expiry", other.block_expiry.to_string()), + ("confirmation_height", other.confirmation_height.to_string()), + ] + .into_iter() + .map(Attribute::from) + .collect() + } +} +#[deprecated(since = "0.5.0", note = "only used during migration")] +pub const CONFIG: Item = Item::new("config"); + +#[cfg(test)] +mod tests { + use axelar_wasm_std::msg_id::MessageIdFormat; + use axelar_wasm_std::permission_control::Permission; + use axelar_wasm_std::{nonempty, permission_control, MajorityThreshold, Threshold}; + use cosmwasm_schema::cw_serde; + use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; + use cosmwasm_std::{Addr, Attribute, DepsMut, Empty, Env, Event, MessageInfo, Response}; + use router_api::ChainName; + + use crate::contract::migrations::v0_5_0; + use crate::contract::{migrate, CONTRACT_NAME, CONTRACT_VERSION}; + use crate::state; + + const GOVERNANCE: &str = "governance"; + + #[test] + fn migrate_checks_contract_version() { + let mut deps = mock_dependencies(); + instantiate_contract(deps.as_mut()); + cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, "something wrong").unwrap(); + + assert!(v0_5_0::migrate(deps.as_mut().storage).is_err()); + + cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, v0_5_0::BASE_VERSION) + .unwrap(); + + assert!(v0_5_0::migrate(deps.as_mut().storage).is_ok()); + } + + #[test] + fn migrate_sets_contract_version() { + let mut deps = mock_dependencies(); + instantiate_contract(deps.as_mut()); + + migrate(deps.as_mut(), mock_env(), Empty {}).unwrap(); + + let contract_version = cw2::get_contract_version(deps.as_mut().storage).unwrap(); + assert_eq!(contract_version.contract, CONTRACT_NAME); + assert_eq!(contract_version.version, CONTRACT_VERSION); + } + + #[test] + fn config_gets_migrated() { + let mut deps = mock_dependencies(); + instantiate_contract(deps.as_mut()); + + assert!(v0_5_0::CONFIG.load(deps.as_mut().storage).is_ok()); + assert!(state::CONFIG.load(deps.as_mut().storage).is_err()); + + assert!(v0_5_0::migrate(deps.as_mut().storage).is_ok()); + + assert!(v0_5_0::CONFIG.load(deps.as_mut().storage).is_err()); + assert!(state::CONFIG.load(deps.as_mut().storage).is_ok()); + } + + #[test] + fn permission_control_gets_migrated() { + let mut deps = mock_dependencies(); + instantiate_contract(deps.as_mut()); + + assert!(v0_5_0::migrate(deps.as_mut().storage).is_ok()); + + assert!(permission_control::sender_role( + deps.as_mut().storage, + &Addr::unchecked(GOVERNANCE) + ) + .unwrap() + .contains(Permission::Governance)); + } + + #[test] + fn state_is_cleared_after_migration() { + let mut deps = mock_dependencies(); + instantiate_contract(deps.as_mut()); + + assert!(v0_5_0::migrate(deps.as_mut().storage).is_ok()); + + assert!(state::VOTES.is_empty(deps.as_ref().storage)); + assert!(state::POLLS.is_empty(deps.as_ref().storage)); + assert!(state::poll_messages().is_empty(deps.as_ref().storage)); + assert!(state::poll_verifier_sets().is_empty(deps.as_ref().storage)); + } + + fn instantiate_contract(deps: DepsMut) { + instantiate( + deps, + mock_env(), + mock_info("admin", &[]), + InstantiateMsg { + governance_address: GOVERNANCE.parse().unwrap(), + service_registry_address: "service_registry".parse().unwrap(), + service_name: "service".parse().unwrap(), + source_gateway_address: "source_gateway".parse().unwrap(), + voting_threshold: Threshold::try_from((2u64, 3u64)) + .and_then(MajorityThreshold::try_from) + .unwrap(), + block_expiry: 1, + confirmation_height: 1, + source_chain: "source-chain".parse().unwrap(), + rewards_address: "rewards".to_string(), + msg_id_format: MessageIdFormat::HexTxHashAndEventIndex, + }, + ) + .unwrap(); + } + + #[deprecated(since = "0.5.0", note = "only used to test the migration")] + fn instantiate( + deps: DepsMut, + _env: Env, + _info: MessageInfo, + msg: InstantiateMsg, + ) -> Result { + cw2::set_contract_version(deps.storage, CONTRACT_NAME, v0_5_0::BASE_VERSION)?; + + let config = v0_5_0::Config { + governance: deps.api.addr_validate(&msg.governance_address)?, + service_name: msg.service_name, + service_registry_contract: deps.api.addr_validate(&msg.service_registry_address)?, + source_gateway_address: msg.source_gateway_address, + voting_threshold: msg.voting_threshold, + block_expiry: msg.block_expiry, + confirmation_height: msg.confirmation_height, + source_chain: msg.source_chain, + rewards_contract: deps.api.addr_validate(&msg.rewards_address)?, + msg_id_format: msg.msg_id_format, + }; + v0_5_0::CONFIG.save(deps.storage, &config)?; + + Ok(Response::new() + .add_event(Event::new("instantiated").add_attributes(>::from(config)))) + } + + #[cw_serde] + #[deprecated(since = "0.5.0", note = "only used to test the migration")] + pub struct InstantiateMsg { + /// Address that can call all messages of unrestricted governance permission level, like UpdateVotingThreshold. + /// It can execute messages that bypasses verification checks to rescue the contract if it got into an otherwise unrecoverable state due to external forces. + /// On mainnet it should match the address of the Cosmos governance module. + pub governance_address: nonempty::String, + /// Service registry contract address on axelar. + pub service_registry_address: nonempty::String, + /// Name of service in the service registry for which verifiers are registered. + pub service_name: nonempty::String, + /// Axelar's gateway contract address on the source chain + pub source_gateway_address: nonempty::String, + /// Threshold of weighted votes required for voting to be considered complete for a particular message + pub voting_threshold: MajorityThreshold, + /// The number of blocks after which a poll expires + pub block_expiry: u64, + /// The number of blocks to wait for on the source chain before considering a transaction final + pub confirmation_height: u64, + /// Name of the source chain + pub source_chain: ChainName, + /// Rewards contract address on axelar. + pub rewards_address: String, + /// Format that incoming messages should use for the id field of CrossChainId + pub msg_id_format: MessageIdFormat, + } +} diff --git a/contracts/voting-verifier/src/query.rs b/contracts/voting-verifier/src/contract/query.rs similarity index 52% rename from contracts/voting-verifier/src/query.rs rename to contracts/voting-verifier/src/contract/query.rs index 534105857..4e477503b 100644 --- a/contracts/voting-verifier/src/query.rs +++ b/contracts/voting-verifier/src/contract/query.rs @@ -1,16 +1,12 @@ -use axelar_wasm_std::{ - voting::{PollStatus, Vote}, - MajorityThreshold, VerificationStatus, -}; +use axelar_wasm_std::voting::{PollId, PollStatus, Vote}; +use axelar_wasm_std::{MajorityThreshold, VerificationStatus}; use cosmwasm_std::Deps; use multisig::verifier_set::VerifierSet; use router_api::Message; -use crate::{error::ContractError, state::CONFIG}; -use crate::{ - msg::MessageStatus, - state::{self, Poll, PollContent, POLLS, POLL_MESSAGES, POLL_VERIFIER_SETS}, -}; +use crate::error::ContractError; +use crate::msg::{MessageStatus, PollData, PollResponse}; +use crate::state::{poll_messages, poll_verifier_sets, Poll, PollContent, CONFIG, POLLS}; pub fn voting_threshold(deps: Deps) -> Result { Ok(CONFIG.load(deps.storage)?.voting_threshold) @@ -19,38 +15,91 @@ pub fn voting_threshold(deps: Deps) -> Result pub fn messages_status( deps: Deps, messages: &[Message], + cur_block_height: u64, ) -> Result, ContractError> { messages .iter() .map(|message| { - message_status(deps, message) + message_status(deps, message, cur_block_height) .map(|status| MessageStatus::new(message.to_owned(), status)) }) .collect() } -pub fn message_status(deps: Deps, message: &Message) -> Result { - let loaded_poll_content = POLL_MESSAGES.may_load(deps.storage, &message.hash())?; +pub fn message_status( + deps: Deps, + message: &Message, + cur_block_height: u64, +) -> Result { + let loaded_poll_content = poll_messages().may_load(deps.storage, &message.hash())?; + + Ok(verification_status( + deps, + loaded_poll_content, + message, + cur_block_height, + )) +} - Ok(verification_status(deps, loaded_poll_content, message)) +pub fn poll_response( + deps: Deps, + current_block_height: u64, + poll_id: PollId, +) -> Result { + let poll = POLLS.load(deps.storage, poll_id)?; + let (data, status) = match &poll { + Poll::Messages(poll) => { + let msgs = poll_messages().idx.load_messages(deps.storage, poll_id)?; + assert_eq!( + poll.tallies.len(), + msgs.len(), + "data inconsistency for number of messages in poll {}", + poll.poll_id + ); + + (PollData::Messages(msgs), poll.status(current_block_height)) + } + Poll::ConfirmVerifierSet(poll) => ( + PollData::VerifierSet( + poll_verifier_sets() + .idx + .load_verifier_set(deps.storage, poll_id)? + .expect("verifier set not found in poll"), + ), + poll.status(current_block_height), + ), + }; + + Ok(PollResponse { + poll: poll.weighted_poll(), + data, + status, + }) } pub fn verifier_set_status( deps: Deps, verifier_set: &VerifierSet, + cur_block_height: u64, ) -> Result { - let loaded_poll_content = POLL_VERIFIER_SETS.may_load( + let loaded_poll_content = poll_verifier_sets().may_load( deps.storage, &verifier_set.hash().as_slice().try_into().unwrap(), )?; - Ok(verification_status(deps, loaded_poll_content, verifier_set)) + Ok(verification_status( + deps, + loaded_poll_content, + verifier_set, + cur_block_height, + )) } fn verification_status( deps: Deps, stored_poll_content: Option>, content: &T, + cur_block_height: u64, ) -> VerificationStatus { match stored_poll_content { Some(stored) => { @@ -73,7 +122,9 @@ fn verification_status( Some(Vote::SucceededOnChain) => VerificationStatus::SucceededOnSourceChain, Some(Vote::FailedOnChain) => VerificationStatus::FailedOnSourceChain, Some(Vote::NotFound) => VerificationStatus::NotFoundOnSourceChain, - None if is_finished(&poll) => VerificationStatus::FailedToVerify, + None if voting_completed(&poll, cur_block_height) => { + VerificationStatus::FailedToVerify + } None => VerificationStatus::InProgress, } } @@ -81,45 +132,47 @@ fn verification_status( } } -fn is_finished(poll: &state::Poll) -> bool { +fn voting_completed(poll: &Poll, cur_block_height: u64) -> bool { match poll { - state::Poll::Messages(poll) | state::Poll::ConfirmVerifierSet(poll) => { - poll.status == PollStatus::Finished + Poll::Messages(poll) | Poll::ConfirmVerifierSet(poll) => { + matches!( + poll.status(cur_block_height), + PollStatus::Expired | PollStatus::Finished + ) } } } #[cfg(test)] mod tests { - use axelar_wasm_std::{ - msg_id::tx_hash_event_index::HexTxHashAndEventIndex, - nonempty, - voting::{PollId, Tallies, Vote, WeightedPoll}, - Participant, Snapshot, Threshold, - }; - use cosmwasm_std::{testing::mock_dependencies, Addr, Uint128, Uint64}; + use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; + use axelar_wasm_std::voting::{PollId, Tallies, Vote, WeightedPoll}; + use axelar_wasm_std::{nonempty, Participant, Snapshot, Threshold}; + use cosmwasm_std::testing::{mock_dependencies, mock_env}; + use cosmwasm_std::{Addr, Uint128, Uint64}; + use itertools::Itertools; use router_api::CrossChainId; - use crate::state::PollContent; - use super::*; + use crate::state::PollContent; #[test] fn verification_status_in_progress() { let mut deps = mock_dependencies(); let idx = 0; + let cur_block_height = 100; - let poll = poll(); + let poll = poll(cur_block_height + 10); POLLS .save( deps.as_mut().storage, poll.poll_id, - &state::Poll::Messages(poll.clone()), + &Poll::Messages(poll.clone()), ) .unwrap(); let msg = message(1); - POLL_MESSAGES + poll_messages() .save( deps.as_mut().storage, &msg.hash(), @@ -132,7 +185,7 @@ mod tests { msg.clone(), VerificationStatus::InProgress )], - messages_status(deps.as_ref(), &[msg]).unwrap() + messages_status(deps.as_ref(), &[msg], cur_block_height).unwrap() ); } @@ -140,8 +193,9 @@ mod tests { fn verification_status_verified() { let mut deps = mock_dependencies(); let idx = 0; + let cur_block_height = 100; - let mut poll = poll(); + let mut poll = poll(cur_block_height + 10); poll.tallies[idx] = Tallies::default(); poll.tallies[idx].tally(&Vote::SucceededOnChain, &Uint128::from(5u64)); @@ -149,12 +203,12 @@ mod tests { .save( deps.as_mut().storage, poll.poll_id, - &state::Poll::Messages(poll.clone()), + &Poll::Messages(poll.clone()), ) .unwrap(); let msg = message(1); - POLL_MESSAGES + poll_messages() .save( deps.as_mut().storage, &msg.hash(), @@ -167,7 +221,7 @@ mod tests { msg.clone(), VerificationStatus::SucceededOnSourceChain )], - messages_status(deps.as_ref(), &[msg]).unwrap() + messages_status(deps.as_ref(), &[msg], cur_block_height).unwrap() ); } @@ -175,20 +229,22 @@ mod tests { fn verification_status_failed_to_verify() { let mut deps = mock_dependencies(); let idx = 0; + let cur_block_height = 100; + let poll_duration = 10; + let expires_at = cur_block_height + poll_duration; - let mut poll = poll(); - poll.status = PollStatus::Finished; + let poll = poll(expires_at); POLLS .save( deps.as_mut().storage, poll.poll_id, - &state::Poll::Messages(poll.clone()), + &Poll::Messages(poll.clone()), ) .unwrap(); let msg = message(1); - POLL_MESSAGES + poll_messages() .save( deps.as_mut().storage, &msg.hash(), @@ -201,7 +257,7 @@ mod tests { msg.clone(), VerificationStatus::FailedToVerify )], - messages_status(deps.as_ref(), &[msg]).unwrap() + messages_status(deps.as_ref(), &[msg], expires_at).unwrap() ); } @@ -212,30 +268,65 @@ mod tests { assert_eq!( vec![MessageStatus::new(msg.clone(), VerificationStatus::Unknown)], - messages_status(deps.as_ref(), &[msg]).unwrap() + messages_status(deps.as_ref(), &[msg], 0).unwrap() + ); + } + + #[test] + #[allow(clippy::cast_possible_truncation)] + fn poll_response() { + let mut deps = mock_dependencies(); + + let poll = poll(1); + POLLS + .save( + deps.as_mut().storage, + poll.poll_id, + &Poll::Messages(poll.clone()), + ) + .unwrap(); + + let messages = (0..poll.poll_size as u32).map(message); + messages.clone().enumerate().for_each(|(idx, msg)| { + poll_messages() + .save( + deps.as_mut().storage, + &msg.hash(), + &PollContent::::new(msg, poll.poll_id, idx), + ) + .unwrap() + }); + + assert_eq!( + PollResponse { + poll: poll.clone(), + data: PollData::Messages(messages.collect_vec()), + status: PollStatus::Expired + }, + super::poll_response(deps.as_ref(), mock_env().block.height, poll.poll_id).unwrap() ); } - fn message(id: u64) -> Message { + fn message(id: u32) -> Message { Message { - cc_id: CrossChainId { - chain: "source-chain".parse().unwrap(), - id: HexTxHashAndEventIndex { + cc_id: CrossChainId::new( + "source-chain", + HexTxHashAndEventIndex { tx_hash: [0; 32], - event_index: id as u32, + event_index: id, } .to_string() - .try_into() - .unwrap(), - }, - source_address: format!("source_address{id}").parse().unwrap(), + .as_str(), + ) + .unwrap(), + source_address: format!("source-address{id}").parse().unwrap(), destination_chain: format!("destination-chain{id}").parse().unwrap(), - destination_address: format!("destination_address{id}").parse().unwrap(), + destination_address: format!("destination-address{id}").parse().unwrap(), payload_hash: [0; 32], } } - pub fn poll() -> WeightedPoll { + pub fn poll(expires_at: u64) -> WeightedPoll { let participants: nonempty::Vec = vec!["addr1", "addr2", "addr3"] .into_iter() .map(|participant| Participant { @@ -252,6 +343,6 @@ mod tests { let snapshot = Snapshot::new(threshold.try_into().unwrap(), participants); - WeightedPoll::new(PollId::from(Uint64::one()), snapshot, 0, 5) + WeightedPoll::new(PollId::from(Uint64::one()), snapshot, expires_at, 5) } } diff --git a/contracts/voting-verifier/src/error.rs b/contracts/voting-verifier/src/error.rs index cee961235..0945232f6 100644 --- a/contracts/voting-verifier/src/error.rs +++ b/contracts/voting-verifier/src/error.rs @@ -1,5 +1,4 @@ -use axelar_wasm_std::{nonempty, voting}; -use axelar_wasm_std_derive::IntoContractError; +use axelar_wasm_std::{nonempty, voting, IntoContractError}; use cosmwasm_std::{OverflowError, StdError}; use router_api::ChainName; use service_registry; @@ -39,6 +38,12 @@ pub enum ContractError { #[error("unauthorized")] Unauthorized, + + #[error("poll results have different length")] + PollResultsLengthUnequal, + + #[error("invalid source address")] + InvalidSourceAddress, } impl From for StdError { diff --git a/contracts/voting-verifier/src/events.rs b/contracts/voting-verifier/src/events.rs index 3bd523c91..61d2cf404 100644 --- a/contracts/voting-verifier/src/events.rs +++ b/contracts/voting-verifier/src/events.rs @@ -1,14 +1,14 @@ use std::str::FromStr; use std::vec::Vec; -use axelar_wasm_std::msg_id::base_58_event_index::Base58TxDigestAndEventIndex; -use axelar_wasm_std::msg_id::tx_hash_event_index::HexTxHashAndEventIndex; -use axelar_wasm_std::msg_id::MessageIdFormat; +use axelar_wasm_std::msg_id::{ + Base58SolanaTxSignatureAndEventIndex, Base58TxDigestAndEventIndex, HexTxHash, + HexTxHashAndEventIndex, MessageIdFormat, +}; +use axelar_wasm_std::voting::{PollId, Vote}; +use axelar_wasm_std::{nonempty, VerificationStatus}; use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, Attribute, Event}; - -use axelar_wasm_std::nonempty; -use axelar_wasm_std::voting::{PollId, Vote}; use multisig::verifier_set::VerifierSet; use router_api::{Address, ChainName, Message}; @@ -17,23 +17,44 @@ use crate::state::Config; impl From for Vec { fn from(other: Config) -> Self { + // destructuring the Config struct so changes to the fields don't go unnoticed + let Config { + service_name, + service_registry_contract, + source_gateway_address, + voting_threshold, + block_expiry, + confirmation_height, + source_chain, + rewards_contract, + msg_id_format, + address_format, + } = other; + vec![ - ("service_name", other.service_name.to_string()), + ("service_name", service_name.to_string()), ( "service_registry_contract", - other.service_registry_contract.to_string(), - ), - ( - "source_gateway_address", - other.source_gateway_address.to_string(), + service_registry_contract.to_string(), ), + ("source_gateway_address", source_gateway_address.to_string()), ( "voting_threshold", - serde_json::to_string(&other.voting_threshold) + serde_json::to_string(&voting_threshold) .expect("failed to serialize voting_threshold"), ), - ("block_expiry", other.block_expiry.to_string()), - ("confirmation_height", other.confirmation_height.to_string()), + ("block_expiry", block_expiry.to_string()), + ("confirmation_height", confirmation_height.to_string()), + ("source_chain", source_chain.to_string()), + ("rewards_contract", rewards_contract.to_string()), + ( + "msg_id_format", + serde_json::to_string(&msg_id_format).expect("failed to serialize msg_id_format"), + ), + ( + "address_format", + serde_json::to_string(&address_format).expect("failed to serialize address_format"), + ), ] .into_iter() .map(Attribute::from) @@ -122,27 +143,39 @@ pub struct VerifierSetConfirmation { /// If parsing is successful, returns (tx_id, event_index). Otherwise returns ContractError::InvalidMessageID fn parse_message_id( - message_id: nonempty::String, + message_id: &str, msg_id_format: &MessageIdFormat, ) -> Result<(nonempty::String, u32), ContractError> { match msg_id_format { MessageIdFormat::Base58TxDigestAndEventIndex => { - let id = Base58TxDigestAndEventIndex::from_str(&message_id) - .map_err(|_| ContractError::InvalidMessageID(message_id.into()))?; + let id = Base58TxDigestAndEventIndex::from_str(message_id) + .map_err(|_| ContractError::InvalidMessageID(message_id.to_string()))?; Ok((id.tx_digest_as_base58(), id.event_index)) } MessageIdFormat::HexTxHashAndEventIndex => { - let id = HexTxHashAndEventIndex::from_str(&message_id) - .map_err(|_| ContractError::InvalidMessageID(message_id.into()))?; + let id = HexTxHashAndEventIndex::from_str(message_id) + .map_err(|_| ContractError::InvalidMessageID(message_id.to_string()))?; Ok((id.tx_hash_as_hex(), id.event_index)) } + MessageIdFormat::Base58SolanaTxSignatureAndEventIndex => { + let id = Base58SolanaTxSignatureAndEventIndex::from_str(message_id) + .map_err(|_| ContractError::InvalidMessageID(message_id.to_string()))?; + + Ok((id.signature_as_base58(), id.event_index)) + } + MessageIdFormat::HexTxHash => { + let id = HexTxHash::from_str(message_id) + .map_err(|_| ContractError::InvalidMessageID(message_id.into()))?; + + Ok((id.tx_hash_as_hex(), 0)) + } } } impl VerifierSetConfirmation { pub fn new( - message_id: nonempty::String, + message_id: &str, msg_id_format: MessageIdFormat, verifier_set: VerifierSet, ) -> Result { @@ -173,7 +206,7 @@ pub struct TxEventConfirmation { impl TryFrom<(Message, &MessageIdFormat)> for TxEventConfirmation { type Error = ContractError; fn try_from((msg, msg_id_format): (Message, &MessageIdFormat)) -> Result { - let (tx_id, event_index) = parse_message_id(msg.cc_id.id, msg_id_format)?; + let (tx_id, event_index) = parse_message_id(&msg.cc_id.message_id, msg_id_format)?; Ok(TxEventConfirmation { tx_id, @@ -204,6 +237,7 @@ impl From for Event { pub struct PollEnded { pub poll_id: PollId, + pub source_chain: ChainName, pub results: Vec>, } @@ -214,6 +248,11 @@ impl From for Event { "poll_id", serde_json::to_string(&other.poll_id).expect("failed to serialize poll_id"), ) + .add_attribute( + "source_chain", + serde_json::to_string(&other.source_chain) + .expect("failed to serialize source_chain"), + ) .add_attribute( "results", serde_json::to_string(&other.results).expect("failed to serialize results"), @@ -221,17 +260,41 @@ impl From for Event { } } +pub struct QuorumReached { + pub content: T, + pub status: VerificationStatus, + pub poll_id: PollId, +} + +impl From> for Event +where + T: cosmwasm_schema::serde::Serialize, +{ + fn from(value: QuorumReached) -> Self { + Event::new("quorum_reached") + .add_attribute( + "content", + serde_json::to_string(&value.content).expect("failed to serialize content"), + ) + .add_attribute( + "status", + serde_json::to_string(&value.status).expect("failed to serialize status"), + ) + .add_attribute( + "poll_id", + serde_json::to_string(&value.poll_id).expect("failed to serialize poll_id"), + ) + } +} + #[cfg(test)] mod test { use std::collections::BTreeMap; - use axelar_wasm_std::{ - msg_id::{ - base_58_event_index::Base58TxDigestAndEventIndex, - tx_hash_event_index::HexTxHashAndEventIndex, MessageIdFormat, - }, - nonempty, + use axelar_wasm_std::msg_id::{ + Base58TxDigestAndEventIndex, HexTxHash, HexTxHashAndEventIndex, MessageIdFormat, }; + use axelar_wasm_std::nonempty; use cosmwasm_std::Uint128; use multisig::verifier_set::VerifierSet; use router_api::{CrossChainId, Message}; @@ -248,11 +311,8 @@ mod test { fn generate_msg(msg_id: nonempty::String) -> Message { Message { - cc_id: CrossChainId { - chain: "source-chain".parse().unwrap(), - id: msg_id, - }, - source_address: "source_address".parse().unwrap(), + cc_id: CrossChainId::new("source-chain", msg_id).unwrap(), + source_address: "source-address".parse().unwrap(), destination_chain: "destination-chain".parse().unwrap(), destination_address: "destination-address".parse().unwrap(), payload_hash: [0; 32], @@ -267,7 +327,7 @@ mod test { } #[test] - fn should_make_tx_event_confirmation_with_hex_msg_id() { + fn should_make_tx_event_confirmation_with_hex_event_index_msg_id() { let msg_id = HexTxHashAndEventIndex { tx_hash: random_32_bytes(), event_index: 0, @@ -283,6 +343,21 @@ mod test { compare_event_to_message(event, msg); } + #[test] + fn should_make_tx_event_confirmation_with_hex_msg_id() { + let msg_id = HexTxHash { + tx_hash: random_32_bytes(), + }; + let msg = generate_msg(msg_id.to_string().parse().unwrap()); + + let event = + TxEventConfirmation::try_from((msg.clone(), &MessageIdFormat::HexTxHash)).unwrap(); + + assert_eq!(event.tx_id, msg_id.tx_hash_as_hex()); + assert_eq!(event.event_index, 0); + compare_event_to_message(event, msg); + } + #[test] fn should_make_tx_event_confirmation_with_base58_msg_id() { let msg_id = Base58TxDigestAndEventIndex { @@ -337,7 +412,7 @@ mod test { created_at: 1, }; let event = VerifierSetConfirmation::new( - msg_id.to_string().parse().unwrap(), + &msg_id.to_string(), MessageIdFormat::HexTxHashAndEventIndex, verifier_set.clone(), ) @@ -360,7 +435,7 @@ mod test { created_at: 1, }; let event = VerifierSetConfirmation::new( - msg_id.to_string().parse().unwrap(), + &msg_id.to_string(), MessageIdFormat::Base58TxDigestAndEventIndex, verifier_set.clone(), ) @@ -381,7 +456,7 @@ mod test { }; let event = VerifierSetConfirmation::new( - msg_id.to_string().parse().unwrap(), + msg_id, MessageIdFormat::Base58TxDigestAndEventIndex, verifier_set, ); @@ -401,7 +476,7 @@ mod test { }; let event = VerifierSetConfirmation::new( - msg_id.to_string().parse().unwrap(), + &msg_id.to_string(), MessageIdFormat::Base58TxDigestAndEventIndex, verifier_set, ); diff --git a/contracts/voting-verifier/src/lib.rs b/contracts/voting-verifier/src/lib.rs index 20ce222dd..2cfb800f1 100644 --- a/contracts/voting-verifier/src/lib.rs +++ b/contracts/voting-verifier/src/lib.rs @@ -4,8 +4,5 @@ pub use client::Client; pub mod contract; pub mod error; pub mod events; -pub mod execute; -mod migrations; pub mod msg; -pub mod query; -pub mod state; +mod state; diff --git a/contracts/voting-verifier/src/msg.rs b/contracts/voting-verifier/src/msg.rs index 3e25cd51d..b85e84234 100644 --- a/contracts/voting-verifier/src/msg.rs +++ b/contracts/voting-verifier/src/msg.rs @@ -1,11 +1,9 @@ +use axelar_wasm_std::address::AddressFormat; +use axelar_wasm_std::msg_id::MessageIdFormat; +use axelar_wasm_std::voting::{PollId, PollStatus, Vote, WeightedPoll}; +use axelar_wasm_std::{nonempty, MajorityThreshold, VerificationStatus}; use cosmwasm_schema::{cw_serde, QueryResponses}; - -use axelar_wasm_std::{ - msg_id::MessageIdFormat, - nonempty, - voting::{PollId, Vote}, - MajorityThreshold, VerificationStatus, -}; +use msgs_derive::EnsurePermissions; use multisig::verifier_set::VerifierSet; use router_api::{ChainName, Message}; @@ -24,69 +22,75 @@ pub struct InstantiateMsg { /// Threshold of weighted votes required for voting to be considered complete for a particular message pub voting_threshold: MajorityThreshold, /// The number of blocks after which a poll expires - pub block_expiry: u64, + pub block_expiry: nonempty::Uint64, /// The number of blocks to wait for on the source chain before considering a transaction final pub confirmation_height: u64, /// Name of the source chain pub source_chain: ChainName, /// Rewards contract address on axelar. - pub rewards_address: String, + pub rewards_address: nonempty::String, /// Format that incoming messages should use for the id field of CrossChainId pub msg_id_format: MessageIdFormat, + pub address_format: AddressFormat, } #[cw_serde] +#[derive(EnsurePermissions)] pub enum ExecuteMsg { // Computes the results of a poll // For all verified messages, calls MessagesVerified on the verifier - EndPoll { - poll_id: PollId, - }, + #[permission(Any)] + EndPoll { poll_id: PollId }, // Casts votes for specified poll - Vote { - poll_id: PollId, - votes: Vec, - }, + #[permission(Any)] + Vote { poll_id: PollId, votes: Vec }, // returns a vector of true/false values, indicating current verification status for each message // starts a poll for any not yet verified messages - VerifyMessages { - messages: Vec, - }, + #[permission(Any)] + VerifyMessages(Vec), // Starts a poll to confirm a verifier set update on the external gateway + #[permission(Any)] VerifyVerifierSet { message_id: nonempty::String, new_verifier_set: VerifierSet, }, // Update the threshold used for new polls. Callable only by governance + #[permission(Governance)] UpdateVotingThreshold { new_voting_threshold: MajorityThreshold, }, } #[cw_serde] -pub struct Poll { - poll_id: PollId, - messages: Vec, +pub enum PollData { + Messages(Vec), + VerifierSet(VerifierSet), +} +#[cw_serde] +pub struct PollResponse { + pub poll: WeightedPoll, + pub data: PollData, + pub status: PollStatus, } #[cw_serde] #[derive(QueryResponses)] pub enum QueryMsg { - #[returns(Poll)] - GetPoll { poll_id: PollId }, + #[returns(PollResponse)] + Poll { poll_id: PollId }, #[returns(Vec)] - GetMessagesStatus { messages: Vec }, + MessagesStatus(Vec), #[returns(VerificationStatus)] - GetVerifierSetStatus { new_verifier_set: VerifierSet }, + VerifierSetStatus(VerifierSet), #[returns(MajorityThreshold)] - GetCurrentThreshold, + CurrentThreshold, } #[cw_serde] diff --git a/contracts/voting-verifier/src/state.rs b/contracts/voting-verifier/src/state.rs index b88160765..63f6bfc8b 100644 --- a/contracts/voting-verifier/src/state.rs +++ b/contracts/voting-verifier/src/state.rs @@ -1,15 +1,11 @@ +use axelar_wasm_std::address::AddressFormat; +use axelar_wasm_std::hash::Hash; +use axelar_wasm_std::msg_id::MessageIdFormat; +use axelar_wasm_std::voting::{PollId, Vote, WeightedPoll}; +use axelar_wasm_std::{counter, nonempty, MajorityThreshold}; use cosmwasm_schema::cw_serde; -use cosmwasm_std::Addr; -use cw_storage_plus::{Item, Map}; - -use axelar_wasm_std::{ - counter, - hash::Hash, - msg_id::MessageIdFormat, - nonempty, - voting::{PollId, WeightedPoll}, - MajorityThreshold, -}; +use cosmwasm_std::{Addr, Order, StdResult, Storage}; +use cw_storage_plus::{Index, IndexList, IndexedMap, Item, Map, MultiIndex}; use multisig::verifier_set::VerifierSet; use router_api::{ChainName, Message}; @@ -17,16 +13,16 @@ use crate::error::ContractError; #[cw_serde] pub struct Config { - pub governance: Addr, pub service_registry_contract: Addr, pub service_name: nonempty::String, pub source_gateway_address: nonempty::String, pub voting_threshold: MajorityThreshold, - pub block_expiry: u64, // number of blocks after which a poll expires + pub block_expiry: nonempty::Uint64, // number of blocks after which a poll expires pub confirmation_height: u64, pub source_chain: ChainName, pub rewards_contract: Addr, pub msg_id_format: MessageIdFormat, + pub address_format: AddressFormat, } #[cw_serde] @@ -46,6 +42,13 @@ impl Poll { Poll::ConfirmVerifierSet(poll) => Ok(Poll::ConfirmVerifierSet(func(poll)?)), } } + + pub fn weighted_poll(self) -> WeightedPoll { + match self { + Poll::Messages(poll) => poll, + Poll::ConfirmVerifierSet(poll) => poll, + } + } } #[cw_serde] @@ -79,8 +82,130 @@ pub const POLL_ID: counter::Counter = counter::Counter::new("poll_id"); pub const POLLS: Map = Map::new("polls"); -pub const POLL_MESSAGES: Map<&Hash, PollContent> = Map::new("poll_messages"); +type VerifierAddr = String; +pub const VOTES: Map<(PollId, VerifierAddr), Vec> = Map::new("votes"); pub const CONFIG: Item = Item::new("config"); -pub const POLL_VERIFIER_SETS: Map<&Hash, PollContent> = Map::new("poll_verifier_sets"); +/// A multi-index that indexes a message by (PollID, index in poll) pair. The primary key of the underlying +/// map is the hash of the message (typed as Hash). This allows looking up a Message by it's hash, +/// or by a (PollID, index in poll) pair. The PollID is stored as a String +pub struct PollMessagesIndex<'a>(MultiIndex<'a, (String, u32), PollContent, &'a Hash>); + +impl<'a> PollMessagesIndex<'a> { + fn new( + idx_fn: fn(&[u8], &PollContent) -> (String, u32), + pk_namespace: &'a str, + idx_namespace: &'a str, + ) -> Self { + PollMessagesIndex(MultiIndex::new(idx_fn, pk_namespace, idx_namespace)) + } + + pub fn load_message( + &self, + storage: &dyn Storage, + poll_id: PollId, + index_in_poll: u32, + ) -> StdResult> { + match self + .0 + .prefix((poll_id.to_string(), index_in_poll)) + .range(storage, None, None, Order::Ascending) + .collect::)>, _>>()? + .as_slice() + { + [] => Ok(None), + [(_, content)] => Ok(Some(content.content.to_owned())), + _ => panic!("More than one message for poll_id and index_in_poll"), + } + } + + pub fn load_messages(&self, storage: &dyn Storage, poll_id: PollId) -> StdResult> { + poll_messages() + .idx + .0 + .sub_prefix(poll_id.to_string()) + .range(storage, None, None, Order::Ascending) + .map(|item| item.map(|(_, poll_content)| poll_content.content)) + .collect::>>() + } +} + +const POLL_MESSAGES_PKEY_NAMESPACE: &str = "poll_messages"; +const POLL_MESSAGES_IDX_NAMESPACE: &str = "poll_messages_idx"; + +pub fn poll_messages<'a>() -> IndexedMap<'a, &'a Hash, PollContent, PollMessagesIndex<'a>> +{ + IndexedMap::new( + POLL_MESSAGES_PKEY_NAMESPACE, + PollMessagesIndex::new( + |_pk: &[u8], d: &PollContent| (d.poll_id.to_string(), d.index_in_poll), + POLL_MESSAGES_PKEY_NAMESPACE, + POLL_MESSAGES_IDX_NAMESPACE, + ), + ) +} + +impl<'a> IndexList> for PollMessagesIndex<'a> { + fn get_indexes(&'_ self) -> Box>> + '_> { + let v: Vec<&dyn Index>> = vec![&self.0]; + Box::new(v.into_iter()) + } +} + +/// A multi-index that indexes a verifier set by PollID. The primary key of the underlying +/// map is the hash of the verifier set (typed as Hash). This allows looking up a VerifierSet by it's hash, +/// or by a PollID. PollIDs are stored as String. +pub struct PollVerifierSetsIndex<'a>(MultiIndex<'a, String, PollContent, &'a Hash>); + +impl<'a> PollVerifierSetsIndex<'a> { + fn new( + idx_fn: fn(&[u8], &PollContent) -> String, + pk_namespace: &'a str, + idx_namespace: &'a str, + ) -> Self { + PollVerifierSetsIndex(MultiIndex::new(idx_fn, pk_namespace, idx_namespace)) + } + + pub fn load_verifier_set( + &self, + storage: &dyn Storage, + poll_id: PollId, + ) -> StdResult> { + match self + .0 + .prefix(poll_id.to_string()) + .range(storage, None, None, Order::Ascending) + .collect::)>, _>>()? + .as_slice() + { + [] => Ok(None), + [(_, content)] => Ok(Some(content.content.to_owned())), + _ => panic!("More than one verifier_set for poll_id"), + } + } +} + +const POLL_VERIFIER_SETS_PKEY_NAMESPACE: &str = "poll_verifier_sets"; +const POLL_VERIFIER_SETS_IDX_NAMESPACE: &str = "poll_verifier_sets_idx"; + +pub fn poll_verifier_sets<'a>( +) -> IndexedMap<'a, &'a Hash, PollContent, PollVerifierSetsIndex<'a>> { + IndexedMap::new( + POLL_VERIFIER_SETS_PKEY_NAMESPACE, + PollVerifierSetsIndex::new( + |_pk: &[u8], d: &PollContent| d.poll_id.to_string(), + POLL_VERIFIER_SETS_PKEY_NAMESPACE, + POLL_VERIFIER_SETS_IDX_NAMESPACE, + ), + ) +} + +impl<'a> IndexList> for PollVerifierSetsIndex<'a> { + fn get_indexes( + &'_ self, + ) -> Box>> + '_> { + let v: Vec<&dyn Index>> = vec![&self.0]; + Box::new(v.into_iter()) + } +} diff --git a/doc/README.md b/doc/README.md index 88a313bee..4e03fe37c 100644 --- a/doc/README.md +++ b/doc/README.md @@ -33,4 +33,4 @@ mdbook serve doc --open ## Contributing Information about how to contribute to the documentation can be found in the documentation -chapter [here](http://localhost:3000/contributing/documentation.html) +chapter [here](src/contributing/documentation.md) diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index 4799b6ae3..e9229e3e2 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -13,6 +13,10 @@ - [Rewards](contracts/rewards.md) - [Coordinator](contracts/coordinator.md) +## Message Access Requirements + +- [Amplifier Access Control](message_access.md) + # Contributing -- [Documentation](contributing/documentation.md) +- [Documentation](contributing/documentation.md) \ No newline at end of file diff --git a/doc/src/contracts/service_registry.md b/doc/src/contracts/service_registry.md index 7fe2b84dd..60237bf55 100644 --- a/doc/src/contracts/service_registry.md +++ b/doc/src/contracts/service_registry.md @@ -26,7 +26,7 @@ pub enum ExecuteMsg { // Can only be called by governance account RegisterService { service_name: String, - service_contract: Addr, + coordinator_contract: Addr, min_num_verifiers: u16, max_num_verifiers: Option, min_verifier_bond: Uint128, diff --git a/doc/src/message_access.md b/doc/src/message_access.md new file mode 100644 index 000000000..71eb26691 --- /dev/null +++ b/doc/src/message_access.md @@ -0,0 +1,134 @@ +# Access Control for Contract Messages +This module provides access control for contract execute messages. An execute message can be called by: +- Anyone +- Only by governance +- Only by the contract admin +- Either governance or the contract admin + +Only contracts that have at least one execute message with restricted access are included in this module. + +## Router + +### Governance-Only +```rust +RegisterChain { + chain: ChainName, + gateway_address: Address, + msg_id_format: MessageIdFormat, +}, + +UpgradeGateway { + chain: ChainName, + contract_address: Address, +}, +``` + +### Admin-Only +```rust +FreezeChain { + chain: ChainName, + direction: GatewayDirection, +}, + +UnfreezeChain { + chain: ChainName, + direction: GatewayDirection, +} +``` + + +## Verifier + +### Governance-Only +```rust +UpdateVotingThreshold { + new_voting_threshold: MajorityThreshold, +} +``` + + +## Prover + +### Governance-Only +```rust +UpdateSigningThreshold { + new_signing_threshold: MajorityThreshold, +}, + +UpdateAdmin { + new_admin_address: String, +} +``` + +### Admin or Governance +```rust +UpdateVerifierSet +``` + + +## Multisig + +### Governance-Only +```rust +AuthorizeCaller { + contract_address: Addr, +}, + +UnauthorizeCaller { + contract_address: Addr, +} +``` + +### Authorized Caller Only +Authorized caller is any contract that is previously authorized from governance by calling `AuthorizeCaller`. +```rust +StartSigningSession { // Can only be called by an authorized contract + verifier_set_id: String, + msg: HexBinary, + chain_name: ChainName, + sig_verifier: Option, +} +``` + + +## Service Registry + +### Governance-Only +```rust +RegisterService { + service_name: String, + coordinator_contract: Addr, + min_num_verifiers: u16, + max_num_verifiers: Option, + min_verifier_bond: Uint128, + bond_denom: String, + unbonding_period_days: u16, + description: String, +}, + +AuthorizeVerifiers { + verifiers: Vec, + service_name: String, +}, + +UnauthorizeVerifiers { + verifiers: Vec, + service_name: String, +}, + +JailVerifiers { + verifiers: Vec, + service_name: String, +} +``` + + +## Coordinator + +### Governance-Only +```rust +RegisterProverContract { + chain_name: ChainName, + new_prover_addr: Addr, +} +``` \ No newline at end of file diff --git a/external-gateways/stellar/Cargo.toml b/external-gateways/stellar/Cargo.toml new file mode 100644 index 000000000..3efc2cb84 --- /dev/null +++ b/external-gateways/stellar/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "stellar" +version = "1.0.0" +edition = { workspace = true } +rust-version = { workspace = true } + +[dependencies] +axelar-wasm-std = { workspace = true } +cosmwasm-std = { workspace = true } +error-stack = { workspace = true } +hex = "0.4.3" +multisig = { workspace = true, features = ["library"] } +router-api = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +sha3 = { workspace = true } +stellar-strkey = { version = "0.0.8" } +stellar-xdr = { version = "21.2.0" } +thiserror = { workspace = true } + +[lints] +workspace = true + +[dev-dependencies] +goldie = { workspace = true } +rand = "0.8.5" diff --git a/external-gateways/stellar/release.toml b/external-gateways/stellar/release.toml new file mode 100644 index 000000000..954564097 --- /dev/null +++ b/external-gateways/stellar/release.toml @@ -0,0 +1 @@ +release = false diff --git a/external-gateways/stellar/src/error.rs b/external-gateways/stellar/src/error.rs new file mode 100644 index 000000000..3722a04fe --- /dev/null +++ b/external-gateways/stellar/src/error.rs @@ -0,0 +1,11 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum Error { + #[error("unsupported type of public key")] + UnsupportedPublicKey, + #[error("invalid public key")] + InvalidPublicKey, + #[error("invalid destination address")] + InvalidDestinationAddress, +} diff --git a/external-gateways/stellar/src/lib.rs b/external-gateways/stellar/src/lib.rs new file mode 100644 index 000000000..279629793 --- /dev/null +++ b/external-gateways/stellar/src/lib.rs @@ -0,0 +1,341 @@ +pub mod error; + +use std::str::FromStr; + +use axelar_wasm_std::utils::TryMapExt; +use cosmwasm_std::Uint256; +use error_stack::{Report, ResultExt}; +use multisig::key::PublicKey; +use multisig::verifier_set::VerifierSet; +use sha3::{Digest, Keccak256}; +use stellar_strkey::Contract; +use stellar_xdr::curr::{ + BytesM, Error as XdrError, Hash, Limits, ScAddress, ScMapEntry, ScVal, StringM, VecM, WriteXdr, +}; + +use crate::error::Error; + +#[derive(Debug, Clone)] +pub enum CommandType { + ApproveMessages, + RotateSigners, +} + +impl CommandType { + pub fn as_str(&self) -> &'static str { + match self { + CommandType::ApproveMessages => "ApproveMessages", + CommandType::RotateSigners => "RotateSigners", + } + } +} + +impl TryFrom for ScVal { + type Error = XdrError; + + fn try_from(value: CommandType) -> Result { + let val: VecM = + vec![ScVal::Symbol(StringM::from_str(value.as_str())?.into())].try_into()?; + + Ok(ScVal::Vec(Some(val.into()))) + } +} + +#[derive(Debug, Clone)] +pub struct Message { + pub message_id: String, + pub source_chain: String, + pub source_address: String, + pub contract_address: Contract, + pub payload_hash: Hash, +} + +impl TryFrom<&router_api::Message> for Message { + type Error = Report; + + fn try_from(value: &router_api::Message) -> Result { + Ok(Self { + source_chain: value.cc_id.source_chain.to_string(), + message_id: value.cc_id.message_id.to_string(), + source_address: value.source_address.to_string(), + contract_address: Contract::from_string(value.destination_address.as_str()) + .change_context(Error::InvalidDestinationAddress) + .attach_printable(value.destination_address.to_string())?, + payload_hash: value.payload_hash.into(), + }) + } +} + +impl TryFrom for ScVal { + type Error = XdrError; + + fn try_from(value: Message) -> Result { + let keys: [&'static str; 5] = [ + "contract_address", + "message_id", + "payload_hash", + "source_address", + "source_chain", + ]; + + let vals: [ScVal; 5] = [ + ScVal::Address(ScAddress::Contract(Hash(value.contract_address.0))), + ScVal::String(StringM::from_str(&value.message_id)?.into()), + ScVal::Bytes(BytesM::try_from(AsRef::<[u8; 32]>::as_ref(&value.payload_hash))?.into()), + ScVal::String(StringM::from_str(&value.source_address)?.into()), + ScVal::String(StringM::from_str(&value.source_chain)?.into()), + ]; + + sc_map_from_slices(&keys, &vals) + } +} + +pub struct Messages(Vec); + +impl From> for Messages { + fn from(v: Vec) -> Self { + Messages(v) + } +} + +impl Messages { + pub fn messages_approval_hash(&self) -> Result<[u8; 32], XdrError> { + let messages = self + .0 + .iter() + .map(|message| message.clone().try_into()) + .collect::, _>>()?; + + let val: ScVal = (CommandType::ApproveMessages, messages) + .try_into() + .expect("must convert tuple of size 2 to ScVec"); + + let hash = Keccak256::digest(val.to_xdr(Limits::none())?); + + Ok(hash.into()) + } +} + +#[derive(Clone, Debug)] +pub struct WeightedSigner { + pub signer: BytesM<32>, + pub weight: u128, +} + +impl TryFrom for ScVal { + type Error = XdrError; + + fn try_from(value: WeightedSigner) -> Result { + let keys: [&'static str; 2] = ["signer", "weight"]; + + let vals: [ScVal; 2] = [ + ScVal::Bytes(value.signer.to_vec().try_into()?), + value.weight.into(), + ]; + + sc_map_from_slices(&keys, &vals) + } +} + +#[derive(Debug, Clone)] +pub struct WeightedSigners { + pub signers: Vec, + pub threshold: u128, + pub nonce: BytesM<32>, +} + +impl WeightedSigners { + pub fn hash(&self) -> Result<[u8; 32], XdrError> { + let val: ScVal = self.clone().try_into()?; + let hash = Keccak256::digest(val.to_xdr(Limits::none())?); + + Ok(hash.into()) + } + + pub fn signers_rotation_hash(&self) -> Result<[u8; 32], XdrError> { + let val: ScVal = (CommandType::RotateSigners, self.clone()) + .try_into() + .expect("must convert tuple of size 2 to ScVec"); + + let hash = Keccak256::digest(val.to_xdr(Limits::none())?); + + Ok(hash.into()) + } +} + +impl TryFrom for ScVal { + type Error = XdrError; + + fn try_from(value: WeightedSigners) -> Result { + let signers = value.signers.clone().try_map(|signer| signer.try_into())?; + + let keys: [&'static str; 3] = ["nonce", "signers", "threshold"]; + + let vals: [ScVal; 3] = [ + ScVal::Bytes(value.nonce.to_vec().try_into()?), + ScVal::Vec(Some(signers.try_into()?)), + value.threshold.into(), + ]; + + sc_map_from_slices(&keys, &vals) + } +} + +impl TryFrom<&VerifierSet> for WeightedSigners { + type Error = Report; + + fn try_from(value: &VerifierSet) -> Result { + let mut signers = value + .signers + .values() + .map(|signer| match &signer.pub_key { + PublicKey::Ed25519(key) => Ok(WeightedSigner { + signer: BytesM::try_from(key.as_ref()) + .change_context(Error::InvalidPublicKey) + .attach_printable(key.to_hex())?, + weight: signer.weight.into(), + }), + PublicKey::Ecdsa(_) => Err(Report::new(Error::UnsupportedPublicKey)), + }) + .collect::, _>>()?; + + signers.sort_by(|signer1, signer2| signer1.signer.cmp(&signer2.signer)); + + let nonce = Uint256::from(value.created_at) + .to_be_bytes() + .try_into() + .expect("must convert from 32 bytes"); + + Ok(Self { + signers, + threshold: value.threshold.into(), + nonce, + }) + } +} + +/// Form a new Map from a slice of symbol-names and a slice of values. Keys must be in sorted order. +fn sc_map_from_slices(keys: &[&str], vals: &[ScVal]) -> Result { + let vec: VecM = keys + .iter() + .zip(vals.iter()) + .map(|(key, val)| { + Ok(ScMapEntry { + key: ScVal::Symbol(StringM::from_str(key)?.into()), + val: val.clone(), + }) + }) + .collect::, XdrError>>()? + .try_into()?; + + Ok(ScVal::Map(Some(vec.into()))) +} + +#[cfg(test)] +mod test { + use axelar_wasm_std::FnExt; + use cosmwasm_std::HexBinary; + use serde::Serialize; + use stellar_xdr::curr::{Limits, ScVal, WriteXdr}; + + use crate::{CommandType, Message, Messages, WeightedSigner, WeightedSigners}; + + #[test] + fn command_type_encode() { + #[derive(Serialize)] + struct Encoded { + approve_messages: String, + rotate_signers: String, + } + let approve_messages = ScVal::try_from(CommandType::ApproveMessages) + .unwrap() + .to_xdr(Limits::none()) + .unwrap() + .then(HexBinary::from) + .to_hex(); + let rotate_signers = ScVal::try_from(CommandType::RotateSigners) + .unwrap() + .to_xdr(Limits::none()) + .unwrap() + .then(HexBinary::from) + .to_hex(); + + let encoded = Encoded { + approve_messages, + rotate_signers, + }; + + goldie::assert_json!(&encoded); + } + + #[test] + fn messages_approval_hash() { + let payload_hashes = [ + "cfa347779c9b646ddf628c4da721976ceb998f1ab2c097b52e66a575c3975a6c", + "fb5eb8245e3b8eb9d44f228ee142a3378f57d49fc95fa78d437ff8aa5dd564ba", + "90e3761c0794fbbd8b563a0d05d83395e7f88f64f30eebb7c5533329f6653e84", + "60e146cb9c548ba6e614a87910d8172c9d21279a3f8f4da256ff36e15b80ea30", + ]; + + let messages: Messages = (1..=4) + .map(|i| Message { + message_id: format!("test-{}", i), + source_chain: format!("source-{}", i), + source_address: "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + .to_string(), + contract_address: "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + .parse() + .unwrap(), + payload_hash: payload_hashes[i - 1].parse().expect("invalid hash"), + }) + .collect::>() + .into(); + goldie::assert!(HexBinary::from(messages.messages_approval_hash().unwrap()).to_hex()); + } + + #[test] + fn signers_rotation_hash() { + let weighted_signers = WeightedSigners { + signers: vec![ + WeightedSigner { + signer: "0a245a2a2a5e8ec439d1377579a08fc78ea55647ba6fcb1f5d8a360218e8a985" + .parse() + .unwrap(), + weight: 3, + }, + WeightedSigner { + signer: "0b422cf449d900f6f8eb97f62e35811c62eb75feb84dfccef44a5c1c3dbac2ad" + .parse() + .unwrap(), + weight: 2, + }, + WeightedSigner { + signer: "18c34bf01a11b5ba21ea11b1678f3035ef753f0bdb1d5014ec21037e8f99e2a2" + .parse() + .unwrap(), + weight: 4, + }, + WeightedSigner { + signer: "f683ca8a6d7fe55f25599bb64b01edcc5eeb85fe5b63d3a4f0b3c32405005518" + .parse() + .unwrap(), + weight: 4, + }, + WeightedSigner { + signer: "fbb4b870e800038f1379697fae3058938c59b696f38dd0fdf2659c0cf3a5b663" + .parse() + .unwrap(), + weight: 2, + }, + ], + threshold: 8, + nonce: "8784bf7be5a9baaeea47e12d9e8ad0dec29afcbc3617d97f771e3c24fa945dce" + .parse() + .unwrap(), + }; + + goldie::assert!( + HexBinary::from(weighted_signers.signers_rotation_hash().unwrap()).to_hex() + ); + } +} diff --git a/external-gateways/stellar/src/testdata/command_type_encode.golden b/external-gateways/stellar/src/testdata/command_type_encode.golden new file mode 100644 index 000000000..6db86d933 --- /dev/null +++ b/external-gateways/stellar/src/testdata/command_type_encode.golden @@ -0,0 +1,4 @@ +{ + "approve_messages": "0000001000000001000000010000000f0000000f417070726f76654d6573736167657300", + "rotate_signers": "0000001000000001000000010000000f0000000d526f746174655369676e657273000000" +} \ No newline at end of file diff --git a/external-gateways/stellar/src/testdata/messages_approval_hash.golden b/external-gateways/stellar/src/testdata/messages_approval_hash.golden new file mode 100644 index 000000000..4512173fc --- /dev/null +++ b/external-gateways/stellar/src/testdata/messages_approval_hash.golden @@ -0,0 +1 @@ +49f6a85aec4b4f72c667f2ba7950a692a59f462007abdbce0181e2982c0b602e \ No newline at end of file diff --git a/external-gateways/stellar/src/testdata/signers_rotation_hash.golden b/external-gateways/stellar/src/testdata/signers_rotation_hash.golden new file mode 100644 index 000000000..774e8446b --- /dev/null +++ b/external-gateways/stellar/src/testdata/signers_rotation_hash.golden @@ -0,0 +1 @@ +4ad8f3015146ac68334fd405f90e6ca75fbf2c276b333a8747c9ba83d9c3f1f6 \ No newline at end of file diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 665c8b6e0..b60cf7729 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "integration-tests" -version = "0.1.0" +version = "1.0.0" rust-version = { workspace = true } -edition = "2021" +edition = { workspace = true } description = "Amplifier Integration Tests" exclude = [ @@ -25,7 +25,7 @@ library = [] optimize = """docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/rust-optimizer:0.12.6 + cosmwasm/optimizer:0.16.0 """ [dependencies] @@ -36,7 +36,7 @@ cw-multi-test = "0.15.1" error-stack = { workspace = true } gateway = { workspace = true } gateway-api = { workspace = true } -k256 = { version = "0.13.1", features = ["ecdsa"] } +k256 = { workspace = true } multisig = { workspace = true } multisig-prover = { workspace = true } rand = "0.8.5" @@ -48,7 +48,7 @@ serde = { workspace = true } serde_json = { workspace = true } service-registry = { workspace = true } sha3 = { workspace = true } -tofn = { git = "https://github.com/axelarnetwork/tofn.git", branch = "update-deps" } +tofn = { workspace = true } voting-verifier = { workspace = true } [lints] diff --git a/integration-tests/src/contract.rs b/integration-tests/src/contract.rs index 4d2cea980..487ca9ca2 100644 --- a/integration-tests/src/contract.rs +++ b/integration-tests/src/contract.rs @@ -22,7 +22,7 @@ pub trait Contract { app: &mut App, caller: Addr, execute_message: &Self::ExMsg, - ) -> Result + ) -> Result where Self::ExMsg: Serialize, Self::ExMsg: std::fmt::Debug, @@ -36,7 +36,7 @@ pub trait Contract { caller: Addr, execute_message: &Self::ExMsg, funds: &[Coin], - ) -> Result + ) -> Result where Self::ExMsg: Serialize, Self::ExMsg: std::fmt::Debug, @@ -49,7 +49,7 @@ pub trait Contract { ) .map_err(|err| { report!(err - .downcast::() + .downcast::() .unwrap_or_else(|err| err.downcast::().unwrap().into())) }) } diff --git a/integration-tests/src/coordinator_contract.rs b/integration-tests/src/coordinator_contract.rs index 7b178253f..c742492c1 100644 --- a/integration-tests/src/coordinator_contract.rs +++ b/integration-tests/src/coordinator_contract.rs @@ -1,8 +1,9 @@ -use crate::contract::Contract; use coordinator::contract::{execute, instantiate, query}; use cosmwasm_std::Addr; use cw_multi_test::{App, ContractWrapper, Executor}; +use crate::contract::Contract; + #[derive(Clone)] pub struct CoordinatorContract { pub contract_addr: Addr, diff --git a/integration-tests/src/gateway_contract.rs b/integration-tests/src/gateway_contract.rs index 7ed4b8585..73ec11b32 100644 --- a/integration-tests/src/gateway_contract.rs +++ b/integration-tests/src/gateway_contract.rs @@ -1,7 +1,8 @@ -use crate::contract::Contract; use cosmwasm_std::Addr; use cw_multi_test::{App, ContractWrapper, Executor}; +use crate::contract::Contract; + #[derive(Clone)] pub struct GatewayContract { pub contract_addr: Addr, diff --git a/integration-tests/src/multisig_contract.rs b/integration-tests/src/multisig_contract.rs index 63d35b0d0..ef7bbefc4 100644 --- a/integration-tests/src/multisig_contract.rs +++ b/integration-tests/src/multisig_contract.rs @@ -1,7 +1,9 @@ -use crate::contract::Contract; +use axelar_wasm_std::nonempty; use cosmwasm_std::Addr; use cw_multi_test::{App, ContractWrapper, Executor}; +use crate::contract::Contract; + #[derive(Clone)] pub struct MultisigContract { pub contract_addr: Addr, @@ -11,8 +13,9 @@ impl MultisigContract { pub fn instantiate_contract( app: &mut App, governance: Addr, + admin: Addr, rewards_address: Addr, - block_expiry: u64, + block_expiry: nonempty::Uint64, ) -> Self { let code = ContractWrapper::new( multisig::contract::execute, @@ -28,6 +31,7 @@ impl MultisigContract { &multisig::msg::InstantiateMsg { rewards_address: rewards_address.to_string(), governance_address: governance.to_string(), + admin_address: admin.to_string(), block_expiry, }, &[], diff --git a/integration-tests/src/multisig_prover_contract.rs b/integration-tests/src/multisig_prover_contract.rs index a86ba7f20..8db0da918 100644 --- a/integration-tests/src/multisig_prover_contract.rs +++ b/integration-tests/src/multisig_prover_contract.rs @@ -1,9 +1,11 @@ -use crate::{contract::Contract, protocol::Protocol}; use axelar_wasm_std::Threshold; use cosmwasm_std::Addr; use cw_multi_test::{ContractWrapper, Executor}; use multisig::key::KeyType; -use multisig_prover::encoding::Encoder; +use multisig_prover::Encoder; + +use crate::contract::Contract; +use crate::protocol::Protocol; #[derive(Clone)] pub struct MultisigProverContract { diff --git a/integration-tests/src/protocol.rs b/integration-tests/src/protocol.rs index dd7ae0506..962ad2dff 100644 --- a/integration-tests/src/protocol.rs +++ b/integration-tests/src/protocol.rs @@ -2,11 +2,11 @@ use axelar_wasm_std::nonempty; use cosmwasm_std::Addr; use cw_multi_test::App; -use crate::{ - coordinator_contract::CoordinatorContract, multisig_contract::MultisigContract, - rewards_contract::RewardsContract, router_contract::RouterContract, - service_registry_contract::ServiceRegistryContract, -}; +use crate::coordinator_contract::CoordinatorContract; +use crate::multisig_contract::MultisigContract; +use crate::rewards_contract::RewardsContract; +use crate::router_contract::RouterContract; +use crate::service_registry_contract::ServiceRegistryContract; pub struct Protocol { pub genesis_address: Addr, // holds u128::max coins, can use to send coins to other addresses diff --git a/integration-tests/src/rewards_contract.rs b/integration-tests/src/rewards_contract.rs index 48f1f02dc..fb8572b1b 100644 --- a/integration-tests/src/rewards_contract.rs +++ b/integration-tests/src/rewards_contract.rs @@ -1,23 +1,19 @@ -use crate::contract::Contract; -use cosmwasm_std::{Addr, Binary, Deps, Env, StdResult}; +use cosmwasm_std::Addr; use cw_multi_test::{App, ContractWrapper, Executor}; +use crate::contract::Contract; + #[derive(Clone)] pub struct RewardsContract { pub contract_addr: Addr, } impl RewardsContract { - pub fn instantiate_contract( - app: &mut App, - governance: Addr, - rewards_denom: String, - params: rewards::msg::Params, - ) -> Self { + pub fn instantiate_contract(app: &mut App, governance: Addr, rewards_denom: String) -> Self { let code = ContractWrapper::new( rewards::contract::execute, rewards::contract::instantiate, - |_: Deps, _: Env, _: rewards::msg::QueryMsg| -> StdResult { todo!() }, + rewards::contract::query, ); let code_id = app.store_code(Box::new(code)); @@ -28,7 +24,6 @@ impl RewardsContract { &rewards::msg::InstantiateMsg { governance_address: governance.to_string(), rewards_denom, - params, }, &[], "rewards", diff --git a/integration-tests/src/router_contract.rs b/integration-tests/src/router_contract.rs index 1ade90804..45ca7e33f 100644 --- a/integration-tests/src/router_contract.rs +++ b/integration-tests/src/router_contract.rs @@ -1,7 +1,8 @@ -use crate::contract::Contract; use cosmwasm_std::Addr; use cw_multi_test::{App, ContractWrapper, Executor}; +use crate::contract::Contract; + #[derive(Clone)] pub struct RouterContract { pub contract_addr: Addr, diff --git a/integration-tests/src/service_registry_contract.rs b/integration-tests/src/service_registry_contract.rs index 34d79c477..59afd08da 100644 --- a/integration-tests/src/service_registry_contract.rs +++ b/integration-tests/src/service_registry_contract.rs @@ -1,8 +1,9 @@ -use crate::contract::Contract; use cosmwasm_std::Addr; use cw_multi_test::{App, ContractWrapper, Executor}; use service_registry::contract::{execute, instantiate, query}; +use crate::contract::Contract; + #[derive(Clone)] pub struct ServiceRegistryContract { pub contract_addr: Addr, diff --git a/integration-tests/src/voting_verifier_contract.rs b/integration-tests/src/voting_verifier_contract.rs index 5f992770b..daa53f20e 100644 --- a/integration-tests/src/voting_verifier_contract.rs +++ b/integration-tests/src/voting_verifier_contract.rs @@ -1,11 +1,11 @@ -use crate::contract::Contract; -use crate::protocol::Protocol; -use axelar_wasm_std::nonempty; -use axelar_wasm_std::MajorityThreshold; +use axelar_wasm_std::{nonempty, MajorityThreshold}; use cosmwasm_std::Addr; use cw_multi_test::{ContractWrapper, Executor}; use router_api::ChainName; +use crate::contract::Contract; +use crate::protocol::Protocol; + #[derive(Clone)] pub struct VotingVerifierContract { pub contract_addr: Addr, @@ -41,11 +41,17 @@ impl VotingVerifierContract { service_name: protocol.service_name.clone(), source_gateway_address, voting_threshold, - block_expiry: 10, + block_expiry: 10.try_into().unwrap(), confirmation_height: 5, source_chain, - rewards_address: protocol.rewards.contract_addr.to_string(), + rewards_address: protocol + .rewards + .contract_addr + .to_string() + .try_into() + .unwrap(), msg_id_format: axelar_wasm_std::msg_id::MessageIdFormat::HexTxHashAndEventIndex, + address_format: axelar_wasm_std::address::AddressFormat::Eip55, }, &[], "voting_verifier", diff --git a/integration-tests/tests/bond_unbond.rs b/integration-tests/tests/bond_unbond.rs index 16e2118a8..257b39e35 100644 --- a/integration-tests/tests/bond_unbond.rs +++ b/integration-tests/tests/bond_unbond.rs @@ -1,11 +1,20 @@ use cosmwasm_std::BlockInfo; +use integration_tests::contract::Contract; +use service_registry::msg::ExecuteMsg; pub mod test_utils; #[test] -fn verifiers_can_claim_stake() { +fn claim_stake_after_rotation_success() { + let chains: Vec = vec![ + "Ethereum".try_into().unwrap(), + "Polygon".try_into().unwrap(), + ]; + let test_utils::TestCase { mut protocol, + chain1: ethereum, + chain2: polygon, verifiers, min_verifier_bond, unbonding_period_days, @@ -14,8 +23,29 @@ fn verifiers_can_claim_stake() { let before_balances = test_utils::query_balances(&protocol.app, &verifiers); + let new_verifiers = test_utils::create_new_verifiers_vec( + chains.clone(), + vec![("verifier3".to_string(), 3), ("verifier4".to_string(), 4)], + ); + + test_utils::register_verifiers(&mut protocol, &new_verifiers, min_verifier_bond); + test_utils::deregister_verifiers(&mut protocol, &verifiers); + test_utils::rotate_active_verifier_set(&mut protocol, ethereum, &verifiers, &new_verifiers); + test_utils::rotate_active_verifier_set(&mut protocol, polygon, &verifiers, &new_verifiers); + + for verifier in &verifiers { + let response = protocol.service_registry.execute( + &mut protocol.app, + verifier.addr.clone(), + &ExecuteMsg::UnbondVerifier { + service_name: protocol.service_name.to_string(), + }, + ); + assert!(response.is_ok()); + } + // balances don't change after deregistering assert_eq!( before_balances, @@ -33,10 +63,262 @@ fn verifiers_can_claim_stake() { ..block }); - test_utils::claim_stakes(&mut protocol, &verifiers); + let claim_results = test_utils::claim_stakes(&mut protocol, &verifiers); + for claim_result in claim_results { + assert!(claim_result.is_ok()); + } + let after_balances = test_utils::query_balances(&protocol.app, &verifiers); for (before_balance, after_balance) in before_balances.into_iter().zip(after_balances) { - assert_eq!(after_balance, before_balance + min_verifier_bond); + assert_eq!( + after_balance, + before_balance + min_verifier_bond.into_inner() + ); + } +} + +#[test] +fn claim_stake_when_in_all_active_verifier_sets_fails() { + let chains: Vec = vec![ + "Ethereum".try_into().unwrap(), + "Polygon".try_into().unwrap(), + ]; + + let test_utils::TestCase { + mut protocol, + verifiers, + min_verifier_bond, + .. + } = test_utils::setup_test_case(); + + let new_verifiers = test_utils::create_new_verifiers_vec( + chains.clone(), + vec![("verifier3".to_string(), 3), ("verifier4".to_string(), 4)], + ); + + test_utils::register_verifiers(&mut protocol, &new_verifiers, min_verifier_bond); + + test_utils::deregister_verifiers(&mut protocol, &verifiers); + + for verifier in &verifiers { + let response = protocol.service_registry.execute( + &mut protocol.app, + verifier.addr.clone(), + &ExecuteMsg::UnbondVerifier { + service_name: protocol.service_name.to_string(), + }, + ); + assert!(response.is_ok()); + } + + let claim_results = test_utils::claim_stakes(&mut protocol, &verifiers); + for claim_result in claim_results { + assert!(claim_result.is_err()); + } +} + +#[test] +fn claim_stake_when_in_some_active_verifier_sets_fails() { + let chains: Vec = vec![ + "Ethereum".try_into().unwrap(), + "Polygon".try_into().unwrap(), + ]; + + let test_utils::TestCase { + mut protocol, + chain1: ethereum, + verifiers, + min_verifier_bond, + .. + } = test_utils::setup_test_case(); + + let new_verifiers = test_utils::create_new_verifiers_vec( + chains.clone(), + vec![("verifier3".to_string(), 3), ("verifier4".to_string(), 4)], + ); + + test_utils::register_verifiers(&mut protocol, &new_verifiers, min_verifier_bond); + + test_utils::deregister_verifiers(&mut protocol, &verifiers); + + // Only rotate the first chain's verifier set + test_utils::rotate_active_verifier_set(&mut protocol, ethereum, &verifiers, &new_verifiers); + + for verifier in &verifiers { + let response = protocol.service_registry.execute( + &mut protocol.app, + verifier.addr.clone(), + &ExecuteMsg::UnbondVerifier { + service_name: protocol.service_name.to_string(), + }, + ); + assert!(response.is_ok()); + } + + let claim_results = test_utils::claim_stakes(&mut protocol, &verifiers); + for claim_result in claim_results { + assert!(claim_result.is_err()); + } +} + +#[test] +fn claim_stake_after_deregistering_before_rotation_fails() { + let chains: Vec = vec![ + "Ethereum".try_into().unwrap(), + "Polygon".try_into().unwrap(), + ]; + + let test_utils::TestCase { + mut protocol, + verifiers, + min_verifier_bond, + .. + } = test_utils::setup_test_case(); + + let new_verifiers = test_utils::create_new_verifiers_vec( + chains.clone(), + vec![("verifier3".to_string(), 3), ("verifier4".to_string(), 4)], + ); + + test_utils::register_verifiers(&mut protocol, &new_verifiers, min_verifier_bond); + + for verifier in &verifiers { + let response = protocol.service_registry.execute( + &mut protocol.app, + verifier.addr.clone(), + &ExecuteMsg::DeregisterChainSupport { + service_name: protocol.service_name.to_string(), + chains: chains.clone(), + }, + ); + assert!(response.is_ok()); + } + + for verifier in &verifiers { + let response = protocol.service_registry.execute( + &mut protocol.app, + verifier.addr.clone(), + &ExecuteMsg::UnbondVerifier { + service_name: protocol.service_name.to_string(), + }, + ); + assert!(response.is_ok()); + } + + let claim_results = test_utils::claim_stakes(&mut protocol, &verifiers); + for claim_result in claim_results { + assert!(claim_result.is_err()); + } +} + +#[test] +fn claim_stake_when_jailed_fails() { + let chains: Vec = vec![ + "Ethereum".try_into().unwrap(), + "Polygon".try_into().unwrap(), + ]; + + let test_utils::TestCase { + mut protocol, + chain1: ethereum, + chain2: polygon, + verifiers, + min_verifier_bond, + .. + } = test_utils::setup_test_case(); + + let new_verifiers = test_utils::create_new_verifiers_vec( + chains.clone(), + vec![("verifier3".to_string(), 3), ("verifier4".to_string(), 4)], + ); + + test_utils::register_verifiers(&mut protocol, &new_verifiers, min_verifier_bond); + + test_utils::deregister_verifiers(&mut protocol, &verifiers); + + test_utils::rotate_active_verifier_set(&mut protocol, ethereum, &verifiers, &new_verifiers); + test_utils::rotate_active_verifier_set(&mut protocol, polygon, &verifiers, &new_verifiers); + + for verifier in &verifiers { + let response = protocol.service_registry.execute( + &mut protocol.app, + verifier.addr.clone(), + &ExecuteMsg::UnbondVerifier { + service_name: protocol.service_name.to_string(), + }, + ); + assert!(response.is_ok()); + } + + let response = protocol.service_registry.execute( + &mut protocol.app, + protocol.governance_address.clone(), + &ExecuteMsg::JailVerifiers { + service_name: protocol.service_name.to_string(), + verifiers: vec!["verifier1".to_string(), "verifier2".to_string()], + }, + ); + assert!(response.is_ok()); + + let claim_results = test_utils::claim_stakes(&mut protocol, &verifiers); + for claim_result in claim_results { + assert!(claim_result.clone().is_err()); + } +} + +#[test] +fn claim_stake_when_in_next_verifier_sets_fails() { + let chains: Vec = vec![ + "Ethereum".try_into().unwrap(), + "Polygon".try_into().unwrap(), + ]; + + let test_utils::TestCase { + mut protocol, + chain1: ethereum, + chain2: polygon, + verifiers, + min_verifier_bond, + .. + } = test_utils::setup_test_case(); + + let new_verifiers = test_utils::create_new_verifiers_vec( + chains.clone(), + vec![("verifier3".to_string(), 3), ("verifier4".to_string(), 4)], + ); + + test_utils::register_verifiers(&mut protocol, &new_verifiers, min_verifier_bond); + + test_utils::deregister_verifiers(&mut protocol, &verifiers); + + let response = ethereum.multisig_prover.execute( + &mut protocol.app, + ethereum.multisig_prover.admin_addr.clone(), + &multisig_prover::msg::ExecuteMsg::UpdateVerifierSet, + ); + assert!(response.is_ok()); + + let response = polygon.multisig_prover.execute( + &mut protocol.app, + polygon.multisig_prover.admin_addr.clone(), + &multisig_prover::msg::ExecuteMsg::UpdateVerifierSet, + ); + assert!(response.is_ok()); + + for verifier in &verifiers { + let response = protocol.service_registry.execute( + &mut protocol.app, + verifier.addr.clone(), + &ExecuteMsg::UnbondVerifier { + service_name: protocol.service_name.to_string(), + }, + ); + assert!(response.is_ok()); + } + + let claim_results = test_utils::claim_stakes(&mut protocol, &verifiers); + for claim_result in claim_results { + assert!(claim_result.is_err()); } } diff --git a/integration-tests/tests/chain_freeze_unfreeze.rs b/integration-tests/tests/chain_freeze_unfreeze.rs index 8cc2984a5..f1f46d0a8 100644 --- a/integration-tests/tests/chain_freeze_unfreeze.rs +++ b/integration-tests/tests/chain_freeze_unfreeze.rs @@ -1,5 +1,4 @@ use cosmwasm_std::{Addr, HexBinary}; - use integration_tests::contract::Contract; use router_api::{CrossChainId, Message}; @@ -17,14 +16,12 @@ fn chain_can_be_freezed_unfreezed() { } = test_utils::setup_test_case(); let msgs = vec![Message { - cc_id: CrossChainId { - chain: chain1.chain_name.clone(), - id: "0x88d7956fd7b6fcec846548d83bd25727f2585b4be3add21438ae9fbb34625924-3" - .to_string() - .try_into() - .unwrap(), - }, - source_address: "0xBf12773B49()0e1Deb57039061AAcFA2A87DEaC9b9" + cc_id: CrossChainId::new( + chain1.chain_name.clone(), + "0x88d7956fd7b6fcec846548d83bd25727f2585b4be3add21438ae9fbb34625924-3", + ) + .unwrap(), + source_address: "0xBf12773B490e1Deb57039061AAcFA2A87DEaC9b9" .to_string() .try_into() .unwrap(), @@ -64,7 +61,7 @@ fn chain_can_be_freezed_unfreezed() { test_utils::freeze_chain( &mut protocol.app, &protocol.router, - &chain1.chain_name, + chain1.chain_name.clone(), router_api::GatewayDirection::Bidirectional, &protocol.router_admin_address, ); @@ -91,7 +88,7 @@ fn chain_can_be_freezed_unfreezed() { // routed message should have been preserved let found_msgs = - test_utils::get_messages_from_gateway(&mut protocol.app, &chain2.gateway, &msg_ids); + test_utils::messages_from_gateway(&mut protocol.app, &chain2.gateway, &msg_ids); assert_eq!(found_msgs, msgs); // can route again diff --git a/integration-tests/tests/message_routing.rs b/integration-tests/tests/message_routing.rs index bdc2764b7..6ed73be9b 100644 --- a/integration-tests/tests/message_routing.rs +++ b/integration-tests/tests/message_routing.rs @@ -1,5 +1,4 @@ use cosmwasm_std::{Addr, HexBinary, Uint128}; - use integration_tests::contract::Contract; use router_api::{CrossChainId, Message}; @@ -20,14 +19,12 @@ fn single_message_can_be_verified_and_routed_and_proven_and_rewards_are_distribu } = test_utils::setup_test_case(); let msgs = vec![Message { - cc_id: CrossChainId { - chain: chain1.chain_name.clone(), - id: "0x88d7956fd7b6fcec846548d83bd25727f2585b4be3add21438ae9fbb34625924-3" - .to_string() - .try_into() - .unwrap(), - }, - source_address: "0xBf12773B49()0e1Deb57039061AAcFA2A87DEaC9b9" + cc_id: CrossChainId::new( + chain1.chain_name.clone(), + "0x88d7956fd7b6fcec846548d83bd25727f2585b4be3add21438ae9fbb34625924-3", + ) + .unwrap(), + source_address: "0xBf12773B490e1Deb57039061AAcFA2A87DEaC9b9" .to_string() .try_into() .unwrap(), @@ -67,7 +64,7 @@ fn single_message_can_be_verified_and_routed_and_proven_and_rewards_are_distribu // check that the message can be found at the outgoing gateway let found_msgs = - test_utils::get_messages_from_gateway(&mut protocol.app, &chain2.gateway, &msg_ids); + test_utils::messages_from_gateway(&mut protocol.app, &chain2.gateway, &msg_ids); assert_eq!(found_msgs, msgs); // trigger signing and submit all necessary signatures @@ -78,7 +75,7 @@ fn single_message_can_be_verified_and_routed_and_proven_and_rewards_are_distribu &verifiers, ); - let proof = test_utils::get_proof(&mut protocol.app, &chain2.multisig_prover, &session_id); + let proof = test_utils::proof(&mut protocol.app, &chain2.multisig_prover, &session_id); // proof should be complete by now assert!(matches!( @@ -126,14 +123,12 @@ fn routing_to_incorrect_gateway_interface() { .. } = test_utils::setup_test_case(); - let msgs = vec![Message { - cc_id: CrossChainId { - chain: chain1.chain_name.clone(), - id: "0x88d7956fd7b6fcec846548d83bd25727f2585b4be3add21438ae9fbb34625924-3" - .to_string() - .try_into() - .unwrap(), - }, + let msgs = [Message { + cc_id: CrossChainId::new( + chain1.chain_name.clone(), + "0x88d7956fd7b6fcec846548d83bd25727f2585b4be3add21438ae9fbb34625924-3", + ) + .unwrap(), source_address: "0xBf12773B49()0e1Deb57039061AAcFA2A87DEaC9b9" .to_string() .try_into() diff --git a/integration-tests/tests/test_utils/mod.rs b/integration-tests/tests/test_utils/mod.rs index fe0966818..7f1676482 100644 --- a/integration-tests/tests/test_utils/mod.rs +++ b/integration-tests/tests/test_utils/mod.rs @@ -1,42 +1,38 @@ -use axelar_wasm_std::{ - msg_id::tx_hash_event_index::HexTxHashAndEventIndex, - nonempty, - voting::{PollId, Vote}, - Participant, Threshold, -}; +use std::collections::{HashMap, HashSet}; + +use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; +use axelar_wasm_std::voting::{PollId, Vote}; +use axelar_wasm_std::{nonempty, Participant, Threshold}; +use coordinator::msg::ExecuteMsg as CoordinatorExecuteMsg; use cosmwasm_std::{ coins, Addr, Attribute, BlockInfo, Event, HexBinary, StdError, Uint128, Uint64, }; use cw_multi_test::{App, AppResponse, Executor}; -use router_api::{Address, ChainName, CrossChainId, GatewayDirection, Message}; - use integration_tests::contract::Contract; use integration_tests::coordinator_contract::CoordinatorContract; use integration_tests::gateway_contract::GatewayContract; use integration_tests::multisig_contract::MultisigContract; use integration_tests::multisig_prover_contract::MultisigProverContract; +use integration_tests::protocol::Protocol; use integration_tests::rewards_contract::RewardsContract; +use integration_tests::router_contract::RouterContract; use integration_tests::service_registry_contract::ServiceRegistryContract; use integration_tests::voting_verifier_contract::VotingVerifierContract; -use integration_tests::{protocol::Protocol, router_contract::RouterContract}; - use k256::ecdsa; -use sha3::{Digest, Keccak256}; - -use coordinator::msg::ExecuteMsg as CoordinatorExecuteMsg; -use multisig::{ - key::{KeyType, PublicKey}, - verifier_set::VerifierSet, -}; -use rewards::state::PoolId; +use multisig::key::{KeyType, PublicKey}; +use multisig::verifier_set::VerifierSet; +use multisig_prover::msg::VerifierSetResponse; +use rewards::PoolId; +use router_api::{Address, ChainName, CrossChainId, GatewayDirection, Message}; use service_registry::msg::ExecuteMsg; +use sha3::{Digest, Keccak256}; use tofn::ecdsa::KeyPair; pub const AXL_DENOMINATION: &str = "uaxl"; pub const SIGNATURE_BLOCK_EXPIRY: u64 = 100; -fn get_event_attribute<'a>( +fn find_event_attribute<'a>( events: &'a [Event], event_type: &str, attribute_name: &str, @@ -65,10 +61,10 @@ pub fn verify_messages( let response = response.unwrap(); - let poll_id = get_event_attribute(&response.events, "wasm-messages_poll_started", "poll_id") + let poll_id = find_event_attribute(&response.events, "wasm-messages_poll_started", "poll_id") .map(|attr| serde_json::from_str(&attr.value).unwrap()) .expect("couldn't get poll_id"); - let expiry = get_event_attribute(&response.events, "wasm-messages_poll_started", "expires_at") + let expiry = find_event_attribute(&response.events, "wasm-messages_poll_started", "expires_at") .map(|attr| attr.value.as_str().parse().unwrap()) .expect("couldn't get poll expiry"); (poll_id, expiry) @@ -86,16 +82,15 @@ pub fn route_messages(app: &mut App, gateway: &GatewayContract, msgs: &[Message] pub fn freeze_chain( app: &mut App, router: &RouterContract, - chain_name: &ChainName, + chain_name: ChainName, direction: GatewayDirection, admin: &Addr, ) { let response = router.execute( app, admin.clone(), - &router_api::msg::ExecuteMsg::FreezeChain { - chain: chain_name.clone(), - direction, + &router_api::msg::ExecuteMsg::FreezeChains { + chains: HashMap::from([(chain_name, direction)]), }, ); assert!(response.is_ok(), "{:?}", response); @@ -111,9 +106,8 @@ pub fn unfreeze_chain( let response = router.execute( app, admin.clone(), - &router_api::msg::ExecuteMsg::UnfreezeChain { - chain: chain_name.clone(), - direction, + &router_api::msg::ExecuteMsg::UnfreezeChains { + chains: HashMap::from([(chain_name.clone(), direction)]), }, ); assert!(response.is_ok(), "{:?}", response); @@ -203,17 +197,17 @@ pub fn construct_proof_and_sign( let response = multisig_prover.execute( &mut protocol.app, Addr::unchecked("relayer"), - &multisig_prover::msg::ExecuteMsg::ConstructProof { - message_ids: messages.iter().map(|msg| msg.cc_id.clone()).collect(), - }, + &multisig_prover::msg::ExecuteMsg::ConstructProof( + messages.iter().map(|msg| msg.cc_id.clone()).collect(), + ), ); assert!(response.is_ok()); sign_proof(protocol, verifiers, response.unwrap()) } -pub fn get_multisig_session_id(response: AppResponse) -> Uint64 { - get_event_attribute(&response.events, "wasm-signing_started", "session_id") +pub fn multisig_session_id(response: AppResponse) -> Uint64 { + find_event_attribute(&response.events, "wasm-signing_started", "session_id") .map(|attr| attr.value.as_str().try_into().unwrap()) .expect("couldn't get session_id") } @@ -223,10 +217,10 @@ pub fn sign_proof( verifiers: &Vec, response: AppResponse, ) -> Uint64 { - let msg_to_sign = get_event_attribute(&response.events, "wasm-signing_started", "msg") + let msg_to_sign = find_event_attribute(&response.events, "wasm-signing_started", "msg") .map(|attr| attr.value.clone()) .expect("couldn't find message to sign"); - let session_id = get_multisig_session_id(response); + let session_id = multisig_session_id(response); for verifier in verifiers { let signature = tofn::ecdsa::sign( @@ -257,7 +251,7 @@ pub fn sign_proof( pub fn register_service( protocol: &mut Protocol, - min_verifier_bond: Uint128, + min_verifier_bond: nonempty::Uint128, unbonding_period_days: u16, ) { let response = protocol.service_registry.execute( @@ -265,7 +259,7 @@ pub fn register_service( protocol.governance_address.clone(), &ExecuteMsg::RegisterService { service_name: protocol.service_name.to_string(), - service_contract: Addr::unchecked("nowhere"), + coordinator_contract: protocol.coordinator.contract_addr.clone(), min_num_verifiers: 0, max_num_verifiers: Some(100), min_verifier_bond, @@ -277,31 +271,29 @@ pub fn register_service( assert!(response.is_ok()); } -pub fn get_messages_from_gateway( +pub fn messages_from_gateway( app: &mut App, gateway: &GatewayContract, message_ids: &[CrossChainId], ) -> Vec { let query_response: Result, StdError> = gateway.query( app, - &gateway_api::msg::QueryMsg::GetOutgoingMessages { - message_ids: message_ids.to_owned(), - }, + &gateway_api::msg::QueryMsg::OutgoingMessages(message_ids.to_owned()), ); assert!(query_response.is_ok()); query_response.unwrap() } -pub fn get_proof( +pub fn proof( app: &mut App, multisig_prover: &MultisigProverContract, multisig_session_id: &Uint64, -) -> multisig_prover::msg::GetProofResponse { - let query_response: Result = multisig_prover +) -> multisig_prover::msg::ProofResponse { + let query_response: Result = multisig_prover .query( app, - &multisig_prover::msg::QueryMsg::GetProof { + &multisig_prover::msg::QueryMsg::Proof { multisig_session_id: *multisig_session_id, }, ); @@ -310,29 +302,15 @@ pub fn get_proof( query_response.unwrap() } -pub fn get_verifier_set_from_prover( +pub fn verifier_set_from_prover( app: &mut App, multisig_prover_contract: &MultisigProverContract, ) -> VerifierSet { - let query_response: Result = - multisig_prover_contract.query(app, &multisig_prover::msg::QueryMsg::GetVerifierSet); + let query_response: Result, StdError> = + multisig_prover_contract.query(app, &multisig_prover::msg::QueryMsg::CurrentVerifierSet); assert!(query_response.is_ok()); - query_response.unwrap() -} - -pub fn get_verifier_set_from_coordinator( - app: &mut App, - coordinator_contract: &CoordinatorContract, - chain_name: ChainName, -) -> VerifierSet { - let query_response: Result = coordinator_contract.query( - app, - &coordinator::msg::QueryMsg::GetActiveVerifiers { chain_name }, - ); - assert!(query_response.is_ok()); - - query_response.unwrap() + query_response.unwrap().unwrap().verifier_set } #[allow(clippy::arithmetic_side_effects)] @@ -377,13 +355,13 @@ pub fn setup_protocol(service_name: nonempty::String) -> Protocol { .init_balance(storage, &genesis, coins(u128::MAX, AXL_DENOMINATION)) .unwrap() }); - let router_admin_address = Addr::unchecked("admin"); + let admin_address = Addr::unchecked("admin"); let governance_address = Addr::unchecked("governance"); let nexus_gateway = Addr::unchecked("nexus_gateway"); let router = RouterContract::instantiate_contract( &mut app, - router_admin_address.clone(), + admin_address.clone(), governance_address.clone(), nexus_gateway.clone(), ); @@ -397,14 +375,14 @@ pub fn setup_protocol(service_name: nonempty::String) -> Protocol { &mut app, governance_address.clone(), AXL_DENOMINATION.to_string(), - rewards_params.clone(), ); let multisig = MultisigContract::instantiate_contract( &mut app, governance_address.clone(), + admin_address.clone(), rewards.contract_addr.clone(), - SIGNATURE_BLOCK_EXPIRY, + SIGNATURE_BLOCK_EXPIRY.try_into().unwrap(), ); let coordinator = @@ -417,7 +395,7 @@ pub fn setup_protocol(service_name: nonempty::String) -> Protocol { genesis_address: genesis, governance_address, router, - router_admin_address, + router_admin_address: admin_address, multisig, coordinator, service_registry, @@ -446,70 +424,10 @@ pub struct Verifier { pub fn register_verifiers( protocol: &mut Protocol, verifiers: &Vec, - min_verifier_bond: Uint128, + min_verifier_bond: nonempty::Uint128, ) { - let response = protocol.service_registry.execute( - &mut protocol.app, - protocol.governance_address.clone(), - &ExecuteMsg::AuthorizeVerifiers { - verifiers: verifiers - .iter() - .map(|verifier| verifier.addr.to_string()) - .collect(), - service_name: protocol.service_name.to_string(), - }, - ); - assert!(response.is_ok()); - - for verifier in verifiers { - let response = protocol.app.send_tokens( - protocol.genesis_address.clone(), - verifier.addr.clone(), - &coins(min_verifier_bond.u128(), AXL_DENOMINATION), - ); - assert!(response.is_ok()); - - let response = protocol.service_registry.execute_with_funds( - &mut protocol.app, - verifier.addr.clone(), - &ExecuteMsg::BondVerifier { - service_name: protocol.service_name.to_string(), - }, - &coins(min_verifier_bond.u128(), AXL_DENOMINATION), - ); - assert!(response.is_ok()); - - let response = protocol.service_registry.execute( - &mut protocol.app, - verifier.addr.clone(), - &ExecuteMsg::RegisterChainSupport { - service_name: protocol.service_name.to_string(), - chains: verifier.supported_chains.clone(), - }, - ); - assert!(response.is_ok()); - - let address_hash = Keccak256::digest(verifier.addr.as_bytes()); - - let sig = tofn::ecdsa::sign( - verifier.key_pair.signing_key(), - &address_hash.as_slice().try_into().unwrap(), - ) - .unwrap(); - let sig = ecdsa::Signature::from_der(&sig).unwrap(); - - let response = protocol.multisig.execute( - &mut protocol.app, - verifier.addr.clone(), - &multisig::msg::ExecuteMsg::RegisterPublicKey { - public_key: PublicKey::Ecdsa(HexBinary::from( - verifier.key_pair.encoded_verifying_key(), - )), - signed_sender_address: HexBinary::from(sig.to_vec()), - }, - ); - assert!(response.is_ok()); - } + register_in_service_registry(protocol, verifiers, min_verifier_bond); + submit_pubkeys(protocol, verifiers); } pub fn deregister_verifiers(protocol: &mut Protocol, verifiers: &Vec) { @@ -538,7 +456,12 @@ pub fn deregister_verifiers(protocol: &mut Protocol, verifiers: &Vec) } } -pub fn claim_stakes(protocol: &mut Protocol, verifiers: &Vec) { +pub fn claim_stakes( + protocol: &mut Protocol, + verifiers: &Vec, +) -> Vec> { + let mut responses = Vec::new(); + for verifier in verifiers { let response = protocol.service_registry.execute( &mut protocol.app, @@ -547,8 +470,11 @@ pub fn claim_stakes(protocol: &mut Protocol, verifiers: &Vec) { service_name: protocol.service_name.to_string(), }, ); - assert!(response.is_ok()); + + responses.push(response.map_err(|e| e.to_string())); } + + responses } pub fn confirm_verifier_set( @@ -564,15 +490,15 @@ pub fn confirm_verifier_set( assert!(response.is_ok()); } -fn get_verifier_set_poll_id_and_expiry(response: AppResponse) -> (PollId, PollExpiryBlock) { - let poll_id = get_event_attribute( +fn verifier_set_poll_id_and_expiry(response: AppResponse) -> (PollId, PollExpiryBlock) { + let poll_id = find_event_attribute( &response.events, "wasm-verifier_set_poll_started", "poll_id", ) .map(|attr| serde_json::from_str(&attr.value).unwrap()) .expect("couldn't get poll_id"); - let expiry = get_event_attribute( + let expiry = find_event_attribute( &response.events, "wasm-verifier_set_poll_started", "expires_at", @@ -604,7 +530,7 @@ pub fn create_verifier_set_poll( ); assert!(response.is_ok()); - get_verifier_set_poll_id_and_expiry(response.unwrap()) + verifier_set_poll_id_and_expiry(response.unwrap()) } pub fn verifiers_to_verifier_set( @@ -660,7 +586,7 @@ pub fn update_registry_and_construct_verifier_set_update_proof( verifiers_to_remove: &Vec, current_verifiers: &Vec, chain_multisig_prover: &MultisigProverContract, - min_verifier_bond: Uint128, + min_verifier_bond: nonempty::Uint128, ) -> Uint64 { // Register new verifiers register_verifiers(protocol, new_verifiers, min_verifier_bond); @@ -712,11 +638,15 @@ pub struct Chain { pub chain_name: ChainName, } -pub fn setup_chain(protocol: &mut Protocol, chain_name: ChainName) -> Chain { +pub fn setup_chain( + protocol: &mut Protocol, + chain_name: ChainName, + verifiers: &[Verifier], +) -> Chain { let voting_verifier = VotingVerifierContract::instantiate_contract( protocol, - "doesn't matter".to_string().try_into().unwrap(), - Threshold::try_from((9, 10)).unwrap().try_into().unwrap(), + "doesn't matter".try_into().unwrap(), + Threshold::try_from((3, 4)).unwrap().try_into().unwrap(), chain_name.clone(), ); @@ -745,8 +675,11 @@ pub fn setup_chain(protocol: &mut Protocol, chain_name: ChainName) -> Chain { let response = protocol.multisig.execute( &mut protocol.app, protocol.governance_address.clone(), - &multisig::msg::ExecuteMsg::AuthorizeCaller { - contract_address: multisig_prover.contract_addr.clone(), + &multisig::msg::ExecuteMsg::AuthorizeCallers { + contracts: HashMap::from([( + multisig_prover.contract_addr.to_string(), + chain_name.clone(), + )]), }, ); assert!(response.is_ok()); @@ -762,6 +695,38 @@ pub fn setup_chain(protocol: &mut Protocol, chain_name: ChainName) -> Chain { ); assert!(response.is_ok()); + let rewards_params = rewards::msg::Params { + epoch_duration: nonempty::Uint64::try_from(10u64).unwrap(), + rewards_per_epoch: Uint128::from(100u128).try_into().unwrap(), + participation_threshold: (1, 2).try_into().unwrap(), + }; + + let response = protocol.rewards.execute( + &mut protocol.app, + protocol.governance_address.clone(), + &rewards::msg::ExecuteMsg::CreatePool { + pool_id: PoolId { + chain_name: chain_name.clone(), + contract: voting_verifier.contract_addr.clone(), + }, + params: rewards_params.clone(), + }, + ); + assert!(response.is_ok()); + + let response = protocol.rewards.execute( + &mut protocol.app, + protocol.governance_address.clone(), + &rewards::msg::ExecuteMsg::CreatePool { + pool_id: PoolId { + chain_name: chain_name.clone(), + contract: protocol.multisig.contract_addr.clone(), + }, + params: rewards_params, + }, + ); + assert!(response.is_ok()); + let response = protocol.rewards.execute_with_funds( &mut protocol.app, protocol.genesis_address.clone(), @@ -798,6 +763,17 @@ pub fn setup_chain(protocol: &mut Protocol, chain_name: ChainName) -> Chain { ); assert!(response.is_ok()); + let mut verifier_union_set = HashSet::new(); + verifier_union_set.extend(verifiers.iter().map(|verifier| verifier.addr.clone())); + let response = protocol.coordinator.execute( + &mut protocol.app, + multisig_prover.contract_addr.clone(), + &coordinator::msg::ExecuteMsg::SetActiveVerifiers { + verifiers: verifier_union_set, + }, + ); + assert!(response.is_ok()); + Chain { gateway, voting_verifier, @@ -822,41 +798,81 @@ pub fn query_balances(app: &App, verifiers: &Vec) -> Vec { balances } +pub fn rotate_active_verifier_set( + protocol: &mut Protocol, + chain: Chain, + previous_verifiers: &Vec, + new_verifiers: &Vec, +) { + let response = chain.multisig_prover.execute( + &mut protocol.app, + chain.multisig_prover.admin_addr.clone(), + &multisig_prover::msg::ExecuteMsg::UpdateVerifierSet, + ); + assert!(response.is_ok()); + + let session_id = sign_proof(protocol, previous_verifiers, response.unwrap()); + + let proof = proof(&mut protocol.app, &chain.multisig_prover, &session_id); + assert!(matches!( + proof.status, + multisig_prover::msg::ProofStatus::Completed { .. } + )); + assert_eq!(proof.message_ids.len(), 0); + + let new_verifier_set = verifiers_to_verifier_set(protocol, new_verifiers); + let (poll_id, expiry) = create_verifier_set_poll( + &mut protocol.app, + Addr::unchecked("relayer"), + &chain.voting_verifier, + new_verifier_set.clone(), + ); + + vote_true_for_verifier_set( + &mut protocol.app, + &chain.voting_verifier, + new_verifiers, + poll_id, + ); + + advance_at_least_to_height(&mut protocol.app, expiry); + end_poll(&mut protocol.app, &chain.voting_verifier, poll_id); + + confirm_verifier_set( + &mut protocol.app, + Addr::unchecked("relayer"), + &chain.multisig_prover, + ); +} + pub struct TestCase { pub protocol: Protocol, pub chain1: Chain, pub chain2: Chain, pub verifiers: Vec, - pub min_verifier_bond: Uint128, + pub min_verifier_bond: nonempty::Uint128, pub unbonding_period_days: u16, } // Creates an instance of Axelar Amplifier with an initial verifier set registered, and returns a TestCase instance. pub fn setup_test_case() -> TestCase { - let mut protocol = setup_protocol("validators".to_string().try_into().unwrap()); + let mut protocol = setup_protocol("validators".try_into().unwrap()); let chains = vec![ - "Ethereum".to_string().try_into().unwrap(), - "Polygon".to_string().try_into().unwrap(), + "Ethereum".try_into().unwrap(), + "Polygon".try_into().unwrap(), ]; - let verifiers = vec![ - Verifier { - addr: Addr::unchecked("verifier1"), - supported_chains: chains.clone(), - key_pair: generate_key(0), - }, - Verifier { - addr: Addr::unchecked("verifier2"), - supported_chains: chains.clone(), - key_pair: generate_key(1), - }, - ]; - let min_verifier_bond = Uint128::new(100); + let verifiers = create_new_verifiers_vec( + chains.clone(), + vec![("verifier1".to_string(), 0), ("verifier2".to_string(), 1)], + ); + + let min_verifier_bond = nonempty::Uint128::try_from(100).unwrap(); let unbonding_period_days = 10; register_service(&mut protocol, min_verifier_bond, unbonding_period_days); register_verifiers(&mut protocol, &verifiers, min_verifier_bond); - let chain1 = setup_chain(&mut protocol, chains.first().unwrap().clone()); - let chain2 = setup_chain(&mut protocol, chains.get(1).unwrap().clone()); + let chain1 = setup_chain(&mut protocol, chains.first().unwrap().clone(), &verifiers); + let chain2 = setup_chain(&mut protocol, chains.get(1).unwrap().clone(), &verifiers); TestCase { protocol, chain1, @@ -868,8 +884,81 @@ pub fn setup_test_case() -> TestCase { } pub fn assert_contract_err_strings_equal( - actual: impl Into, - expected: impl Into, + actual: impl Into, + expected: impl Into, ) { assert_eq!(actual.into().to_string(), expected.into().to_string()); } + +pub fn register_in_service_registry( + protocol: &mut Protocol, + verifiers: &Vec, + min_verifier_bond: nonempty::Uint128, +) { + let response = protocol.service_registry.execute( + &mut protocol.app, + protocol.governance_address.clone(), + &ExecuteMsg::AuthorizeVerifiers { + verifiers: verifiers + .iter() + .map(|verifier| verifier.addr.to_string()) + .collect(), + service_name: protocol.service_name.to_string(), + }, + ); + assert!(response.is_ok()); + + for verifier in verifiers { + let response = protocol.app.send_tokens( + protocol.genesis_address.clone(), + verifier.addr.clone(), + &coins(min_verifier_bond.into_inner().u128(), AXL_DENOMINATION), + ); + assert!(response.is_ok()); + + let response = protocol.service_registry.execute_with_funds( + &mut protocol.app, + verifier.addr.clone(), + &ExecuteMsg::BondVerifier { + service_name: protocol.service_name.to_string(), + }, + &coins(min_verifier_bond.into_inner().u128(), AXL_DENOMINATION), + ); + assert!(response.is_ok()); + + let response = protocol.service_registry.execute( + &mut protocol.app, + verifier.addr.clone(), + &ExecuteMsg::RegisterChainSupport { + service_name: protocol.service_name.to_string(), + chains: verifier.supported_chains.clone(), + }, + ); + assert!(response.is_ok()); + } +} + +pub fn submit_pubkeys(protocol: &mut Protocol, verifiers: &Vec) { + for verifier in verifiers { + let address_hash = Keccak256::digest(verifier.addr.as_bytes()); + + let sig = tofn::ecdsa::sign( + verifier.key_pair.signing_key(), + &address_hash.as_slice().try_into().unwrap(), + ) + .unwrap(); + let sig = ecdsa::Signature::from_der(&sig).unwrap(); + + let response = protocol.multisig.execute( + &mut protocol.app, + verifier.addr.clone(), + &multisig::msg::ExecuteMsg::RegisterPublicKey { + public_key: PublicKey::Ecdsa(HexBinary::from( + verifier.key_pair.encoded_verifying_key(), + )), + signed_sender_address: HexBinary::from(sig.to_vec()), + }, + ); + assert!(response.is_ok()); + } +} diff --git a/integration-tests/tests/update_worker_set.rs b/integration-tests/tests/update_worker_set.rs index 002f2421a..3c0323745 100644 --- a/integration-tests/tests/update_worker_set.rs +++ b/integration-tests/tests/update_worker_set.rs @@ -1,19 +1,17 @@ use cosmwasm_std::Addr; use cw_multi_test::Executor; - use integration_tests::contract::Contract; use multisig_prover::msg::ExecuteMsg; -use test_utils::Verifier; - -use crate::test_utils::get_multisig_session_id; +use service_registry::msg::QueryMsg as ServiceRegistryQueryMsg; +use service_registry::WeightedVerifier; pub mod test_utils; #[test] fn verifier_set_can_be_initialized_and_then_manually_updated() { let chains: Vec = vec![ - "Ethereum".to_string().try_into().unwrap(), - "Polygon".to_string().try_into().unwrap(), + "Ethereum".try_into().unwrap(), + "Polygon".try_into().unwrap(), ]; let test_utils::TestCase { @@ -28,19 +26,19 @@ fn verifier_set_can_be_initialized_and_then_manually_updated() { test_utils::verifiers_to_verifier_set(&mut protocol, &initial_verifiers); let verifier_set = - test_utils::get_verifier_set_from_prover(&mut protocol.app, ðereum.multisig_prover); + test_utils::verifier_set_from_prover(&mut protocol.app, ðereum.multisig_prover); assert_eq!(verifier_set, simulated_verifier_set); // add third and fourth verifier let mut new_verifiers = Vec::new(); - let new_verifier = Verifier { + let new_verifier = test_utils::Verifier { addr: Addr::unchecked("verifier3"), supported_chains: chains.clone(), key_pair: test_utils::generate_key(2), }; new_verifiers.push(new_verifier); - let new_verifier = Verifier { + let new_verifier = test_utils::Verifier { addr: Addr::unchecked("verifier4"), supported_chains: chains.clone(), key_pair: test_utils::generate_key(3), @@ -68,7 +66,7 @@ fn verifier_set_can_be_initialized_and_then_manually_updated() { // sign with old verifiers let session_id = test_utils::sign_proof(&mut protocol, &initial_verifiers, response); - let proof = test_utils::get_proof(&mut protocol.app, ðereum.multisig_prover, &session_id); + let proof = test_utils::proof(&mut protocol.app, ðereum.multisig_prover, &session_id); assert!(matches!( proof.status, multisig_prover::msg::ProofStatus::Completed { .. } @@ -102,22 +100,15 @@ fn verifier_set_can_be_initialized_and_then_manually_updated() { ); let new_verifier_set = - test_utils::get_verifier_set_from_prover(&mut protocol.app, ðereum.multisig_prover); + test_utils::verifier_set_from_prover(&mut protocol.app, ðereum.multisig_prover); assert_eq!(new_verifier_set, expected_new_verifier_set); - - let coordinator_verifier_set = test_utils::get_verifier_set_from_coordinator( - &mut protocol.app, - &protocol.coordinator, - ethereum.chain_name, - ); - assert_eq!(coordinator_verifier_set, expected_new_verifier_set); } #[test] fn verifier_set_cannot_be_updated_again_while_pending_verifier_is_not_yet_confirmed() { let chains = vec![ - "Ethereum".to_string().try_into().unwrap(), - "Polygon".to_string().try_into().unwrap(), + "Ethereum".try_into().unwrap(), + "Polygon".try_into().unwrap(), ]; let test_utils::TestCase { mut protocol, @@ -131,7 +122,7 @@ fn verifier_set_cannot_be_updated_again_while_pending_verifier_is_not_yet_confir test_utils::verifiers_to_verifier_set(&mut protocol, &initial_verifiers); let verifier_set = - test_utils::get_verifier_set_from_prover(&mut protocol.app, ðereum.multisig_prover); + test_utils::verifier_set_from_prover(&mut protocol.app, ðereum.multisig_prover); assert_eq!(verifier_set, simulated_verifier_set); @@ -165,7 +156,7 @@ fn verifier_set_cannot_be_updated_again_while_pending_verifier_is_not_yet_confir let session_id = test_utils::sign_proof(&mut protocol, &initial_verifiers, response); - let proof = test_utils::get_proof(&mut protocol.app, ðereum.multisig_prover, &session_id); + let proof = test_utils::proof(&mut protocol.app, ðereum.multisig_prover, &session_id); // proof must be completed assert!(matches!( @@ -211,7 +202,7 @@ fn verifier_set_cannot_be_updated_again_while_pending_verifier_is_not_yet_confir ); let new_verifier_set = - test_utils::get_verifier_set_from_prover(&mut protocol.app, ðereum.multisig_prover); + test_utils::verifier_set_from_prover(&mut protocol.app, ðereum.multisig_prover); assert_eq!(new_verifier_set, expected_new_verifier_set); @@ -236,8 +227,8 @@ fn verifier_set_cannot_be_updated_again_while_pending_verifier_is_not_yet_confir #[test] fn verifier_set_update_can_be_resigned() { let chains = vec![ - "Ethereum".to_string().try_into().unwrap(), - "Polygon".to_string().try_into().unwrap(), + "Ethereum".try_into().unwrap(), + "Polygon".try_into().unwrap(), ]; let test_utils::TestCase { mut protocol, @@ -251,7 +242,7 @@ fn verifier_set_update_can_be_resigned() { test_utils::verifiers_to_verifier_set(&mut protocol, &initial_verifiers); let verifier_set = - test_utils::get_verifier_set_from_prover(&mut protocol.app, ðereum.multisig_prover); + test_utils::verifier_set_from_prover(&mut protocol.app, ðereum.multisig_prover); assert_eq!(verifier_set, simulated_verifier_set); @@ -280,7 +271,7 @@ fn verifier_set_update_can_be_resigned() { ) .unwrap(); - let first_session_id = get_multisig_session_id(response.clone()); + let first_session_id = test_utils::multisig_session_id(response.clone()); // signing didn't occur, trigger signing again let response = protocol @@ -293,7 +284,7 @@ fn verifier_set_update_can_be_resigned() { ) .unwrap(); - let second_session_id = get_multisig_session_id(response.clone()); + let second_session_id = test_utils::multisig_session_id(response.clone()); assert_ne!(first_session_id, second_session_id); test_utils::sign_proof(&mut protocol, &initial_verifiers, response); @@ -309,13 +300,13 @@ fn verifier_set_update_can_be_resigned() { ) .unwrap(); - let third_session_id = get_multisig_session_id(response.clone()); + let third_session_id = test_utils::multisig_session_id(response.clone()); assert_ne!(first_session_id, second_session_id); assert_ne!(second_session_id, third_session_id); test_utils::sign_proof(&mut protocol, &initial_verifiers, response); - let proof = test_utils::get_proof( + let proof = test_utils::proof( &mut protocol.app, ðereum.multisig_prover, &second_session_id, @@ -330,7 +321,7 @@ fn verifier_set_update_can_be_resigned() { #[test] fn governance_should_confirm_new_verifier_set_without_verification() { - let chains: Vec = vec!["Ethereum".to_string().try_into().unwrap()]; + let chains: Vec = vec!["Ethereum".try_into().unwrap()]; let test_utils::TestCase { mut protocol, chain1: ethereum, @@ -341,7 +332,7 @@ fn governance_should_confirm_new_verifier_set_without_verification() { // add third verifier let mut new_verifiers = Vec::new(); - let new_verifier = Verifier { + let new_verifier = test_utils::Verifier { addr: Addr::unchecked("verifier3"), supported_chains: chains.clone(), key_pair: test_utils::generate_key(2), @@ -372,7 +363,76 @@ fn governance_should_confirm_new_verifier_set_without_verification() { ); let new_verifier_set = - test_utils::get_verifier_set_from_prover(&mut protocol.app, ðereum.multisig_prover); + test_utils::verifier_set_from_prover(&mut protocol.app, ðereum.multisig_prover); assert_eq!(new_verifier_set, expected_new_verifier_set); } + +#[test] +fn rotate_signers_should_filter_out_signers_without_pubkey() { + let test_utils::TestCase { + mut protocol, + chain1, + verifiers: initial_verifiers, + min_verifier_bond, + .. + } = test_utils::setup_test_case(); + + let chains: Vec = vec![chain1.chain_name.clone()]; + + // add a third verifier to satisfy min verifier change threshold + test_utils::register_verifiers( + &mut protocol, + &test_utils::create_new_verifiers_vec(chains.clone(), vec![("verifier3".to_string(), 2)]), + min_verifier_bond, + ); + + // add a fourth verifier in service registry but does not submit a pubkey to multisig + test_utils::register_in_service_registry( + &mut protocol, + &test_utils::create_new_verifiers_vec(chains.clone(), vec![("verifier4".to_string(), 3)]), + min_verifier_bond, + ); + + // the fourth verifier should be filtered out in prover because it does not have a pubkey + let expect_new_verifiers = test_utils::create_new_verifiers_vec( + chains.clone(), + vec![ + ("verifier1".to_string(), 0), + ("verifier2".to_string(), 1), + ("verifier3".to_string(), 2), + ], + ); + let expected_verifier_set = + test_utils::verifiers_to_verifier_set(&mut protocol, &expect_new_verifiers); + + // should get initial + 2 active verifiers from service registry + let active_verifiers: Vec = protocol + .service_registry + .query( + &protocol.app, + &ServiceRegistryQueryMsg::ActiveVerifiers { + service_name: protocol.service_name.to_string(), + chain_name: chains[0].clone(), + }, + ) + .unwrap(); + + assert_eq!( + active_verifiers.len(), + initial_verifiers.len().checked_add(2).unwrap() + ); + + // rotate signers + test_utils::rotate_active_verifier_set( + &mut protocol, + chain1.clone(), + &initial_verifiers, + &expect_new_verifiers, + ); + + let verifier_set = + test_utils::verifier_set_from_prover(&mut protocol.app, &chain1.multisig_prover); + + assert_eq!(verifier_set, expected_verifier_set); +} diff --git a/interchain-token-service/Cargo.toml b/interchain-token-service/Cargo.toml new file mode 100644 index 000000000..738c27a49 --- /dev/null +++ b/interchain-token-service/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "interchain-token-service" +version = "0.1.0" +rust-version = { workspace = true } +edition = { workspace = true } + +[dependencies] +alloy-primitives = { workspace = true } +alloy-sol-types = { workspace = true } +axelar-wasm-std = { workspace = true, features = ["derive"] } +cosmwasm-schema = { workspace = true } +cosmwasm-std = { workspace = true } +error-stack = { workspace = true } +report = { workspace = true } +router-api = { workspace = true } +schemars = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +strum = { workspace = true } +thiserror = { workspace = true } + +[dev-dependencies] +goldie = { workspace = true } + +[lints] +workspace = true diff --git a/interchain-token-service/release.toml b/interchain-token-service/release.toml new file mode 100644 index 000000000..954564097 --- /dev/null +++ b/interchain-token-service/release.toml @@ -0,0 +1 @@ +release = false diff --git a/interchain-token-service/src/abi.rs b/interchain-token-service/src/abi.rs new file mode 100644 index 000000000..0c9f6f6dc --- /dev/null +++ b/interchain-token-service/src/abi.rs @@ -0,0 +1,579 @@ +use alloy_primitives::{FixedBytes, U256}; +use alloy_sol_types::{sol, SolValue}; +use axelar_wasm_std::FnExt; +use cosmwasm_std::{HexBinary, Uint256}; +use error_stack::{Report, ResultExt}; +use router_api::ChainName; + +use crate::error::Error; +use crate::primitives::{ItsHubMessage, ItsMessage}; +use crate::{TokenId, TokenManagerType}; + +// ITS Message payload types +// Reference: https://github.com/axelarnetwork/interchain-token-service/blob/v1.2.4/DESIGN.md#interchain-communication-spec +// `abi_encode_params` is used to encode the struct fields as ABI params as required by the spec. +// E.g. `DeployTokenManager::abi_encode_params` encodes as `abi.encode([uint256, bytes32, uint256, bytes], [...])`. +sol! { + enum MessageType { + InterchainTransfer, + DeployInterchainToken, + DeployTokenManager, + SendToHub, + ReceiveFromHub, + } + + struct InterchainTransfer { + uint256 messageType; + bytes32 tokenId; + bytes sourceAddress; + bytes destinationAddress; + uint256 amount; + bytes data; + } + + struct DeployInterchainToken { + uint256 messageType; + bytes32 tokenId; + string name; + string symbol; + uint8 decimals; + bytes minter; + } + + struct DeployTokenManager { + uint256 messageType; + bytes32 tokenId; + uint256 tokenManagerType; + bytes params; + } + + struct SendToHub { + uint256 messageType; + /// True destination chain name when sending a message from ITS edge source contract -> ITS Hub + string destination_chain; + bytes message; + } + + struct ReceiveFromHub { + uint256 messageType; + /// True source chain name when receiving a message from ITS Hub -> ITS edge destination contract + string source_chain; + bytes message; + } +} + +impl ItsMessage { + pub fn abi_encode(self) -> HexBinary { + match self { + ItsMessage::InterchainTransfer { + token_id, + source_address, + destination_address, + amount, + data, + } => InterchainTransfer { + messageType: MessageType::InterchainTransfer.into(), + tokenId: FixedBytes::<32>::new(token_id.into()), + sourceAddress: Vec::::from(source_address).into(), + destinationAddress: Vec::::from(destination_address).into(), + amount: U256::from_le_bytes(amount.to_le_bytes()), + data: Vec::::from(data).into(), + } + .abi_encode_params(), + ItsMessage::DeployInterchainToken { + token_id, + name, + symbol, + decimals, + minter, + } => DeployInterchainToken { + messageType: MessageType::DeployInterchainToken.into(), + tokenId: FixedBytes::<32>::new(token_id.into()), + name, + symbol, + decimals, + minter: Vec::::from(minter).into(), + } + .abi_encode_params(), + ItsMessage::DeployTokenManager { + token_id, + token_manager_type, + params, + } => DeployTokenManager { + messageType: MessageType::DeployTokenManager.into(), + tokenId: FixedBytes::<32>::new(token_id.into()), + tokenManagerType: token_manager_type.into(), + params: Vec::::from(params).into(), + } + .abi_encode_params(), + } + .into() + } + + pub fn abi_decode(payload: &[u8]) -> Result> { + if payload.len() < 32 { + return Err(Report::new(Error::InvalidMessage)); + } + + let message_type = MessageType::abi_decode(&payload[0..32], true) + .change_context(Error::InvalidMessageType)?; + + let message = match message_type { + MessageType::InterchainTransfer => { + let decoded = InterchainTransfer::abi_decode_params(payload, true) + .change_context(Error::InvalidMessage)?; + + Ok(ItsMessage::InterchainTransfer { + token_id: TokenId::new(decoded.tokenId.into()), + source_address: HexBinary::from(decoded.sourceAddress.to_vec()), + destination_address: HexBinary::from(decoded.destinationAddress.as_ref()), + amount: Uint256::from_le_bytes(decoded.amount.to_le_bytes()), + data: HexBinary::from(decoded.data.as_ref()), + }) + } + MessageType::DeployInterchainToken => { + let decoded = DeployInterchainToken::abi_decode_params(payload, true) + .change_context(Error::InvalidMessage)?; + + Ok(ItsMessage::DeployInterchainToken { + token_id: TokenId::new(decoded.tokenId.into()), + name: decoded.name, + symbol: decoded.symbol, + decimals: decoded.decimals, + minter: HexBinary::from(decoded.minter.as_ref()), + }) + } + MessageType::DeployTokenManager => { + let decoded = DeployTokenManager::abi_decode_params(payload, true) + .change_context(Error::InvalidMessage)?; + + let token_manager_type = u8::try_from(decoded.tokenManagerType) + .change_context(Error::InvalidTokenManagerType)? + .then(TokenManagerType::from_repr) + .ok_or_else(|| Report::new(Error::InvalidTokenManagerType))?; + + Ok(ItsMessage::DeployTokenManager { + token_id: TokenId::new(decoded.tokenId.into()), + token_manager_type, + params: HexBinary::from(decoded.params.as_ref()), + }) + } + _ => Err(Report::new(Error::InvalidMessageType)), + }?; + + Ok(message) + } +} + +impl ItsHubMessage { + pub fn abi_encode(self) -> HexBinary { + match self { + ItsHubMessage::SendToHub { + destination_chain, + message, + } => SendToHub { + messageType: MessageType::SendToHub.into(), + destination_chain: destination_chain.into(), + message: Vec::::from(message.abi_encode()).into(), + } + .abi_encode_params() + .into(), + ItsHubMessage::ReceiveFromHub { + source_chain, + message, + } => ReceiveFromHub { + messageType: MessageType::ReceiveFromHub.into(), + source_chain: source_chain.into(), + message: Vec::::from(message.abi_encode()).into(), + } + .abi_encode_params() + .into(), + } + } + + pub fn abi_decode(payload: &[u8]) -> Result> { + if payload.len() < 32 { + return Err(Report::new(Error::InvalidMessage)); + } + + let message_type = MessageType::abi_decode(&payload[0..32], true) + .change_context(Error::InvalidMessageType)?; + + let hub_message = match message_type { + MessageType::SendToHub => { + let decoded = SendToHub::abi_decode_params(payload, true) + .change_context(Error::InvalidMessage)?; + + ItsHubMessage::SendToHub { + destination_chain: ChainName::try_from(decoded.destination_chain) + .change_context(Error::InvalidChainName)?, + message: ItsMessage::abi_decode(&decoded.message)?, + } + } + MessageType::ReceiveFromHub => { + let decoded = ReceiveFromHub::abi_decode_params(payload, true) + .change_context(Error::InvalidMessage)?; + + ItsHubMessage::ReceiveFromHub { + source_chain: ChainName::try_from(decoded.source_chain) + .change_context(Error::InvalidChainName)?, + message: ItsMessage::abi_decode(&decoded.message)?, + } + } + _ => return Err(Report::new(Error::InvalidMessageType)), + }; + + Ok(hub_message) + } +} + +impl From for U256 { + fn from(value: MessageType) -> Self { + U256::from(value as u8) + } +} + +impl From for U256 { + fn from(value: TokenManagerType) -> Self { + U256::from(value as u8) + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use alloy_primitives::{FixedBytes, U256}; + use alloy_sol_types::SolValue; + use cosmwasm_std::{HexBinary, Uint256}; + use router_api::ChainName; + + use crate::abi::{DeployTokenManager, MessageType, SendToHub}; + use crate::error::Error; + use crate::{ItsHubMessage, ItsMessage, TokenManagerType}; + + #[test] + fn interchain_transfer_encode_decode() { + let remote_chain = ChainName::from_str("chain").unwrap(); + + let cases = vec![ + ItsHubMessage::SendToHub { + destination_chain: remote_chain.clone(), + message: ItsMessage::InterchainTransfer { + token_id: [0u8; 32].into(), + source_address: HexBinary::from_hex("").unwrap(), + destination_address: HexBinary::from_hex("").unwrap(), + amount: Uint256::zero(), + data: HexBinary::from_hex("").unwrap(), + }, + }, + ItsHubMessage::SendToHub { + destination_chain: remote_chain.clone(), + message: ItsMessage::InterchainTransfer { + token_id: [255u8; 32].into(), + source_address: HexBinary::from_hex("4F4495243837681061C4743b74B3eEdf548D56A5") + .unwrap(), + destination_address: HexBinary::from_hex( + "4F4495243837681061C4743b74B3eEdf548D56A5", + ) + .unwrap(), + amount: Uint256::MAX, + data: HexBinary::from_hex("abcd").unwrap(), + }, + }, + ItsHubMessage::ReceiveFromHub { + source_chain: remote_chain.clone(), + message: ItsMessage::InterchainTransfer { + token_id: [0u8; 32].into(), + source_address: HexBinary::from_hex("").unwrap(), + destination_address: HexBinary::from_hex("").unwrap(), + amount: Uint256::zero(), + data: HexBinary::from_hex("").unwrap(), + }, + }, + ItsHubMessage::ReceiveFromHub { + source_chain: remote_chain.clone(), + message: ItsMessage::InterchainTransfer { + token_id: [255u8; 32].into(), + source_address: HexBinary::from_hex("4F4495243837681061C4743b74B3eEdf548D56A5") + .unwrap(), + destination_address: HexBinary::from_hex( + "4F4495243837681061C4743b74B3eEdf548D56A5", + ) + .unwrap(), + amount: Uint256::MAX, + data: HexBinary::from_hex("abcd").unwrap(), + }, + }, + ]; + + let encoded: Vec<_> = cases + .iter() + .map(|original| original.clone().abi_encode().to_hex()) + .collect(); + + goldie::assert_json!(encoded); + + for original in cases { + let encoded = original.clone().abi_encode(); + let decoded = ItsHubMessage::abi_decode(&encoded).unwrap(); + assert_eq!(original, decoded); + } + } + + #[test] + fn deploy_interchain_token_encode_decode() { + let remote_chain = ChainName::from_str("chain").unwrap(); + + let cases = vec![ + ItsHubMessage::SendToHub { + destination_chain: remote_chain.clone(), + message: ItsMessage::DeployInterchainToken { + token_id: [0u8; 32].into(), + name: "".into(), + symbol: "".into(), + decimals: 0, + minter: HexBinary::from_hex("").unwrap(), + }, + }, + ItsHubMessage::SendToHub { + destination_chain: remote_chain.clone(), + message: ItsMessage::DeployInterchainToken { + token_id: [1u8; 32].into(), + name: "Test Token".into(), + symbol: "TST".into(), + decimals: 18, + minter: HexBinary::from_hex("1234").unwrap(), + }, + }, + ItsHubMessage::SendToHub { + destination_chain: remote_chain.clone(), + message: ItsMessage::DeployInterchainToken { + token_id: [0u8; 32].into(), + name: "Unicode Token 🪙".into(), + symbol: "UNI🔣".into(), + decimals: 255, + minter: HexBinary::from_hex("abcd").unwrap(), + }, + }, + ItsHubMessage::ReceiveFromHub { + source_chain: remote_chain.clone(), + message: ItsMessage::DeployInterchainToken { + token_id: [0u8; 32].into(), + name: "".into(), + symbol: "".into(), + decimals: 0, + minter: HexBinary::from_hex("").unwrap(), + }, + }, + ItsHubMessage::ReceiveFromHub { + source_chain: remote_chain.clone(), + message: ItsMessage::DeployInterchainToken { + token_id: [1u8; 32].into(), + name: "Test Token".into(), + symbol: "TST".into(), + decimals: 18, + minter: HexBinary::from_hex("1234").unwrap(), + }, + }, + ItsHubMessage::ReceiveFromHub { + source_chain: remote_chain.clone(), + message: ItsMessage::DeployInterchainToken { + token_id: [0u8; 32].into(), + name: "Unicode Token 🪙".into(), + symbol: "UNI🔣".into(), + decimals: 255, + minter: HexBinary::from_hex("abcd").unwrap(), + }, + }, + ]; + + let encoded: Vec<_> = cases + .iter() + .map(|original| original.clone().abi_encode().to_hex()) + .collect(); + + goldie::assert_json!(encoded); + + for original in cases { + let encoded = original.clone().abi_encode(); + let decoded = ItsHubMessage::abi_decode(&encoded).unwrap(); + assert_eq!(original, decoded); + } + } + + #[test] + fn deploy_token_manager_encode_decode() { + let remote_chain = ChainName::from_str("chain").unwrap(); + + let cases = vec![ + ItsHubMessage::SendToHub { + destination_chain: remote_chain.clone(), + message: ItsMessage::DeployTokenManager { + token_id: [0u8; 32].into(), + token_manager_type: TokenManagerType::NativeInterchainToken, + params: HexBinary::default(), + }, + }, + ItsHubMessage::SendToHub { + destination_chain: remote_chain.clone(), + message: ItsMessage::DeployTokenManager { + token_id: [1u8; 32].into(), + token_manager_type: TokenManagerType::Gateway, + params: HexBinary::from_hex("1234").unwrap(), + }, + }, + ItsHubMessage::ReceiveFromHub { + source_chain: remote_chain.clone(), + message: ItsMessage::DeployTokenManager { + token_id: [0u8; 32].into(), + token_manager_type: TokenManagerType::NativeInterchainToken, + params: HexBinary::default(), + }, + }, + ItsHubMessage::ReceiveFromHub { + source_chain: remote_chain.clone(), + message: ItsMessage::DeployTokenManager { + token_id: [1u8; 32].into(), + token_manager_type: TokenManagerType::Gateway, + params: HexBinary::from_hex("1234").unwrap(), + }, + }, + ]; + + let encoded: Vec<_> = cases + .iter() + .map(|original| original.clone().abi_encode().to_hex()) + .collect(); + + goldie::assert_json!(encoded); + + for original in cases { + let encoded = original.clone().abi_encode(); + let decoded = ItsHubMessage::abi_decode(&encoded).unwrap(); + assert_eq!(original, decoded); + } + } + + #[test] + fn invalid_its_hub_message_type() { + let invalid_payload = SendToHub { + messageType: U256::from(MessageType::ReceiveFromHub as u8 + 1), + destination_chain: "remote-chain".into(), + message: vec![].into(), + } + .abi_encode_params(); + + let result = ItsHubMessage::abi_decode(&invalid_payload); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().current_context().to_string(), + Error::InvalidMessageType.to_string() + ); + } + + #[test] + fn invalid_its_message_type() { + let mut message = MessageType::DeployTokenManager.abi_encode(); + message[31] = 3; + + let invalid_payload = SendToHub { + messageType: MessageType::SendToHub.into(), + destination_chain: "remote-chain".into(), + message: message.into(), + } + .abi_encode_params(); + + let result = ItsHubMessage::abi_decode(&invalid_payload); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().current_context().to_string(), + Error::InvalidMessageType.to_string() + ); + } + + #[test] + fn invalid_destination_chain() { + let message = DeployTokenManager { + messageType: MessageType::DeployTokenManager.into(), + tokenId: FixedBytes::<32>::new([0u8; 32]), + tokenManagerType: TokenManagerType::NativeInterchainToken.into(), + params: vec![].into(), + }; + + let payload = SendToHub { + messageType: MessageType::SendToHub.into(), + destination_chain: "".into(), + message: message.abi_encode_params().into(), + } + .abi_encode_params(); + + let result = ItsHubMessage::abi_decode(&payload); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().current_context().to_string(), + Error::InvalidChainName.to_string() + ); + } + + #[test] + fn invalid_token_manager_type() { + let message = DeployTokenManager { + messageType: MessageType::DeployTokenManager.into(), + tokenId: FixedBytes::<32>::new([0u8; 32]), + tokenManagerType: U256::from(TokenManagerType::Gateway as u8 + 1), + params: vec![].into(), + }; + + let payload = SendToHub { + messageType: MessageType::SendToHub.into(), + destination_chain: "chain".into(), + message: message.abi_encode_params().into(), + } + .abi_encode_params(); + + let result = ItsHubMessage::abi_decode(&payload); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().current_context().to_string(), + Error::InvalidTokenManagerType.to_string() + ); + } + + #[test] + fn encode_decode_large_data() { + let large_data = vec![0u8; 1024 * 1024]; // 1MB of data + let original = ItsHubMessage::SendToHub { + destination_chain: ChainName::from_str("large-data-chain").unwrap(), + message: ItsMessage::InterchainTransfer { + token_id: [0u8; 32].into(), + source_address: HexBinary::from_hex("1234").unwrap(), + destination_address: HexBinary::from_hex("5678").unwrap(), + amount: Uint256::from(1u128), + data: HexBinary::from(large_data), + }, + }; + + let encoded = original.clone().abi_encode(); + let decoded = ItsHubMessage::abi_decode(&encoded).unwrap(); + assert_eq!(original, decoded); + } + + #[test] + fn encode_decode_unicode_strings() { + let original = ItsHubMessage::SendToHub { + destination_chain: ChainName::from_str("chain").unwrap(), + message: ItsMessage::DeployInterchainToken { + token_id: [0u8; 32].into(), + name: "Unicode Token 🪙".into(), + symbol: "UNI🔣".into(), + decimals: 18, + minter: HexBinary::from_hex("abcd").unwrap(), + }, + }; + + let encoded = original.clone().abi_encode(); + let decoded = ItsHubMessage::abi_decode(&encoded).unwrap(); + assert_eq!(original, decoded); + } +} diff --git a/interchain-token-service/src/error.rs b/interchain-token-service/src/error.rs new file mode 100644 index 000000000..e7f8be5c5 --- /dev/null +++ b/interchain-token-service/src/error.rs @@ -0,0 +1,14 @@ +use axelar_wasm_std::IntoContractError; +use thiserror::Error; + +#[derive(Error, Debug, PartialEq, IntoContractError)] +pub enum Error { + #[error("failed to decode ITS message")] + InvalidMessage, + #[error("invalid message type")] + InvalidMessageType, + #[error("invalid chain name")] + InvalidChainName, + #[error("invalid token manager type")] + InvalidTokenManagerType, +} diff --git a/interchain-token-service/src/lib.rs b/interchain-token-service/src/lib.rs new file mode 100644 index 000000000..d4f5ac941 --- /dev/null +++ b/interchain-token-service/src/lib.rs @@ -0,0 +1,5 @@ +mod primitives; + +pub mod error; +pub use primitives::*; +pub mod abi; diff --git a/interchain-token-service/src/primitives.rs b/interchain-token-service/src/primitives.rs new file mode 100644 index 000000000..4970d294e --- /dev/null +++ b/interchain-token-service/src/primitives.rs @@ -0,0 +1,89 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{HexBinary, Uint256}; +use router_api::ChainName; +use strum::FromRepr; + +#[cw_serde] +#[derive(Eq)] +pub struct TokenId( + #[serde(with = "axelar_wasm_std::hex")] + #[schemars(with = "String")] + [u8; 32], +); + +#[cw_serde] +#[derive(Eq, Copy, FromRepr)] +#[repr(u8)] +pub enum TokenManagerType { + NativeInterchainToken, + MintBurnFrom, + LockUnlock, + LockUnlockFee, + MintBurn, + Gateway, +} + +/// ITS message type that can be sent between ITS contracts for transfers/token deployments +/// `ItsMessage` that are routed via the ITS hub get wrapped inside `ItsHubMessage` +#[cw_serde] +#[derive(Eq)] +pub enum ItsMessage { + InterchainTransfer { + token_id: TokenId, + source_address: HexBinary, + destination_address: HexBinary, + amount: Uint256, + data: HexBinary, + }, + DeployInterchainToken { + token_id: TokenId, + name: String, + symbol: String, + decimals: u8, + minter: HexBinary, + }, + DeployTokenManager { + token_id: TokenId, + token_manager_type: TokenManagerType, + params: HexBinary, + }, +} + +/// ITS message type that can be sent between ITS edge contracts and the ITS Hub +#[cw_serde] +#[derive(Eq)] +pub enum ItsHubMessage { + /// ITS edge source contract -> ITS Hub + SendToHub { + /// True destination chain of the ITS message + destination_chain: ChainName, + message: ItsMessage, + }, + /// ITS Hub -> ITS edge destination contract + ReceiveFromHub { + /// True source chain of the ITS message + source_chain: ChainName, + message: ItsMessage, + }, +} + +impl TokenId { + #[inline(always)] + pub fn new(id: [u8; 32]) -> Self { + id.into() + } +} + +impl From<[u8; 32]> for TokenId { + #[inline(always)] + fn from(id: [u8; 32]) -> Self { + Self(id) + } +} + +impl From for [u8; 32] { + #[inline(always)] + fn from(id: TokenId) -> Self { + id.0 + } +} diff --git a/interchain-token-service/src/testdata/deploy_interchain_token_encode_decode.golden b/interchain-token-service/src/testdata/deploy_interchain_token_encode_decode.golden new file mode 100644 index 000000000..e642fc6d3 --- /dev/null +++ b/interchain-token-service/src/testdata/deploy_interchain_token_encode_decode.golden @@ -0,0 +1,8 @@ +[ + "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000001010101010101010101010101010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000a5465737420546f6b656e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003545354000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021234000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000ff00000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000012556e69636f646520546f6b656e20f09faa9900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007554e49f09f94a3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002abcd000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000001010101010101010101010101010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000a5465737420546f6b656e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003545354000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021234000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000ff00000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000012556e69636f646520546f6b656e20f09faa9900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007554e49f09f94a3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002abcd000000000000000000000000000000000000000000000000000000000000" +] \ No newline at end of file diff --git a/interchain-token-service/src/testdata/deploy_token_manager_encode_decode.golden b/interchain-token-service/src/testdata/deploy_token_manager_encode_decode.golden new file mode 100644 index 000000000..9bd957c4a --- /dev/null +++ b/interchain-token-service/src/testdata/deploy_token_manager_encode_decode.golden @@ -0,0 +1,6 @@ +[ + "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000201010101010101010101010101010101010101010101010101010101010101010000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000021234000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000201010101010101010101010101010101010101010101010101010101010101010000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000021234000000000000000000000000000000000000000000000000000000000000" +] \ No newline at end of file diff --git a/interchain-token-service/src/testdata/interchain_transfer_encode_decode.golden b/interchain-token-service/src/testdata/interchain_transfer_encode_decode.golden new file mode 100644 index 000000000..eb9766e8a --- /dev/null +++ b/interchain-token-service/src/testdata/interchain_transfer_encode_decode.golden @@ -0,0 +1,6 @@ +[ + "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000144f4495243837681061c4743b74b3eedf548d56a500000000000000000000000000000000000000000000000000000000000000000000000000000000000000144f4495243837681061c4743b74b3eedf548d56a50000000000000000000000000000000000000000000000000000000000000000000000000000000000000002abcd000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000144f4495243837681061c4743b74b3eedf548d56a500000000000000000000000000000000000000000000000000000000000000000000000000000000000000144f4495243837681061c4743b74b3eedf548d56a50000000000000000000000000000000000000000000000000000000000000000000000000000000000000002abcd000000000000000000000000000000000000000000000000000000000000" +] \ No newline at end of file diff --git a/packages/axelar-wasm-std-derive/Cargo.toml b/packages/axelar-wasm-std-derive/Cargo.toml index 1639c75e7..e3dee2952 100644 --- a/packages/axelar-wasm-std-derive/Cargo.toml +++ b/packages/axelar-wasm-std-derive/Cargo.toml @@ -1,20 +1,22 @@ [package] name = "axelar-wasm-std-derive" -version = "0.1.0" +version = "1.0.0" rust-version = { workspace = true } -edition = "2021" +edition = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] proc-macro = true [dependencies] -axelar-wasm-std = { workspace = true } error-stack = { workspace = true } quote = "1.0.33" report = { workspace = true } syn = "2.0.29" thiserror = { workspace = true } +[dev-dependencies] +axelar-wasm-std = { workspace = true } + [lints] workspace = true diff --git a/packages/axelar-wasm-std-derive/src/lib.rs b/packages/axelar-wasm-std-derive/src/lib.rs index d1fb6f92a..4ebac5c0f 100644 --- a/packages/axelar-wasm-std-derive/src/lib.rs +++ b/packages/axelar-wasm-std-derive/src/lib.rs @@ -9,14 +9,11 @@ pub fn into_contract_error_derive(input: TokenStream) -> TokenStream { let name = &ast.ident; let gen = quote! { - use axelar_wasm_std::ContractError as _ContractError; - - impl From<#name> for _ContractError { + impl From<#name> for axelar_wasm_std::error::ContractError { fn from(error: #name) -> Self { - use report::LoggableError; use error_stack::report; - LoggableError::from(&report!(error)).into() + report!(error).into() } } }; diff --git a/packages/axelar-wasm-std-derive/tests/derive.rs b/packages/axelar-wasm-std-derive/tests/derive.rs index 5c04d3827..e5e17f8f6 100644 --- a/packages/axelar-wasm-std-derive/tests/derive.rs +++ b/packages/axelar-wasm-std-derive/tests/derive.rs @@ -1,5 +1,5 @@ -use axelar_wasm_std::ContractError; -use axelar_wasm_std_derive::IntoContractError; +use axelar_wasm_std::error::ContractError; +use axelar_wasm_std::IntoContractError; use thiserror::Error; #[derive(Error, Debug, IntoContractError)] diff --git a/packages/axelar-wasm-std/Cargo.toml b/packages/axelar-wasm-std/Cargo.toml index 362659699..05c32e53a 100644 --- a/packages/axelar-wasm-std/Cargo.toml +++ b/packages/axelar-wasm-std/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "axelar-wasm-std" -version = "0.1.0" +version = "1.0.0" rust-version = { workspace = true } -edition = "2021" +edition = { workspace = true } description = "Axelar cosmwasm standard library crate" exclude = [ @@ -18,23 +18,27 @@ crate-type = ["rlib"] [features] # for more explicit tests, cargo test --features=backtraces backtraces = ["cosmwasm-std/backtraces"] -# use library feature to disable all instantiate/execute/query exports -library = [] +derive = ["dep:axelar-wasm-std-derive"] [package.metadata.scripts] optimize = """docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/rust-optimizer:0.12.6 + cosmwasm/optimizer:0.16.0 """ [dependencies] +alloy-primitives = { workspace = true } +axelar-wasm-std-derive = { workspace = true, optional = true } bs58 = "0.5.1" cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } cw-storage-plus = { workspace = true } +cw2 = { workspace = true } error-stack = { workspace = true } flagset = { version = "0.4.3", features = ["serde"] } +into-inner-derive = { workspace = true } +itertools = { workspace = true } lazy_static = "1.4.0" num-traits = { workspace = true } regex = { version = "1.10.0", default-features = false, features = ["perf", "std"] } @@ -43,7 +47,8 @@ schemars = "0.8.10" serde = { version = "1.0.145", default-features = false, features = ["derive"] } serde_json = "1.0.89" sha3 = { workspace = true } -strum = { version = "0.25", default-features = false, features = ["derive"] } +strum = { workspace = true } +sui-types = { workspace = true } thiserror = { workspace = true } valuable = { version = "0.1.0", features = ["derive"] } diff --git a/packages/axelar-wasm-std/src/address.rs b/packages/axelar-wasm-std/src/address.rs new file mode 100644 index 000000000..69162ca30 --- /dev/null +++ b/packages/axelar-wasm-std/src/address.rs @@ -0,0 +1,107 @@ +use std::str::FromStr; + +use alloy_primitives::Address; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, Api}; +use error_stack::{Result, ResultExt}; +use sui_types::SuiAddress; + +#[derive(thiserror::Error)] +#[cw_serde] +pub enum Error { + #[error("invalid address '{0}'")] + InvalidAddress(String), +} + +#[cw_serde] +pub enum AddressFormat { + Eip55, + Sui, +} + +pub fn validate_address(address: &str, format: &AddressFormat) -> Result<(), Error> { + match format { + AddressFormat::Eip55 => { + Address::parse_checksummed(address, None) + .change_context(Error::InvalidAddress(address.to_string()))?; + } + AddressFormat::Sui => { + SuiAddress::from_str(address) + .change_context(Error::InvalidAddress(address.to_string()))?; + } + } + + Ok(()) +} + +pub fn validate_cosmwasm_address(api: &dyn Api, addr: &str) -> Result { + api.addr_validate(addr) + .change_context(Error::InvalidAddress(addr.to_string())) +} + +#[cfg(test)] +mod tests { + use cosmwasm_std::testing::MockApi; + + use crate::{address, err_contains}; + + #[test] + fn validate_eip55_address() { + let addr = "0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5"; + + assert!(address::validate_address(addr, &address::AddressFormat::Eip55).is_ok()); + + let without_prefix = addr.strip_prefix("0x").unwrap(); + assert!(address::validate_address(without_prefix, &address::AddressFormat::Eip55).is_err()); + + let lower_case = addr.to_lowercase(); + assert!(address::validate_address(&lower_case, &address::AddressFormat::Eip55).is_err()); + + let upper_case = addr.to_uppercase(); + assert!(address::validate_address(&upper_case, &address::AddressFormat::Eip55).is_err()); + } + + #[test] + fn validate_sui_address() { + let addr = "0x8cc8d18733a4bf98de8f861d356e2191918733e3afff29f327a01b5ba2997a4d"; + + assert!(address::validate_address(addr, &address::AddressFormat::Sui).is_ok()); + + let without_prefix = addr.strip_prefix("0x").unwrap(); + assert!(address::validate_address(without_prefix, &address::AddressFormat::Sui).is_err()); + + let upper_case = addr.to_uppercase(); + assert!(address::validate_address(&upper_case, &address::AddressFormat::Sui).is_err()); + + let mixed_case = addr + .chars() + .enumerate() + .map(|(i, c)| { + if i % 2 == 0 { + c.to_uppercase().next().unwrap() + } else { + c + } + .to_string() + }) + .collect::(); + assert!(address::validate_address(&mixed_case, &address::AddressFormat::Sui).is_err()); + + let invalid_length = format!("{}5f", addr); + assert!(address::validate_address(&invalid_length, &address::AddressFormat::Sui).is_err()); + } + + #[test] + fn validate_cosmwasm_address() { + let api = MockApi::default(); + let addr = "axelar1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v"; + assert!(address::validate_cosmwasm_address(&api, addr).is_ok()); + + let upper_case = addr.to_uppercase(); + assert!(err_contains!( + address::validate_cosmwasm_address(&api, &upper_case).unwrap_err(), + address::Error, + address::Error::InvalidAddress(..) + )); + } +} diff --git a/packages/axelar-wasm-std/src/counter.rs b/packages/axelar-wasm-std/src/counter.rs index 05a709663..90ff709e8 100644 --- a/packages/axelar-wasm-std/src/counter.rs +++ b/packages/axelar-wasm-std/src/counter.rs @@ -38,7 +38,7 @@ mod tests { use super::*; #[test] - fn test_get_and_incr() { + fn test_cur_and_incr() { let mut store = MockStorage::new(); let counter: Counter = Counter::new("counter"); diff --git a/packages/axelar-wasm-std/src/error.rs b/packages/axelar-wasm-std/src/error.rs index 88a9b19db..1e2922591 100644 --- a/packages/axelar-wasm-std/src/error.rs +++ b/packages/axelar-wasm-std/src/error.rs @@ -1,19 +1,50 @@ +use std::fmt::{Display, Formatter}; + use cosmwasm_std::StdError; -use error_stack::{Context, Report}; +use error_stack::{report, Context, Report}; use report::LoggableError; use thiserror::Error; +use crate::permission_control; + /// This error is supposed to be the top-level error type our contracts return to the cosmwasm module. /// Ideally, we would like to return an error-stack [Report] directly, /// but it won't show all necessary information (namely attachments) in the error message, and many places also return an [StdError]. /// To this end, reports get converted into [LoggableError] and this [ContractError] type unifies [LoggableError] and [StdError], /// so we can return both to cosmwasm. -#[derive(Error, Debug, PartialEq)] -pub enum ContractError { - #[error(transparent)] - Std(#[from] StdError), - #[error(transparent)] - Structured(#[from] LoggableError), +#[derive(Error, Debug)] +pub struct ContractError { + pub report: Report, +} + +#[derive(Error, Debug)] +pub enum Error { + #[error("")] + Report, +} + +impl From for ContractError { + fn from(err: StdError) -> Self { + ContractError { + report: report!(err).change_context(Error::Report), + } + } +} + +impl From for ContractError { + fn from(err: cw2::VersionError) -> Self { + ContractError { + report: report!(err).change_context(Error::Report), + } + } +} + +impl From for ContractError { + fn from(err: permission_control::Error) -> Self { + ContractError { + report: report!(err).change_context(Error::Report), + } + } } impl From> for ContractError @@ -21,7 +52,15 @@ where T: Context, { fn from(report: Report) -> Self { - ContractError::Structured(LoggableError::from(&report)) + ContractError { + report: report.change_context(Error::Report), + } + } +} + +impl Display for ContractError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + LoggableError::from(&self.report).fmt(f) } } @@ -37,3 +76,19 @@ pub fn extend_err( Err(added_error) } } + +#[macro_export] +macro_rules! err_contains { + ($expression:expr, $error_type:ty, $pattern:pat $(if $guard:expr)? $(,)?) => { + match $expression.downcast_ref::<$error_type>() { + Some($pattern) $(if $guard)? => true, + _ => { + println!("actual: {:?}", $expression); + + false + } + } + }; +} + +pub use err_contains; diff --git a/packages/axelar-wasm-std/src/flagset.rs b/packages/axelar-wasm-std/src/flagset.rs index 8402862f0..78280ef89 100644 --- a/packages/axelar-wasm-std/src/flagset.rs +++ b/packages/axelar-wasm-std/src/flagset.rs @@ -1,6 +1,8 @@ use std::ops::{Deref, DerefMut}; -use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema}; +use schemars::gen::SchemaGenerator; +use schemars::schema::Schema; +use schemars::JsonSchema; use serde::{Deserialize, Deserializer, Serialize}; #[derive(Serialize, Clone, Debug, PartialEq, Eq)] diff --git a/packages/axelar-wasm-std/src/killswitch.rs b/packages/axelar-wasm-std/src/killswitch.rs new file mode 100644 index 000000000..b2342d5a8 --- /dev/null +++ b/packages/axelar-wasm-std/src/killswitch.rs @@ -0,0 +1,218 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Event, Response, StdError, StdResult, Storage}; +use cw_storage_plus::Item; + +/// This is a generic module to be used as a "killswitch" for any contract. +/// The killswitch can be set to "engaged" or "disengaged". The contract +/// can then call `is_contract_active`, which will return true if the killswitch +/// is disengaged. `init` should be called at contract instantiation to set +/// the initial state of the killswitch. + +#[cw_serde] +pub enum State { + Engaged, + Disengaged, +} + +/// Sets the initial state of the killswitch. Should be called during contract instantiation +pub fn init(storage: &mut dyn Storage, initial_state: State) -> StdResult<()> { + STATE.save(storage, &initial_state) +} + +/// Sets the killswitch state to `Engaged`. If the state was previously `Disengaged`, +/// adds the on_state_changed event to the response. Returns an error if the killswitch +/// was not initialized via `init` +pub fn engage(storage: &mut dyn Storage, on_state_change: impl Into) -> StdResult { + let state = STATE.update(storage, |state| match state { + State::Disengaged => Ok(State::Engaged), + State::Engaged => Err(KillSwitchUpdateError::SameState), + }); + + killswitch_update_response(state, on_state_change) +} + +/// Sets the killswitch state to `Disengaged`. If the state was previously `Engaged`, +/// adds the on_state_changed event to the response. Returns an error if the killswitch +/// was not initialized via `init` +pub fn disengage( + storage: &mut dyn Storage, + on_state_change: impl Into, +) -> StdResult { + let state = STATE.update(storage, |state| match state { + State::Engaged => Ok(State::Disengaged), + State::Disengaged => Err(KillSwitchUpdateError::SameState), + }); + + killswitch_update_response(state, on_state_change) +} + +/// Returns true if the killswitch state is `Disengaged`. Otherwise returns false. +/// Returns false if the killswitch was not initialized +pub fn is_contract_active(storage: &dyn Storage) -> bool { + STATE.load(storage).unwrap_or(State::Engaged) == State::Disengaged +} + +#[derive(thiserror::Error, Debug)] +enum KillSwitchUpdateError { + #[error("killswitch is already in the same state")] + SameState, + #[error(transparent)] + Std(#[from] StdError), +} + +fn killswitch_update_response( + state: Result, + on_state_change: impl Into, +) -> StdResult { + match state { + Ok(_) => Ok(Response::new().add_event(on_state_change.into())), + Err(KillSwitchUpdateError::SameState) => Ok(Response::new()), + Err(KillSwitchUpdateError::Std(err)) => Err(err), + } +} + +const STATE: Item = Item::new("state"); + +#[cfg(test)] +mod tests { + use cosmwasm_std::testing::mock_dependencies; + use cosmwasm_std::Event; + + use crate::killswitch::{disengage, engage, init, is_contract_active, State, STATE}; + + enum Events { + Engaged, + Disengaged, + } + + impl From for Event { + fn from(val: Events) -> Event { + match val { + Events::Engaged => Event::new("engaged"), + Events::Disengaged => Event::new("disengaged"), + } + } + } + + #[test] + fn init_should_be_able_to_set_state_to_engaged() { + let mut deps = mock_dependencies(); + assert!(STATE.may_load(&deps.storage).unwrap().is_none()); + + init(deps.as_mut().storage, State::Engaged).unwrap(); + + assert_eq!(STATE.load(&deps.storage).unwrap(), State::Engaged); + assert!(!is_contract_active(&deps.storage)); + } + + #[test] + fn init_should_be_able_to_set_state_to_disengaged() { + let mut deps = mock_dependencies(); + assert!(STATE.may_load(&deps.storage).unwrap().is_none()); + + init(deps.as_mut().storage, State::Disengaged).unwrap(); + + assert_eq!(STATE.load(&deps.storage).unwrap(), State::Disengaged); + assert!(is_contract_active(&deps.storage)); + } + + #[test] + fn is_contract_active_should_return_true_when_disengaged() { + let mut deps = mock_dependencies(); + + assert!(!is_contract_active(&deps.storage)); + + STATE.save(deps.as_mut().storage, &State::Engaged).unwrap(); + + assert!(!is_contract_active(&deps.storage)); + + STATE + .save(deps.as_mut().storage, &State::Disengaged) + .unwrap(); + + assert!(is_contract_active(&deps.storage)); + } + + #[test] + fn engage_should_error_when_unset() { + let mut deps = mock_dependencies(); + + assert!(engage(deps.as_mut().storage, Events::Engaged).is_err()); + } + + #[test] + fn engage_should_correctly_set_state_when_disengaged() { + let mut deps = mock_dependencies(); + + STATE + .save(deps.as_mut().storage, &State::Disengaged) + .unwrap(); + engage(deps.as_mut().storage, Events::Engaged).unwrap(); + assert_eq!(STATE.load(&deps.storage).unwrap(), State::Engaged); + assert!(!is_contract_active(&deps.storage)); + } + + #[test] + fn engage_should_correctly_set_state_when_engaged() { + let mut deps = mock_dependencies(); + + STATE.save(deps.as_mut().storage, &State::Engaged).unwrap(); + engage(deps.as_mut().storage, Events::Engaged).unwrap(); + assert_eq!(STATE.load(&deps.storage).unwrap(), State::Engaged); + assert!(!is_contract_active(&deps.storage)); + } + + #[test] + fn disengage_should_error_when_unset() { + let mut deps = mock_dependencies(); + + assert!(disengage(deps.as_mut().storage, Events::Disengaged).is_err()); + } + + #[test] + fn disengage_should_correctly_set_state_when_disengaged() { + let mut deps = mock_dependencies(); + + STATE + .save(deps.as_mut().storage, &State::Disengaged) + .unwrap(); + disengage(deps.as_mut().storage, Events::Disengaged).unwrap(); + assert_eq!(STATE.load(&deps.storage).unwrap(), State::Disengaged); + assert!(is_contract_active(&deps.storage)); + } + + #[test] + fn disengage_should_correctly_set_state_when_engaged() { + let mut deps = mock_dependencies(); + + STATE.save(deps.as_mut().storage, &State::Engaged).unwrap(); + disengage(deps.as_mut().storage, Events::Engaged).unwrap(); + assert_eq!(STATE.load(&deps.storage).unwrap(), State::Disengaged); + assert!(is_contract_active(&deps.storage)); + } + + #[test] + fn engage_and_disengage_should_emit_event_when_state_changes() { + let mut deps = mock_dependencies(); + + init(deps.as_mut().storage, State::Disengaged).unwrap(); + + let engaged_event: Event = Events::Engaged.into(); + let disengaged_event: Event = Events::Disengaged.into(); + + let res = engage(deps.as_mut().storage, Events::Engaged).unwrap(); + assert!(res.events.into_iter().any(|event| event == engaged_event)); + + let res = engage(deps.as_mut().storage, Events::Engaged).unwrap(); + assert_eq!(res.events.len(), 0); + + let res = disengage(deps.as_mut().storage, Events::Disengaged).unwrap(); + assert!(res + .events + .into_iter() + .any(|event| event == disengaged_event)); + + let res = disengage(deps.as_mut().storage, Events::Disengaged).unwrap(); + assert_eq!(res.events.len(), 0); + } +} diff --git a/packages/axelar-wasm-std/src/lib.rs b/packages/axelar-wasm-std/src/lib.rs index ea0756e45..43c1f0c77 100644 --- a/packages/axelar-wasm-std/src/lib.rs +++ b/packages/axelar-wasm-std/src/lib.rs @@ -1,21 +1,25 @@ -pub use crate::{ - error::ContractError, - fn_ext::FnExt, - snapshot::{Participant, Snapshot}, - threshold::{MajorityThreshold, Threshold}, - verification::VerificationStatus, -}; +pub use crate::fn_ext::FnExt; +pub use crate::snapshot::{Participant, Snapshot}; +pub use crate::threshold::{MajorityThreshold, Threshold}; +pub use crate::verification::VerificationStatus; +pub mod address; pub mod counter; pub mod error; pub mod flagset; mod fn_ext; pub mod hash; pub mod hex; +pub mod killswitch; pub mod msg_id; pub mod nonempty; +pub mod permission_control; pub mod snapshot; pub mod threshold; pub mod utils; +pub mod vec; pub mod verification; pub mod voting; + +#[cfg(feature = "derive")] +pub use axelar_wasm_std_derive::*; diff --git a/packages/axelar-wasm-std/src/msg_id/base_58_event_index.rs b/packages/axelar-wasm-std/src/msg_id/base_58_event_index.rs index ea6debb0d..1c75542b2 100644 --- a/packages/axelar-wasm-std/src/msg_id/base_58_event_index.rs +++ b/packages/axelar-wasm-std/src/msg_id/base_58_event_index.rs @@ -1,12 +1,14 @@ use core::fmt; -use std::{fmt::Display, str::FromStr}; +use std::fmt::Display; +use std::str::FromStr; use error_stack::{Report, ResultExt}; use lazy_static::lazy_static; use regex::Regex; use super::Error; -use crate::{hash::Hash, nonempty}; +use crate::hash::Hash; +use crate::nonempty; pub struct Base58TxDigestAndEventIndex { pub tx_digest: Hash, diff --git a/packages/axelar-wasm-std/src/msg_id/base_58_solana_event_index.rs b/packages/axelar-wasm-std/src/msg_id/base_58_solana_event_index.rs new file mode 100644 index 000000000..1a625f910 --- /dev/null +++ b/packages/axelar-wasm-std/src/msg_id/base_58_solana_event_index.rs @@ -0,0 +1,362 @@ +use core::fmt; +use std::fmt::Display; +use std::str::FromStr; + +use error_stack::{Report, ResultExt}; +use lazy_static::lazy_static; +use regex::Regex; + +use super::Error; +use crate::nonempty; + +type RawSignature = [u8; 64]; + +pub struct Base58SolanaTxSignatureAndEventIndex { + // Base58 decoded bytes of the Solana signature. + pub raw_signature: RawSignature, + pub event_index: u32, +} + +impl Base58SolanaTxSignatureAndEventIndex { + pub fn signature_as_base58(&self) -> nonempty::String { + bs58::encode(self.raw_signature) + .into_string() + .try_into() + .expect("failed to convert tx hash to non-empty string") + } + + pub fn new(tx_id: impl Into, event_index: impl Into) -> Self { + Self { + raw_signature: tx_id.into(), + event_index: event_index.into(), + } + } +} + +fn decode_b58_signature(signature: &str) -> Result> { + Ok(bs58::decode(signature) + .into_vec() + .change_context(Error::InvalidTxDigest(signature.to_string()))? + .as_slice() + .try_into() + .map_err(|_| Error::InvalidTxDigest(signature.to_owned()))?) +} + +const PATTERN: &str = "^([1-9A-HJ-NP-Za-km-z]{64,88})-(0|[1-9][0-9]*)$"; +lazy_static! { + static ref REGEX: Regex = Regex::new(PATTERN).expect("invalid regex"); +} + +impl FromStr for Base58SolanaTxSignatureAndEventIndex { + type Err = Report; + + fn from_str(message_id: &str) -> Result + where + Self: Sized, + { + // the PATTERN has exactly two capture groups, so the groups can be extracted safely + let (_, [signature, event_index]) = REGEX + .captures(message_id) + .ok_or(Error::InvalidMessageID { + id: message_id.to_string(), + expected_format: PATTERN.to_string(), + })? + .extract(); + + Ok(Base58SolanaTxSignatureAndEventIndex { + raw_signature: decode_b58_signature(signature)?, + event_index: event_index + .parse() + .map_err(|_| Error::EventIndexOverflow(message_id.to_string()))?, + }) + } +} + +impl Display for Base58SolanaTxSignatureAndEventIndex { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}-{}", + bs58::encode(self.raw_signature).into_string(), + self.event_index + ) + } +} + +#[cfg(test)] +mod tests { + + use hex::ToHex; + + use super::*; + + fn random_bytes() -> RawSignature { + let mut bytes = [0; 64]; + for b in &mut bytes { + *b = rand::random(); + } + bytes + } + + fn random_tx_digest() -> String { + bs58::encode(random_bytes()).into_string() + } + + fn random_event_index() -> u32 { + rand::random() + } + + #[test] + fn should_parse_msg_id() { + let res = Base58SolanaTxSignatureAndEventIndex::from_str( + "4hHzKKdpXH2QMB5Jm11YR48cLqUJb9Cwq2YL3tveVTPeFkZaLP8cdcH5UphVPJ7kYwCUCRLnywd3xkUhb4ZYWtf5-0", + ); + assert!(res.is_ok()); + + for _ in 0..1000 { + let tx_digest = random_tx_digest(); + let event_index = random_event_index(); + let msg_id = format!("{}-{}", tx_digest, event_index); + + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&msg_id); + let parsed = res.unwrap(); + assert_eq!(parsed.event_index, event_index); + assert_eq!(parsed.signature_as_base58(), tx_digest.try_into().unwrap()); + assert_eq!(parsed.to_string(), msg_id); + } + } + + #[test] + fn should_not_parse_msg_id_with_wrong_length_base58_tx_digest() { + let tx_digest = random_tx_digest(); + let event_index = random_event_index(); + + // too long + let msg_id = format!("{}{}-{}", tx_digest, tx_digest, event_index); + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&msg_id); + assert!(res.is_err()); + + // too short + let msg_id = format!("{}-{}", &tx_digest[0..63], event_index); + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&msg_id); + assert!(res.is_err()); + } + + #[test] + fn leading_ones_should_not_be_ignored() { + let tx_digest = random_tx_digest(); + let event_index = random_event_index(); + + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&format!( + "1{}-{}", + tx_digest, event_index + )); + assert!(res.is_err()); + + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&format!( + "11{}-{}", + tx_digest, event_index + )); + assert!(res.is_err()); + } + + #[test] + fn should_not_parse_msg_id_with_correct_length_base58_but_wrong_length_hex() { + // this is 88 chars and valid base58, but will decode to 66 bytes + // the leading 1s are encoded as 00 in hex and thus result in too many bytes + let tx_digest = "1111KKdpXH2QMB5Jm11YR48cLqUJb9Cwq2YL3tveVTPeFkZaLP8cdcH5UphVPJ7kYwCUCRLnywd3xkUhb4ZYWtf5"; + let event_index = random_event_index(); + let msg_id = format!("{}-{}", tx_digest, event_index); + + assert!(REGEX.captures(&msg_id).is_some()); + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&msg_id); + assert!(res.is_err()); + + // this is 88 chars and valid base 58, but will encode to 65 bytes + // (z is the largest base58 digit, and so this will overflow 2^512) + let tx_digest = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"; + assert_eq!(tx_digest.len(), 88); + let msg_id = format!("{}-{}", tx_digest, event_index); + + assert!(REGEX.captures(&msg_id).is_some()); + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&msg_id); + assert!(res.is_err()); + } + + #[test] + fn should_parse_msg_id_less_than_88_chars_tx_digest() { + // the tx digest can be less than 88 chars in the presence of leading 1s (00 in hex) + let tx_digest = + "1111KKdpXH2QMB5Jm11YR48cLqUJb9Cwq2YL3tveVTPeFkZaLP8cdcH5UphVPJ7kYwCUCRLnywd3xkUhb4ZYW"; + assert!(tx_digest.len() < 88); + let event_index = random_event_index(); + + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&format!( + "{}-{}", + tx_digest, event_index + )); + assert!(res.is_ok()); + } + + #[test] + fn should_not_parse_msg_id_with_invalid_base58() { + let tx_digest = random_tx_digest(); + let event_index = random_event_index(); + + // 0, O and I are invalid base58 chars + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&format!( + "0{}-{}", + &tx_digest[1..], + event_index + )); + assert!(res.is_err()); + + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&format!( + "I{}-{}", + &tx_digest[1..], + event_index + )); + assert!(res.is_err()); + + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&format!( + "O{}-{}", + &tx_digest[1..], + event_index + )); + assert!(res.is_err()); + } + + #[test] + fn should_not_parse_msg_id_with_hex_tx_digest() { + let tx_digest = random_tx_digest(); + let event_index = random_event_index(); + let tx_digest_hex = bs58::decode(tx_digest) + .into_vec() + .unwrap() + .encode_hex::(); + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&format!( + "{}-{}", + tx_digest_hex, event_index + )); + assert!(res.is_err()); + + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&format!( + "0x{}-{}", + tx_digest_hex, event_index + )); + assert!(res.is_err()); + } + + #[test] + fn should_not_parse_msg_id_with_missing_event_index() { + let msg_id = random_tx_digest(); + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&msg_id); + assert!(res.is_err()); + } + + #[test] + fn should_not_parse_msg_id_with_wrong_separator() { + let tx_digest = random_tx_digest(); + let event_index = random_event_index(); + + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&format!( + "{}:{}", + tx_digest, event_index + )); + assert!(res.is_err()); + + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&format!( + "{}_{}", + tx_digest, event_index + )); + assert!(res.is_err()); + + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&format!( + "{}+{}", + tx_digest, event_index + )); + assert!(res.is_err()); + + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&format!( + "{}{}", + tx_digest, event_index + )); + assert!(res.is_err()); + + for _ in 0..10 { + let random_sep: char = rand::random(); + if random_sep == '-' { + continue; + } + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&format!( + "{}{}{}", + tx_digest, random_sep, event_index + )); + assert!(res.is_err()); + } + } + + #[test] + fn should_not_parse_msg_id_with_event_index_with_leading_zeroes() { + let tx_digest = random_tx_digest(); + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&format!("{}-01", tx_digest)); + assert!(res.is_err()); + } + + #[test] + fn should_not_parse_msg_id_with_non_integer_event_index() { + let tx_digest = random_tx_digest(); + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&format!("{}-1.0", tx_digest)); + assert!(res.is_err()); + + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&format!("{}-0x00", tx_digest)); + assert!(res.is_err()); + + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&format!("{}-foobar", tx_digest)); + assert!(res.is_err()); + + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&format!("{}-true", tx_digest)); + assert!(res.is_err()); + + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&format!("{}-", tx_digest)); + assert!(res.is_err()); + } + + #[test] + fn should_not_parse_msg_id_with_overflowing_event_index() { + let event_index: u64 = u64::MAX; + let tx_digest = random_tx_digest(); + let res = Base58SolanaTxSignatureAndEventIndex::from_str(&format!( + "{}-{}", + tx_digest, event_index + )); + assert!(res.is_err()); + } + + #[test] + fn trimming_leading_ones_should_change_bytes() { + for _ in 0..100 { + let mut bytes = random_bytes(); + + // set a random (non-zero) number of leading bytes to 0 + let leading_zeroes = rand::random::() % bytes.len() + 1; + for b in bytes.iter_mut().take(leading_zeroes) { + *b = 0; + } + + let b58 = bs58::encode(&bytes).into_string(); + + // verify the base58 has the expected number of leading 1's + for c in b58.chars().take(leading_zeroes) { + assert_eq!(c, '1'); + } + + // trim a random (non-zero) number of leading 1's + let trim = rand::random::() % leading_zeroes + 1; + + // converting back to bytes should yield a different result + let decoded = bs58::decode(&b58[trim..]).into_vec().unwrap(); + assert_ne!(bytes.to_vec(), decoded); + } + } +} diff --git a/packages/axelar-wasm-std/src/msg_id/mod.rs b/packages/axelar-wasm-std/src/msg_id/mod.rs index 475c9b3b8..8a0ced623 100644 --- a/packages/axelar-wasm-std/src/msg_id/mod.rs +++ b/packages/axelar-wasm-std/src/msg_id/mod.rs @@ -1,14 +1,18 @@ -use std::{fmt::Display, str::FromStr}; +use std::fmt::Display; +use std::str::FromStr; use cosmwasm_schema::cw_serde; use error_stack::Report; -use self::{ - base_58_event_index::Base58TxDigestAndEventIndex, tx_hash_event_index::HexTxHashAndEventIndex, -}; +pub use self::base_58_event_index::Base58TxDigestAndEventIndex; +pub use self::base_58_solana_event_index::Base58SolanaTxSignatureAndEventIndex; +pub use self::tx_hash::HexTxHash; +pub use self::tx_hash_event_index::HexTxHashAndEventIndex; -pub mod base_58_event_index; -pub mod tx_hash_event_index; +mod base_58_event_index; +mod base_58_solana_event_index; +mod tx_hash; +mod tx_hash_event_index; #[derive(thiserror::Error)] #[cw_serde] @@ -39,6 +43,8 @@ pub trait MessageId: FromStr + Display {} pub enum MessageIdFormat { HexTxHashAndEventIndex, Base58TxDigestAndEventIndex, + Base58SolanaTxSignatureAndEventIndex, + HexTxHash, } // function the router calls to verify msg ids @@ -50,16 +56,18 @@ pub fn verify_msg_id(message_id: &str, format: &MessageIdFormat) -> Result<(), R MessageIdFormat::Base58TxDigestAndEventIndex => { Base58TxDigestAndEventIndex::from_str(message_id).map(|_| ()) } + MessageIdFormat::Base58SolanaTxSignatureAndEventIndex => { + Base58SolanaTxSignatureAndEventIndex::from_str(message_id).map(|_| ()) + } + MessageIdFormat::HexTxHash => HexTxHash::from_str(message_id).map(|_| ()), } } #[cfg(test)] mod test { - use crate::msg_id::{ - base_58_event_index::Base58TxDigestAndEventIndex, verify_msg_id, MessageIdFormat, - }; - use super::tx_hash_event_index::HexTxHashAndEventIndex; + use crate::msg_id::base_58_event_index::Base58TxDigestAndEventIndex; + use crate::msg_id::{verify_msg_id, MessageIdFormat}; #[test] fn should_verify_hex_tx_hash_event_index_msg_id() { diff --git a/packages/axelar-wasm-std/src/msg_id/tx_hash.rs b/packages/axelar-wasm-std/src/msg_id/tx_hash.rs new file mode 100644 index 000000000..785fa4827 --- /dev/null +++ b/packages/axelar-wasm-std/src/msg_id/tx_hash.rs @@ -0,0 +1,138 @@ +use core::fmt; +use std::fmt::Display; +use std::str::FromStr; + +use cosmwasm_std::HexBinary; +use error_stack::{ensure, Report, ResultExt}; +use lazy_static::lazy_static; +use regex::Regex; + +use super::Error; +use crate::hash::Hash; +use crate::nonempty; + +pub struct HexTxHash { + pub tx_hash: Hash, +} + +impl HexTxHash { + pub fn tx_hash_as_hex(&self) -> nonempty::String { + format!("0x{}", HexBinary::from(self.tx_hash).to_hex()) + .try_into() + .expect("failed to convert tx hash to non-empty string") + } + + pub fn new(tx_id: impl Into<[u8; 32]>) -> Self { + Self { + tx_hash: tx_id.into(), + } + } +} + +const PATTERN: &str = "^0x[0-9a-f]{64}$"; +lazy_static! { + static ref REGEX: Regex = Regex::new(PATTERN).expect("invalid regex"); +} + +impl FromStr for HexTxHash { + type Err = Report; + + fn from_str(message_id: &str) -> Result + where + Self: Sized, + { + // the PATTERN has exactly two capture groups, so the groups can be extracted safely + ensure!( + REGEX.is_match(message_id), + Error::InvalidMessageID { + id: message_id.to_string(), + expected_format: PATTERN.to_string(), + } + ); + Ok(HexTxHash { + tx_hash: HexBinary::from_hex(&message_id[2..]) + .change_context(Error::InvalidTxHash(message_id.to_string()))? + .as_slice() + .try_into() + .map_err(|_| Error::InvalidTxHash(message_id.to_string()))?, + }) + } +} + +impl Display for HexTxHash { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "0x{}", HexBinary::from(self.tx_hash).to_hex()) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + fn random_hash() -> String { + let mut bytes = vec![]; + for _ in 0..32 { + let byte: u8 = rand::random(); + bytes.push(byte) + } + format!("0x{}", HexBinary::from(bytes).to_hex()) + } + + #[test] + fn should_parse_msg_id() { + let res = HexTxHash::from_str( + "0x7cedbb3799cd99636045c84c5c55aef8a138f107ac8ba53a08cad1070ba4385b", + ); + assert!(res.is_ok()); + + for _ in 0..1000 { + let msg_id = random_hash(); + + let res = HexTxHash::from_str(&msg_id); + let parsed = res.unwrap(); + assert_eq!(parsed.tx_hash_as_hex(), msg_id.clone().try_into().unwrap()); + assert_eq!(parsed.to_string(), msg_id); + } + } + + #[test] + fn should_not_parse_msg_id_with_wrong_length_tx_hash() { + let tx_hash = random_hash(); + // too long + let res = HexTxHash::from_str(&format!("{}ff", tx_hash)); + assert!(res.is_err()); + + // too short + let res = HexTxHash::from_str(&tx_hash[..tx_hash.len() - 2]); + assert!(res.is_err()); + } + + #[test] + fn should_not_parse_msg_id_with_uppercase_tx_hash() { + let tx_hash = &random_hash()[2..]; + let res = HexTxHash::from_str(&format!("0x{}", tx_hash.to_uppercase())); + assert!(res.is_err()); + } + + #[test] + fn should_not_parse_msg_id_with_non_hex_tx_hash() { + let msg_id = "82GKYvWv5EKm7jnYksHoh3u5M2RxHN2boPreM8Df4ej9"; + let res = HexTxHash::from_str(msg_id); + assert!(res.is_err()); + } + + #[test] + fn should_not_parse_msg_id_without_0x() { + let msg_id = "7cedbb3799cd99636045c84c5c55aef8a138f107ac8ba53a08cad1070ba4385b"; + let res = HexTxHash::from_str(msg_id); + assert!(res.is_err()); + } + + #[test] + fn should_not_parse_msg_id_with_event_index() { + let tx_hash = random_hash(); + let res = HexTxHash::from_str(&format!("{}-1", tx_hash)); + assert!(res.is_err()); + } +} diff --git a/packages/axelar-wasm-std/src/msg_id/tx_hash_event_index.rs b/packages/axelar-wasm-std/src/msg_id/tx_hash_event_index.rs index 42c764f7d..9f9bf8376 100644 --- a/packages/axelar-wasm-std/src/msg_id/tx_hash_event_index.rs +++ b/packages/axelar-wasm-std/src/msg_id/tx_hash_event_index.rs @@ -1,5 +1,6 @@ use core::fmt; -use std::{fmt::Display, str::FromStr}; +use std::fmt::Display; +use std::str::FromStr; use cosmwasm_std::HexBinary; use error_stack::{Report, ResultExt}; @@ -7,7 +8,8 @@ use lazy_static::lazy_static; use regex::Regex; use super::Error; -use crate::{hash::Hash, nonempty}; +use crate::hash::Hash; +use crate::nonempty; pub struct HexTxHashAndEventIndex { pub tx_hash: Hash, @@ -73,6 +75,15 @@ impl Display for HexTxHashAndEventIndex { } } +impl From for nonempty::String { + fn from(msg_id: HexTxHashAndEventIndex) -> Self { + msg_id + .to_string() + .try_into() + .expect("failed to convert msg id to non-empty string") + } +} + #[cfg(test)] mod tests { diff --git a/packages/axelar-wasm-std/src/nonempty/string.rs b/packages/axelar-wasm-std/src/nonempty/string.rs index b891b3326..2de06f2ea 100644 --- a/packages/axelar-wasm-std/src/nonempty/string.rs +++ b/packages/axelar-wasm-std/src/nonempty/string.rs @@ -2,13 +2,14 @@ use std::ops::Deref; use std::str::FromStr; use cosmwasm_schema::cw_serde; +use into_inner_derive::IntoInner; use valuable::Valuable; use crate::nonempty::Error; #[cw_serde] #[serde(try_from = "std::string::String")] -#[derive(Eq, Hash, Valuable)] +#[derive(Eq, Hash, Valuable, IntoInner)] pub struct String(std::string::String); impl TryFrom for String { diff --git a/packages/axelar-wasm-std/src/nonempty/timestamp.rs b/packages/axelar-wasm-std/src/nonempty/timestamp.rs index 2501f8c36..6a8f201ea 100644 --- a/packages/axelar-wasm-std/src/nonempty/timestamp.rs +++ b/packages/axelar-wasm-std/src/nonempty/timestamp.rs @@ -1,7 +1,10 @@ -use crate::nonempty::Error; use cosmwasm_schema::cw_serde; +use into_inner_derive::IntoInner; + +use crate::nonempty::Error; #[cw_serde] +#[derive(IntoInner)] pub struct Timestamp(cosmwasm_std::Timestamp); impl TryFrom for Timestamp { diff --git a/packages/axelar-wasm-std/src/nonempty/uint.rs b/packages/axelar-wasm-std/src/nonempty/uint.rs index 2f2a98fb1..3af6af52a 100644 --- a/packages/axelar-wasm-std/src/nonempty/uint.rs +++ b/packages/axelar-wasm-std/src/nonempty/uint.rs @@ -1,12 +1,14 @@ use std::fmt; -use crate::nonempty::Error; use cosmwasm_schema::cw_serde; +use into_inner_derive::IntoInner; + +use crate::nonempty::Error; #[cw_serde] #[serde(try_from = "cosmwasm_std::Uint64")] #[serde(into = "cosmwasm_std::Uint64")] -#[derive(Copy, PartialOrd)] +#[derive(Copy, PartialOrd, IntoInner)] pub struct Uint64(cosmwasm_std::Uint64); impl TryFrom for Uint64 { @@ -49,7 +51,7 @@ impl fmt::Display for Uint64 { // TODO: consider using macro for these types #[cw_serde] -#[derive(Copy, PartialOrd, Eq)] +#[derive(Copy, PartialOrd, Eq, IntoInner)] pub struct Uint256(cosmwasm_std::Uint256); impl TryFrom for Uint256 { @@ -82,14 +84,8 @@ impl TryFrom for Uint256 { } } -impl AsRef for Uint256 { - fn as_ref(&self) -> &cosmwasm_std::Uint256 { - &self.0 - } -} - #[cw_serde] -#[derive(Copy, PartialOrd, Eq)] +#[derive(Copy, PartialOrd, Eq, IntoInner)] pub struct Uint128(cosmwasm_std::Uint128); impl TryFrom for Uint128 { @@ -110,6 +106,13 @@ impl From for cosmwasm_std::Uint128 { } } +impl TryFrom for Uint128 { + type Error = Error; + fn try_from(value: u128) -> Result { + cosmwasm_std::Uint128::from(value).try_into() + } +} + impl Uint128 { pub const fn one() -> Self { Self(cosmwasm_std::Uint128::one()) @@ -185,9 +188,8 @@ mod tests { } #[test] - fn convert_from_uint256_to_reference_cosmwasm_uint256() { - let val = Uint256(cosmwasm_std::Uint256::one()); - let converted: &cosmwasm_std::Uint256 = val.as_ref(); - assert_eq!(&val.0, converted); + fn convert_from_uint128_to_non_empty_uint128() { + assert!(Uint128::try_from(0u128).is_err()); + assert!(Uint128::try_from(1u128).is_ok()); } } diff --git a/packages/axelar-wasm-std/src/nonempty/vec.rs b/packages/axelar-wasm-std/src/nonempty/vec.rs index 874033ab0..eb9c68918 100644 --- a/packages/axelar-wasm-std/src/nonempty/vec.rs +++ b/packages/axelar-wasm-std/src/nonempty/vec.rs @@ -1,7 +1,8 @@ -use crate::nonempty::Error; use cosmwasm_schema::cw_serde; use cosmwasm_std::HexBinary; +use crate::nonempty::Error; + #[cw_serde] #[serde(try_from = "std::vec::Vec")] pub struct Vec(std::vec::Vec); diff --git a/packages/axelar-wasm-std/src/permission_control.rs b/packages/axelar-wasm-std/src/permission_control.rs new file mode 100644 index 000000000..6320149ec --- /dev/null +++ b/packages/axelar-wasm-std/src/permission_control.rs @@ -0,0 +1,171 @@ +use std::fmt::{Debug, Display, Formatter}; + +use cosmwasm_std::{Addr, StdResult}; +use cw_storage_plus::Item; +use flagset::{flags, Flags}; +use itertools::Itertools; +use serde::{Deserialize, Serialize}; + +use crate::flagset::FlagSet; +use crate::FnExt; + +flags! { + #[repr(u8)] + #[derive(Serialize, Deserialize)] + pub enum Permission: u8 { + NoPrivilege = 0b001, // this specifies that the user MUST NOT have an elevated role + Admin = 0b010, + Governance = 0b100, + Elevated = (Permission::Admin | Permission::Governance).bits(), + Any = (Permission::NoPrivilege | Permission::Elevated).bits(), + } +} + +impl Display for FlagSet { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Permission::LIST + .iter() + .find(|permission| self.eq(&(**permission).into())) + .map_or_else( + || { + self.into_iter() + .map(|permission| format!("{:?}", permission)) + .join(" | ") + }, + |permission| format!("{:?}", permission), + ) + .then(|permission| write!(f, "{}", permission)) + } +} + +#[derive(thiserror::Error, Debug, PartialEq)] +pub enum Error { + #[error("sender with role '{actual}' is not allowed to perform this action that requires '{expected}' permissions")] + PermissionDenied { + expected: FlagSet, + actual: FlagSet, + }, + #[error("sender '{actual}' must be one of the addresses {expected:?}")] + AddressNotWhitelisted { expected: Vec, actual: Addr }, + #[error("no whitelisting condition found for sender address '{sender}'")] + WhitelistNotFound { sender: Addr }, + #[error("specific check called on wrong enum variant")] + WrongVariant, + #[error("sender is not authorized")] + Unauthorized, // generic error to handle errors that don't fall into the above cases +} + +const ADMIN: Item = Item::new("permission_control_contract_admin_addr"); + +const GOVERNANCE: Item = Item::new("permission_control_governance_addr"); + +pub fn set_admin(storage: &mut dyn cosmwasm_std::Storage, addr: &Addr) -> StdResult<()> { + ADMIN.save(storage, addr) +} + +pub fn set_governance(storage: &mut dyn cosmwasm_std::Storage, addr: &Addr) -> StdResult<()> { + GOVERNANCE.save(storage, addr) +} + +/// Generally it shouldn't be necessary to call this function directly, use derived permission controlled functions instead +#[allow(clippy::arithmetic_side_effects)] // flagset is safe +pub fn sender_role( + storage: &dyn cosmwasm_std::Storage, + sender: &Addr, +) -> StdResult> { + let admin = ADMIN.may_load(storage)?; + let governance = GOVERNANCE.may_load(storage)?; + + let mut role = FlagSet::from(Permission::NoPrivilege); + + if admin.is_some_and(|admin| admin == sender) { + *role |= Permission::Admin; + } + + if governance.is_some_and(|governance| governance == sender) { + *role |= Permission::Governance; + } + + // a role cannot be both elevated and without privilege at the same time + if !role.is_disjoint(Permission::Elevated) { + *role -= Permission::NoPrivilege; + } + + Ok(role) +} + +#[cfg(test)] +mod tests { + use cosmwasm_std::testing::MockStorage; + + use super::*; + + #[test] + fn display_permissions() { + assert_eq!(format!("{}", FlagSet::from(Permission::Admin)), "Admin"); + assert_eq!( + format!( + "{}", + FlagSet::from(Permission::NoPrivilege | Permission::Governance) + ), + "NoPrivilege | Governance" + ); + assert_eq!( + format!( + "{}", + FlagSet::from(Permission::NoPrivilege | Permission::Governance | Permission::Admin) + ), + "Any" + ); + } + + #[test] + fn sender_role_from_storage() { + let admin = Addr::unchecked("admin"); + let governance = Addr::unchecked("governance"); + let regular_user = Addr::unchecked("regular user"); + + let mut storage = MockStorage::new(); + set_admin(&mut storage, &admin).unwrap(); + set_governance(&mut storage, &governance).unwrap(); + + assert_eq!( + sender_role(&storage, &admin).unwrap(), + FlagSet::from(Permission::Admin) + ); + assert_eq!( + sender_role(&storage, &governance).unwrap(), + FlagSet::from(Permission::Governance) + ); + assert_eq!( + sender_role(&storage, ®ular_user).unwrap(), + FlagSet::from(Permission::NoPrivilege) + ); + + set_governance(&mut storage, &admin).unwrap(); + assert_eq!( + sender_role(&storage, &admin).unwrap(), + FlagSet::from(Permission::Elevated) + ); + } + + #[test] + fn permission_level_correctly_defined() { + assert!(!FlagSet::from(Permission::NoPrivilege).contains(Permission::Admin)); + assert!(!FlagSet::from(Permission::NoPrivilege).contains(Permission::Governance)); + + assert!(!FlagSet::from(Permission::Admin).contains(Permission::NoPrivilege)); + assert!(!FlagSet::from(Permission::Admin).contains(Permission::Governance)); + + assert!(!FlagSet::from(Permission::Governance).contains(Permission::NoPrivilege)); + assert!(!FlagSet::from(Permission::Governance).contains(Permission::Admin)); + + assert!(!FlagSet::from(Permission::Elevated).contains(Permission::NoPrivilege)); + assert!(FlagSet::from(Permission::Elevated).contains(Permission::Admin)); + assert!(FlagSet::from(Permission::Elevated).contains(Permission::Governance)); + + assert!(FlagSet::from(Permission::Any).contains(Permission::NoPrivilege)); + assert!(FlagSet::from(Permission::Any).contains(Permission::Admin)); + assert!(FlagSet::from(Permission::Any).contains(Permission::Governance)); + } +} diff --git a/packages/axelar-wasm-std/src/snapshot.rs b/packages/axelar-wasm-std/src/snapshot.rs index ec7956d03..165966f6b 100644 --- a/packages/axelar-wasm-std/src/snapshot.rs +++ b/packages/axelar-wasm-std/src/snapshot.rs @@ -3,7 +3,8 @@ use std::collections::HashMap; use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, Uint128}; -use crate::{nonempty, threshold::MajorityThreshold}; +use crate::nonempty; +use crate::threshold::MajorityThreshold; #[cw_serde] pub struct Participant { @@ -44,7 +45,7 @@ impl Snapshot { } } - pub fn get_participants(&self) -> Vec { + pub fn participants(&self) -> Vec { self.participants .keys() .cloned() @@ -52,7 +53,7 @@ impl Snapshot { .collect() } - pub fn get_participant(&self, participant: &Addr) -> Option<&Participant> { + pub fn find(&self, participant: &Addr) -> Option<&Participant> { self.participants.get(&participant.to_string()) } } @@ -61,9 +62,8 @@ impl Snapshot { mod tests { use cosmwasm_std::{from_json, to_json_binary, Uint64}; - use crate::Threshold; - use super::*; + use crate::Threshold; fn mock_participant(address: &str, weight: nonempty::Uint128) -> Participant { Participant { diff --git a/packages/axelar-wasm-std/src/utils.rs b/packages/axelar-wasm-std/src/utils.rs index 06d173a2c..e45c0695a 100644 --- a/packages/axelar-wasm-std/src/utils.rs +++ b/packages/axelar-wasm-std/src/utils.rs @@ -1,74 +1,47 @@ -pub fn try_map(vec: Vec, f: F) -> Result, E> -where - F: FnMut(T) -> Result, -{ - vec.into_iter().map(f).collect::, E>>() +pub trait TryMapExt { + type Monad; + fn try_map(self, func: impl FnMut(T) -> Result) -> Result, E>; } -pub trait InspectorResult { - /// This function should be called `inspect`, but would have a name collision with the unstable [core::result::Result::inspect](https://doc.rust-lang.org/core/result/enum.Result.html#method.inspect) function. - fn tap(self, f: F) -> Self - where - F: FnOnce(&T); +impl TryMapExt for Option { + type Monad = Option; - /// This function should be called `inspect_err`, but would have a name collision with the unstable [core::result::Result::inspect_err](https://doc.rust-lang.org/core/result/enum.Result.html#method.inspect_err) function. - fn tap_err(self, f: F) -> Self - where - F: FnOnce(&E); + fn try_map(self, func: impl FnMut(T) -> Result) -> Result, E> { + self.map(func).transpose() + } } -impl InspectorResult for Result { - /// Use this to create a side effect without consuming the result. - /// - /// Example: - /// ``` - /// use axelar_wasm_std::utils::InspectorResult; - /// - /// let result: Result = Ok(1); - /// assert_eq!(result.tap(|x| println!("result is {}", x)).map(|x| x + 1), Ok(2)); - /// - /// let err:Result = Err("wrong value".to_string()); - /// assert!(err.tap(|x| println!("error is {}", x)).is_err()); // println will not be called - /// ``` - fn tap(self, f: F) -> Self - where - F: FnOnce(&T), - { - self.map(|t| { - f(&t); - t - }) - } +impl TryMapExt for Vec { + type Monad = Vec; - /// Use this to create a side effect without consuming the error. - /// - /// Example: - /// ``` - /// use axelar_wasm_std::utils::InspectorResult; - /// - /// let result: Result = Ok(1); - /// assert_eq!(result.tap_err(|x| println!("error is {}", x)).map(|x| x + 1), Ok(2)); // println will not be called - /// - /// let err:Result = Err("wrong value".to_string()); - /// assert!(err.tap_err(|x| println!("result is {}", x)).is_err()); - /// ``` - fn tap_err(self, f: F) -> Self - where - F: FnOnce(&E), - { - self.map_err(|e| { - f(&e); - e - }) + fn try_map(self, func: impl FnMut(T) -> Result) -> Result, E> { + self.into_iter().map(func).collect::, E>>() } } -pub trait TryMapExt { - fn try_map(self, func: impl FnOnce(T) -> Result) -> Result, E>; -} +#[cfg(test)] +mod test { + use super::*; -impl TryMapExt for Option { - fn try_map(self, func: impl FnOnce(T) -> Result) -> Result, E> { - self.map(func).transpose() + #[test] + fn test_try_map_vec() { + let vec = vec![1, 2, 3]; + let result: Result<_, &str> = vec.try_map(|x| Ok(x + 1)); + assert_eq!(result, Ok(vec![2, 3, 4])); + + let vec = vec![1, 2, 3]; + let result: Result, _> = vec.try_map(|_| Err("error")); + assert_eq!(result, Err("error")); + } + + #[test] + fn test_try_map_option() { + let option = Some(1); + let result: Result<_, &str> = option.try_map(|x| Ok(x + 1)); + assert_eq!(result, Ok(Some(2))); + + let option = Some(1); + let result: Result, _> = option.try_map(|_| Err("error")); + assert_eq!(result, Err("error")); } } diff --git a/packages/axelar-wasm-std/src/vec.rs b/packages/axelar-wasm-std/src/vec.rs new file mode 100644 index 000000000..d24479fd0 --- /dev/null +++ b/packages/axelar-wasm-std/src/vec.rs @@ -0,0 +1,13 @@ +pub trait VecExt { + fn to_none_if_empty(self) -> Option>; +} + +impl VecExt for Vec { + fn to_none_if_empty(self) -> Option> { + if self.is_empty() { + None + } else { + Some(self) + } + } +} diff --git a/packages/axelar-wasm-std/src/voting.rs b/packages/axelar-wasm-std/src/voting.rs index 4a5d5aed6..e3eae4f97 100644 --- a/packages/axelar-wasm-std/src/voting.rs +++ b/packages/axelar-wasm-std/src/voting.rs @@ -14,25 +14,20 @@ on whether the transaction was successfully verified. */ use std::array::TryFromSliceError; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashMap}; use std::fmt; -use std::ops::Add; -use std::ops::Mul; +use std::ops::{Add, Mul}; use std::str::FromStr; use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, StdError, StdResult, Uint128, Uint64}; -use cw_storage_plus::{IntKey, Key, KeyDeserialize, PrimaryKey}; -use num_traits::CheckedAdd; -use num_traits::One; -use strum::EnumIter; -use strum::EnumString; -use strum::IntoEnumIterator; +use cw_storage_plus::{IntKey, Key, KeyDeserialize, Prefixer, PrimaryKey}; +use num_traits::{CheckedAdd, One}; +use strum::{EnumIter, EnumString, IntoEnumIterator}; use thiserror::Error; use valuable::Valuable; -use crate::nonempty; -use crate::Snapshot; +use crate::{nonempty, Snapshot}; #[derive(Error, Debug, PartialEq, Eq)] pub enum Error { @@ -56,6 +51,9 @@ pub enum Error { #[error("message index out of bounds")] MessageIndexOutOfBounds, + + #[error("poll results have different length")] + PollResultsLengthUnequal, } #[cw_serde] @@ -130,6 +128,12 @@ impl<'a> PrimaryKey<'a> for PollId { } } +impl<'a> Prefixer<'a> for PollId { + fn prefix(&self) -> Vec { + vec![Key::Val64(self.0.to_be_bytes())] + } +} + impl KeyDeserialize for PollId { type Output = Self; @@ -204,10 +208,38 @@ impl Tallies { } } +#[cw_serde] +pub struct PollResults(pub Vec>); + +// would be better to implement the Sub trait, but clippy is configured to not allow arithmetic operators +impl PollResults { + /// Returns the elements in self that are Some, but in rhs are None. All other elements are converted to None. + /// This is used to determine which elements have quorum in self, but do not have quorum in rhs. + /// Vectors must be equal length. + pub fn difference(self, rhs: Self) -> Result { + if self.0.len() != rhs.0.len() { + return Err(Error::PollResultsLengthUnequal); + } + Ok(PollResults( + self.0 + .into_iter() + .zip(rhs.0) + .map(|(lhs, rhs)| { + if lhs.is_some() && rhs.is_none() { + lhs + } else { + None + } + }) + .collect(), + )) + } +} + #[cw_serde] pub struct PollState { pub poll_id: PollId, - pub results: Vec>, + pub results: PollResults, /// List of participants who voted for the winning result pub consensus_participants: Vec, } @@ -215,23 +247,24 @@ pub struct PollState { #[cw_serde] pub enum PollStatus { InProgress, + Expired, Finished, } #[cw_serde] pub struct Participation { pub weight: nonempty::Uint128, - pub vote: Option>, + pub voted: bool, } #[cw_serde] pub struct WeightedPoll { pub poll_id: PollId, pub quorum: nonempty::Uint128, - pub expires_at: u64, + expires_at: u64, pub poll_size: u64, pub tallies: Vec, // running tally of weighted votes - pub status: PollStatus, + finished: bool, pub participation: BTreeMap, } @@ -247,7 +280,7 @@ impl WeightedPoll { address, Participation { weight: participant.weight, - vote: None, + voted: false, }, ) }) @@ -259,13 +292,13 @@ impl WeightedPoll { expires_at: expiry, poll_size: poll_size as u64, tallies: vec![Tallies::default(); poll_size], - status: PollStatus::InProgress, + finished: false, participation, } } pub fn finish(mut self, block_height: u64) -> Result { - if matches!(self.status, PollStatus::Finished { .. }) { + if self.finished { return Err(Error::PollNotInProgress); } @@ -273,12 +306,22 @@ impl WeightedPoll { return Err(Error::PollNotEnded); } - self.status = PollStatus::Finished; + self.finished = true; Ok(self) } - pub fn state(&self) -> PollState { + pub fn results(&self) -> PollResults { + let quorum: Uint128 = self.quorum.into(); + PollResults( + self.tallies + .iter() + .map(|tallies| tallies.consensus(quorum)) + .collect(), + ) + } + + pub fn state(&self, voting_history: HashMap>) -> PollState { let quorum: Uint128 = self.quorum.into(); let results: Vec> = self .tallies @@ -289,10 +332,11 @@ impl WeightedPoll { let consensus_participants = self .participation .iter() - .filter_map(|(address, participation)| { - participation.vote.as_ref().and_then(|votes| { + .filter_map(|(address, _)| { + voting_history.get(address).and_then(|votes| { let voted_consensus = votes.iter().zip(results.iter()).all(|(vote, result)| { - result.is_none() || Some(vote) == result.as_ref() // if there was no consensus, we don't care about the vote + result.is_none() || Some(vote) == result.as_ref() + // if there was no consensus, we don't care about the vote }); if voted_consensus { @@ -306,7 +350,7 @@ impl WeightedPoll { PollState { poll_id: self.poll_id, - results, + results: PollResults(results), consensus_participants, } } @@ -334,11 +378,11 @@ impl WeightedPoll { return Err(Error::PollExpired); } - if votes.len() != self.poll_size as usize { + if votes.len() as u64 != self.poll_size { return Err(Error::InvalidVoteSize); } - if participation.vote.is_some() { + if participation.voted { return Err(Error::AlreadyVoted); } @@ -349,21 +393,28 @@ impl WeightedPoll { tallies.tally(vote, &participation.weight.into()); }); - participation.vote = Some(votes); + participation.voted = true; Ok(self) } + + pub fn status(&self, current_height: u64) -> PollStatus { + match self.finished { + true => PollStatus::Finished, + false if current_height >= self.expires_at => PollStatus::Expired, + _ => PollStatus::InProgress, + } + } } #[cfg(test)] mod tests { use cosmwasm_std::{Addr, Uint64}; use rand::distributions::Alphanumeric; - use rand::{thread_rng, Rng}; - - use crate::{nonempty, Participant, Threshold}; + use rand::Rng; use super::*; + use crate::{nonempty, Participant, Threshold}; #[test] fn cast_vote() { @@ -374,7 +425,7 @@ mod tests { poll.participation.get("addr1").unwrap(), &Participation { weight: nonempty::Uint128::try_from(Uint128::from(100u64)).unwrap(), - vote: None, + voted: false } ); @@ -386,14 +437,14 @@ mod tests { poll.participation.get("addr1").unwrap(), &Participation { weight: nonempty::Uint128::try_from(Uint128::from(100u64)).unwrap(), - vote: Some(votes), + voted: true } ); } #[test] fn voter_not_a_participant() { - let mut rng = thread_rng(); + let mut rng = rand::thread_rng(); let poll = new_poll( rng.gen::(), rng.gen_range(1..50), @@ -401,7 +452,7 @@ mod tests { ); let votes = vec![Vote::SucceededOnChain, Vote::SucceededOnChain]; - let rand_addr: String = thread_rng() + let rand_addr: String = rand::thread_rng() .sample_iter(&Alphanumeric) .take(5) .map(char::from) @@ -460,30 +511,39 @@ mod tests { #[test] fn finish_after_poll_conclude() { let mut poll = new_poll(2, 2, vec!["addr1", "addr2"]); - poll.status = PollStatus::Finished; - assert_eq!(poll.finish(2), Err(Error::PollNotInProgress)); + poll = poll.finish(2).unwrap(); + assert_eq!(poll.finish(3), Err(Error::PollNotInProgress)); } #[test] fn should_conclude_poll() { let poll = new_poll(2, 2, vec!["addr1", "addr2", "addr3"]); let votes = vec![Vote::SucceededOnChain, Vote::SucceededOnChain]; + let voters = [Addr::unchecked("addr1"), Addr::unchecked("addr2")]; let poll = poll - .cast_vote(1, &Addr::unchecked("addr1"), votes.clone()) + .cast_vote(1, &voters[0], votes.clone()) .unwrap() - .cast_vote(1, &Addr::unchecked("addr2"), votes) + .cast_vote(1, &voters[1], votes.clone()) .unwrap(); let poll = poll.finish(2).unwrap(); - assert_eq!(poll.status, PollStatus::Finished); + assert_eq!(poll.status(2), PollStatus::Finished); - let result = poll.state(); + let result = poll.state( + voters + .iter() + .map(|voter| (voter.to_string(), votes.clone())) + .collect(), + ); assert_eq!( result, PollState { poll_id: PollId::from(Uint64::one()), - results: vec![Some(Vote::SucceededOnChain), Some(Vote::SucceededOnChain)], + results: PollResults(vec![ + Some(Vote::SucceededOnChain), + Some(Vote::SucceededOnChain) + ]), consensus_participants: vec!["addr1".to_string(), "addr2".to_string(),], } ); @@ -494,27 +554,60 @@ mod tests { let poll = new_poll(2, 2, vec!["addr1", "addr2", "addr3"]); let votes = vec![Vote::SucceededOnChain, Vote::SucceededOnChain]; let wrong_votes = vec![Vote::FailedOnChain, Vote::FailedOnChain]; + let voters = [ + Addr::unchecked("addr1"), + Addr::unchecked("addr2"), + Addr::unchecked("addr3"), + ]; + let voting_history: Vec<(&Addr, Vec)> = voters + .iter() + .enumerate() + .map(|(idx, voter)| { + if idx == 1 { + (voter, wrong_votes.clone()) + } else { + (voter, votes.clone()) + } + }) + .collect(); let poll = poll - .cast_vote(1, &Addr::unchecked("addr1"), votes.clone()) + .cast_vote(1, voting_history[0].0, voting_history[0].1.clone()) .unwrap() - .cast_vote(1, &Addr::unchecked("addr2"), wrong_votes) + .cast_vote(1, voting_history[1].0, voting_history[1].1.clone()) .unwrap() - .cast_vote(1, &Addr::unchecked("addr3"), votes) + .cast_vote(1, voting_history[2].0, voting_history[2].1.clone()) .unwrap(); - let result = poll.finish(2).unwrap().state(); + let result = poll.finish(2).unwrap().state( + voting_history + .into_iter() + .map(|(voter, votes)| (voter.to_string(), votes)) + .collect(), + ); assert_eq!( result, PollState { poll_id: PollId::from(Uint64::one()), - results: vec![Some(Vote::SucceededOnChain), Some(Vote::SucceededOnChain)], + results: PollResults(vec![ + Some(Vote::SucceededOnChain), + Some(Vote::SucceededOnChain) + ]), consensus_participants: vec!["addr1".to_string(), "addr3".to_string(),], } ); } + #[test] + fn status_should_return_current_status() { + let mut poll = new_poll(2, 2, vec!["addr1", "addr2"]); + assert_eq!(poll.status(1), PollStatus::InProgress); + assert_eq!(poll.status(2), PollStatus::Expired); + poll = poll.finish(3).unwrap(); + assert_eq!(poll.status(3), PollStatus::Finished); + } + fn new_poll(expires_at: u64, poll_size: usize, participants: Vec<&str>) -> WeightedPoll { let participants: nonempty::Vec = participants .into_iter() diff --git a/packages/client/Cargo.toml b/packages/client/Cargo.toml index 8cd9ca5c1..cfefeeacb 100644 --- a/packages/client/Cargo.toml +++ b/packages/client/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "client" -version = "0.1.0" -edition = "2021" +version = "1.0.0" +edition = { workspace = true } rust-version = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/packages/client/src/lib.rs b/packages/client/src/lib.rs index 87e1df2d4..49864d1b8 100644 --- a/packages/client/src/lib.rs +++ b/packages/client/src/lib.rs @@ -4,7 +4,8 @@ use cosmwasm_std::{ to_json_binary, Addr, QuerierWrapper, QueryRequest, StdError, WasmMsg, WasmQuery, }; use error_stack::{Report, Result}; -use serde::{de::DeserializeOwned, Serialize}; +use serde::de::DeserializeOwned; +use serde::Serialize; #[derive(thiserror::Error, Debug)] pub enum Error { diff --git a/packages/events-derive/Cargo.toml b/packages/events-derive/Cargo.toml index 630f8b3d1..0d7fcb87e 100644 --- a/packages/events-derive/Cargo.toml +++ b/packages/events-derive/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "events-derive" -version = "0.1.0" +version = "1.0.0" rust-version = { workspace = true } -edition = "2021" +edition = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] diff --git a/packages/events/Cargo.toml b/packages/events/Cargo.toml index 91ca72d8f..1b4dd91c7 100644 --- a/packages/events/Cargo.toml +++ b/packages/events/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "events" -version = "0.1.0" +version = "1.0.0" rust-version = { workspace = true } -edition = "2021" +edition = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/packages/events/src/event.rs b/packages/events/src/event.rs index d39ce5925..ab7fcd1b4 100644 --- a/packages/events/src/event.rs +++ b/packages/events/src/event.rs @@ -3,16 +3,14 @@ use std::fmt::Display; use axelar_wasm_std::FnExt; use base64::engine::general_purpose::STANDARD; use base64::Engine; +use cosmrs::AccountId; use error_stack::{Report, Result, ResultExt}; -use serde_json::Value; use tendermint::abci::EventAttribute; use tendermint::{abci, block}; use crate::errors::DecodingError; use crate::Error; -use cosmrs::AccountId; - #[derive(Clone, Debug, PartialEq, Eq)] pub enum Event { BlockBegin(block::Height), @@ -91,7 +89,7 @@ impl TryFrom for Event { } } -fn try_into_kv_pair(attr: &EventAttribute) -> Result<(String, Value), Error> { +fn try_into_kv_pair(attr: &EventAttribute) -> Result<(String, serde_json::Value), Error> { decode_event_attribute(attr) .change_context(Error::DecodingAttributesFailed) .map(|(key, value)| { diff --git a/packages/evm-gateway/.gitignore b/packages/evm-gateway/.gitignore deleted file mode 100644 index 8b806e780..000000000 --- a/packages/evm-gateway/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# EVM gateway artifacts -src/abi/ diff --git a/packages/evm-gateway/Cargo.toml b/packages/evm-gateway/Cargo.toml index c2fb32ee1..d613723b8 100644 --- a/packages/evm-gateway/Cargo.toml +++ b/packages/evm-gateway/Cargo.toml @@ -1,25 +1,21 @@ [package] name = "evm-gateway" -version = "0.1.0" +version = "1.0.0" rust-version = { workspace = true } -edition = "2021" +edition = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] axelar-wasm-std = { workspace = true } cosmwasm-std = { workspace = true } error-stack = { workspace = true } -ethers = { version = "2.0.14", features = ["abigen"] } -k256 = { version = "0.13.1", features = ["ecdsa"] } +ethers-contract = { workspace = true } +ethers-core = { workspace = true } +k256 = { workspace = true } multisig = { workspace = true } -sha3 ={ workspace = true } +router-api = { workspace = true } +sha3 = { workspace = true } thiserror = { workspace = true } -[build-dependencies] -reqwest = { version = "0.12.4", features = ["blocking", "json"] } -serde = { workspace = true } -serde_json = { workspace = true } -zip = "2.0.0" - [lints] workspace = true diff --git a/packages/evm-gateway/build.rs b/packages/evm-gateway/build.rs deleted file mode 100644 index 080dc4b79..000000000 --- a/packages/evm-gateway/build.rs +++ /dev/null @@ -1,90 +0,0 @@ -use std::env; -use std::fs::{self, File}; -use std::io; -use std::path::{Path, PathBuf}; - -use reqwest::blocking::Client; -use zip::ZipArchive; - -const ABI_FILES: [&str; 1] = ["IAxelarAmplifierGateway.json"]; -const OUTPUT_DIR_BASE: &str = "src/abi"; // Base output directory - -const VERSION: &str = env!( - "SOLIDITY_GATEWAY_VERSION", - "environment variable SOLIDITY_GATEWAY_VERSION is not set" -); -const URL: &str = env!( - "SOLIDITY_RELEASES_URL", - "environment variable SOLIDITY_RELEASES_URL is not set" -); - -fn main() -> Result<(), Box> { - // Append version to output directory - let output_dir = format!("{}/{}/", OUTPUT_DIR_BASE, VERSION); - - // Skip if files already exist - if files_exist(&output_dir, &ABI_FILES) { - return Ok(()); - } - - let zipfile_name = format!("Bytecode-{}.zip", VERSION); - let url = format!("{}/{}/{}", URL, VERSION, &zipfile_name); - let zipfile_path = PathBuf::from(&zipfile_name); - - let mut zip_archive = download(&url, &zipfile_path)?; - - extract(&mut zip_archive, &output_dir)?; - - fs::remove_file(zipfile_path)?; - - Ok(()) -} - -fn files_exist(output_dir: &str, files: &[&str]) -> bool { - let output_path = Path::new(output_dir); - files.iter().all(|file| output_path.join(file).exists()) -} - -fn download(url: &str, zip_path: &Path) -> Result, Box> { - let client = Client::new(); - let mut response = client.get(url).send()?; - if !response.status().is_success() { - return Err(Box::new(io::Error::new( - io::ErrorKind::Other, - format!("failed to download {}", url), - ))); - } - - let mut zipfile = File::create(zip_path)?; - io::copy(&mut response, &mut zipfile)?; - - let zipfile = File::open(zip_path)?; - Ok(ZipArchive::new(zipfile)?) -} - -fn extract(archive: &mut ZipArchive, output_dir: &str) -> io::Result<()> { - let abi_output = Path::new(output_dir); - - fs::create_dir_all(abi_output)?; - - for abi in ABI_FILES.iter() { - let file_path = format!( - "contracts/interfaces/{}.sol/{}", - abi.trim_end_matches(".json"), - abi - ); - let output_path = abi_output.join(abi); - - let mut file = archive.by_name(&file_path).map_err(|_| { - io::Error::new( - io::ErrorKind::NotFound, - format!("file not found in archive: {}", file_path), - ) - })?; - - let mut output_file = File::create(output_path)?; - io::copy(&mut file, &mut output_file)?; - } - - Ok(()) -} diff --git a/contracts/multisig-prover/src/encoding/abi/solidity/IAxelarAmplifierGateway.json b/packages/evm-gateway/src/abi/IAxelarAmplifierGateway.json similarity index 100% rename from contracts/multisig-prover/src/encoding/abi/solidity/IAxelarAmplifierGateway.json rename to packages/evm-gateway/src/abi/IAxelarAmplifierGateway.json diff --git a/packages/evm-gateway/src/error.rs b/packages/evm-gateway/src/error.rs new file mode 100644 index 000000000..261f7c6db --- /dev/null +++ b/packages/evm-gateway/src/error.rs @@ -0,0 +1,13 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum Error { + #[error("invalid public key")] + InvalidPublicKey, + + #[error("invalid address")] + InvalidAddress, + + #[error("invalid signature")] + InvalidSignature, +} diff --git a/packages/evm-gateway/src/lib.rs b/packages/evm-gateway/src/lib.rs index 777337ba3..5354a7c6c 100644 --- a/packages/evm-gateway/src/lib.rs +++ b/packages/evm-gateway/src/lib.rs @@ -1,31 +1,32 @@ +use std::str::FromStr; + +use axelar_wasm_std::hash::Hash; +use axelar_wasm_std::FnExt; use cosmwasm_std::Uint256; use error_stack::{Report, ResultExt}; -use ethers::{ - abi::{encode, Token::Tuple, Tokenize}, - prelude::abigen, - types::Address, - utils::public_key_to_address, -}; +use ethers_contract::abigen; +use ethers_core::abi::Token::{self, Tuple, Uint}; +use ethers_core::abi::{encode, Tokenize}; +use ethers_core::types::{Address, Bytes, U256}; +use ethers_core::utils::public_key_to_address; use k256::ecdsa::VerifyingKey; +use multisig::key::PublicKey; +use multisig::msg::{Signer, SignerWithSig}; +use multisig::verifier_set::VerifierSet; +use router_api::Message as RouterMessage; use sha3::{Digest, Keccak256}; -use thiserror::Error; -use axelar_wasm_std::hash::Hash; -use multisig::{key::PublicKey, msg::Signer, verifier_set::VerifierSet}; +use crate::error::Error; + +pub mod error; // Generates the bindings for the Axelar Amplifier Gateway contract. // This includes the defined structs: Messages, WeightedSigners, WeightedSigner, and Proofs. abigen!( IAxelarAmplifierGateway, - "src/abi/$SOLIDITY_GATEWAY_VERSION/IAxelarAmplifierGateway.json" + "src/abi/IAxelarAmplifierGateway.json" ); -#[derive(Error, Debug)] -pub enum Error { - #[error("invalid public key")] - InvalidPublicKey, -} - impl TryFrom<&VerifierSet> for WeightedSigners { type Error = Report; @@ -69,23 +70,89 @@ impl WeightedSigners { } } -fn evm_address(pub_key: &PublicKey) -> Result> { +impl TryFrom<&RouterMessage> for Message { + type Error = Report; + + fn try_from(msg: &RouterMessage) -> Result { + let contract_address = msg + .destination_address + .as_str() + .then(|addr| addr.strip_prefix("0x").unwrap_or(addr)) + .then(Address::from_str) + .change_context(Error::InvalidAddress)?; + + Ok(Message { + source_chain: msg.cc_id.source_chain.to_string(), + message_id: msg.cc_id.message_id.to_string(), + source_address: msg.source_address.to_string(), + contract_address, + payload_hash: msg.payload_hash, + }) + } +} + +impl Proof { + /// Proof contains the entire verifier set and optimized signatures. Signatures are sorted in ascending order based on the signer's address. + pub fn new( + verifier_set: &VerifierSet, + mut signers_with_sigs: Vec, + ) -> Result> { + let signers = WeightedSigners::try_from(verifier_set)?; + + // The conversion from the public key to the EVM address must be successful, + // otherwise WeightedSigners::try_from would have returned an error. + signers_with_sigs.sort_by_key(|signer| { + evm_address(&signer.signer.pub_key).expect("failed to convert pub key to evm address") + }); + + let signatures = signers_with_sigs + .into_iter() + .map(|signer| Bytes::from(signer.signature.as_ref().to_vec())) + .collect::>(); + + Ok(Proof { + signers, + signatures, + }) + } +} + +#[derive(PartialEq, Debug)] +pub enum CommandType { + ApproveMessages, + RotateSigners, +} + +impl From for Token { + fn from(command_type: CommandType) -> Self { + match command_type { + CommandType::ApproveMessages => Uint(U256::zero()), + CommandType::RotateSigners => Uint(U256::one()), + } + } +} + +pub fn evm_address(pub_key: &PublicKey) -> Result> { match pub_key { PublicKey::Ecdsa(pub_key) => VerifyingKey::from_sec1_bytes(pub_key) .map(|v| public_key_to_address(&v)) .change_context(Error::InvalidPublicKey), - _ => Err(Report::new(Error::InvalidPublicKey).attach_printable("expect ECDSA public key")), + _ => Err(Error::InvalidPublicKey).attach_printable("expect ECDSA public key"), } } #[cfg(test)] mod test { - use cosmwasm_std::{Addr, HexBinary, Uint128}; + use std::str::FromStr; - use axelar_wasm_std::{nonempty, snapshot::Participant}; - use multisig::{key::PublicKey, verifier_set::VerifierSet}; + use axelar_wasm_std::nonempty; + use axelar_wasm_std::snapshot::Participant; + use cosmwasm_std::{Addr, HexBinary, Uint128}; + use multisig::key::PublicKey; + use multisig::verifier_set::VerifierSet; + use router_api::{CrossChainId, Message as RouterMessage}; - use crate::WeightedSigners; + use crate::{Message, WeightedSigners}; #[test] fn weight_signers_hash() { @@ -100,6 +167,48 @@ mod test { ); } + #[test] + fn router_message_to_gateway_message() { + let source_chain = "chain0"; + let message_id = "0xff822c88807859ff226b58e24f24974a70f04b9442501ae38fd665b3c68f3834-0"; + let source_address = "0x52444f1835Adc02086c37Cb226561605e2E1699b"; + let destination_chain = "chain1"; + let payload_hash = "8c3685dc41c2eca11426f8035742fb97ea9f14931152670a5703f18fe8b392f0"; + let destination_addresses = vec![ + "0xa4f10f76b86e01b98daf66a3d02a65e14adb0767", // all lowercase + "0xA4f10f76B86E01B98daF66A3d02a65e14adb0767", // checksummed + "a4f10f76b86e01b98daf66a3d02a65e14adb0767", // no 0x prefix + ]; + + for destination_address in destination_addresses { + let router_messages = RouterMessage { + cc_id: CrossChainId::new(source_chain, message_id).unwrap(), + source_address: source_address.parse().unwrap(), + destination_address: destination_address.parse().unwrap(), + destination_chain: destination_chain.parse().unwrap(), + payload_hash: HexBinary::from_hex(payload_hash) + .unwrap() + .to_array::<32>() + .unwrap(), + }; + + let gateway_message = Message::try_from(&router_messages).unwrap(); + assert_eq!(gateway_message.source_chain, source_chain); + assert_eq!(gateway_message.message_id, message_id); + assert_eq!(gateway_message.source_address, source_address); + assert_eq!( + gateway_message.contract_address, + ethers_core::types::Address::from_str( + destination_address + .strip_prefix("0x") + .unwrap_or(destination_address) + ) + .unwrap() + ); + assert_eq!(gateway_message.payload_hash, router_messages.payload_hash); + } + } + // Generate a worker set matches axelar-gmp-sdk-solidity repo test data pub fn curr_verifier_set() -> VerifierSet { let pub_keys = vec![ diff --git a/packages/gateway-api/Cargo.toml b/packages/gateway-api/Cargo.toml index 0ff5309ce..f497011f1 100644 --- a/packages/gateway-api/Cargo.toml +++ b/packages/gateway-api/Cargo.toml @@ -1,12 +1,16 @@ [package] name = "gateway-api" -version = "0.1.0" +version = "1.0.0" rust-version = { workspace = true } -edition = "2021" +edition = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +axelar-wasm-std = { workspace = true } cosmwasm-schema = { workspace = true } +cosmwasm-std = { workspace = true } +error-stack = { workspace = true } +msgs-derive = { workspace = true } router-api = { workspace = true } [lints] diff --git a/packages/gateway-api/src/msg.rs b/packages/gateway-api/src/msg.rs index 601fb25a5..40c0bc590 100644 --- a/packages/gateway-api/src/msg.rs +++ b/packages/gateway-api/src/msg.rs @@ -1,16 +1,18 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; +use msgs_derive::EnsurePermissions; use router_api::{CrossChainId, Message}; #[cw_serde] +#[derive(EnsurePermissions)] pub enum ExecuteMsg { - // Permissionless /// Before messages that are unknown to the system can be routed, they need to be verified. /// Use this call to trigger verification for any of the given messages that is still unverified. + #[permission(Any)] VerifyMessages(Vec), - // Permissionless /// Forward the given messages to the next step of the routing layer. If these messages are coming in from an external chain, /// they have to be verified first. + #[permission(Any)] RouteMessages(Vec), } @@ -19,5 +21,5 @@ pub enum ExecuteMsg { pub enum QueryMsg { // messages that can be relayed to the chain corresponding to this gateway #[returns(Vec)] - GetOutgoingMessages { message_ids: Vec }, + OutgoingMessages(Vec), } diff --git a/packages/into-inner-derive/Cargo.toml b/packages/into-inner-derive/Cargo.toml new file mode 100644 index 000000000..dc0cbb606 --- /dev/null +++ b/packages/into-inner-derive/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "into-inner-derive" +version = "1.0.0" +rust-version = { workspace = true } +edition = { workspace = true } +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +proc-macro = true + +[dependencies] +error-stack = { workspace = true } +quote = "1.0.33" +report = { workspace = true } +syn = "2.0.29" +thiserror = { workspace = true } + +[dev-dependencies] +axelar-wasm-std = { workspace = true } + +[lints] +workspace = true diff --git a/packages/into-inner-derive/release.toml b/packages/into-inner-derive/release.toml new file mode 100644 index 000000000..954564097 --- /dev/null +++ b/packages/into-inner-derive/release.toml @@ -0,0 +1 @@ +release = false diff --git a/packages/into-inner-derive/src/lib.rs b/packages/into-inner-derive/src/lib.rs new file mode 100644 index 000000000..6a108e74e --- /dev/null +++ b/packages/into-inner-derive/src/lib.rs @@ -0,0 +1,31 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::{DeriveInput, Fields}; + +#[proc_macro_derive(IntoInner)] +pub fn into_inner_derive(input: TokenStream) -> TokenStream { + let ast: DeriveInput = syn::parse(input).unwrap(); + + let name = &ast.ident; + let ty = match ast.data { + syn::Data::Struct(val) => match val.fields { + Fields::Unnamed(fields) if fields.unnamed.len() == 1 => { + fields.unnamed.first().map(|field| field.ty.to_owned()) + } + _ => None, + }, + _ => None, + }; + + match ty { + Some(ty) => quote! { + impl #name { + pub fn into_inner(self) -> #ty { + self.0 + } + } + } + .into(), + None => panic!("only newtype structs are supported"), + } +} diff --git a/packages/into-inner-derive/tests/derive.rs b/packages/into-inner-derive/tests/derive.rs new file mode 100644 index 000000000..52e9f99d7 --- /dev/null +++ b/packages/into-inner-derive/tests/derive.rs @@ -0,0 +1,11 @@ +use into_inner_derive::IntoInner; + +#[derive(IntoInner)] +struct Test(u128); + +#[test] +fn can_into_inner() { + let expected_inner = 32; + let actual_inner = Test(expected_inner).into_inner(); + assert_eq!(actual_inner, expected_inner); +} diff --git a/packages/msgs-derive/Cargo.toml b/packages/msgs-derive/Cargo.toml new file mode 100644 index 000000000..fcc1171f6 --- /dev/null +++ b/packages/msgs-derive/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "msgs-derive" +version = "1.0.0" +edition = { workspace = true } +rust-version = { workspace = true } + +[lib] +proc-macro = true + +[dependencies] +axelar-wasm-std = { workspace = true } +cosmwasm-std = { workspace = true } +error-stack = { workspace = true } +itertools = { workspace = true } +proc-macro2 = "1.0.85" +quote = { workspace = true } +syn = { workspace = true } + +[dev-dependencies] + +[lints] +workspace = true diff --git a/packages/msgs-derive/release.toml b/packages/msgs-derive/release.toml new file mode 100644 index 000000000..954564097 --- /dev/null +++ b/packages/msgs-derive/release.toml @@ -0,0 +1 @@ +release = false diff --git a/packages/msgs-derive/src/lib.rs b/packages/msgs-derive/src/lib.rs new file mode 100644 index 000000000..10b488f02 --- /dev/null +++ b/packages/msgs-derive/src/lib.rs @@ -0,0 +1,359 @@ +use axelar_wasm_std::permission_control::Permission; +use itertools::Itertools; +use proc_macro::TokenStream; +use quote::{format_ident, quote}; +use syn::punctuated::Punctuated; +use syn::token::Comma; +use syn::{Data, DataEnum, DeriveInput, Expr, ExprCall, Ident, Path, Token, Variant}; + +/// This macro derives the `ensure_permissions` method for an enum. The method checks if the sender +/// has the required permissions to execute the variant. The permissions are defined using the +/// `#[permission]` attribute. The attribute can be used in two ways: +/// - `#[permission(Permission1, Permission2, ...)]` requires the sender to have at least one of +/// the specified permissions. These permissions are defined in the [axelar_wasm_std::permission_control::Permission] enum. +/// - `#[permission(Specific(Addr1, Addr2, ...))]` requires the sender to be one of the specified +/// addresses. The macro will generate a function signature that takes closures as arguments to determine +/// the whitelisted addresses. +/// +/// Both attributes can be used together, in which case the sender must have at least one of the +/// specified permissions or be one of the specified addresses. +/// The `ensure_permissions` method will return an error if the sender does not have the required +/// permissions. +/// +/// # Example +/// ``` +/// use cosmwasm_std::{Addr, Deps, Env, MessageInfo}; +/// use axelar_wasm_std::permission_control::Permission; +/// use msgs_derive::EnsurePermissions; +/// +/// #[derive(EnsurePermissions)] +/// pub enum ExecuteMsg { +/// #[permission(NoPrivilege, Admin)] +/// AnyoneButGovernanceCanCallThis, +/// #[permission(Governance)] +/// OnlyGovernanceCanCallThis, +/// #[permission(Admin, Specific(gateway))] +/// AdminOrGatewayCanCallThis, +/// #[permission(Specific(gateway))] +/// OnlyGatewayCanCallThis +/// } +/// +/// fn execute(deps: Deps, env: Env, info: MessageInfo, msg: ExecuteMsg) -> error_stack::Result<(), axelar_wasm_std::permission_control::Error> { +/// // check permissions before handling the message +/// match msg.ensure_permissions(deps.storage, &info.sender, |storage, message | GATEWAY.load(storage))? { +/// ExecuteMsg::AnyoneButGovernanceCanCallThis => Ok(()), +/// ExecuteMsg::OnlyGovernanceCanCallThis => Ok(()), +/// ExecuteMsg::AdminOrGatewayCanCallThis => Ok(()), +/// ExecuteMsg::OnlyGatewayCanCallThis => Ok(()), +/// } +/// } +/// +/// # // mock to make the example compile +/// # struct Store; +/// # impl Store { +/// # fn load(&self, storage: &dyn cosmwasm_std::Storage) -> error_stack::Result { +/// # Ok(Addr::unchecked("gateway")) +/// # } +/// # } +/// # const GATEWAY:Store = Store; +/// # use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; +/// # fn main() { +/// # let mocks = mock_dependencies(); +/// # let deps = mocks.as_ref(); +/// # let env = mock_env(); +/// // example how to call the execute function +/// let info = MessageInfo{ +/// sender: Addr::unchecked("sender"), +/// funds: vec![], +/// }; +/// +/// # let info_root = info; +/// # let info = info_root.clone(); +/// assert!(execute(deps, env, info, ExecuteMsg::AnyoneButGovernanceCanCallThis).is_ok()); +/// # let env = mock_env(); +/// # let info = info_root.clone(); +/// assert!(execute(deps, env, info, ExecuteMsg::OnlyGatewayCanCallThis).is_err()); +/// # } +/// ``` + +#[proc_macro_derive(EnsurePermissions, attributes(permission))] +pub fn derive_ensure_permissions(input: TokenStream) -> TokenStream { + let input = syn::parse_macro_input!(input as DeriveInput); + let ident = input.ident.clone(); + + match input.data.clone() { + Data::Enum(data) => build_implementation(ident, data), + _ => panic!("Only enums are supported"), + } +} +fn build_implementation(enum_type: Ident, data: DataEnum) -> TokenStream { + let (variants, permissions): (Vec<_>, Vec<_>) = data + .variants + .into_iter() + .filter_map(find_permissions) + .unzip(); + + let specific_check = build_specific_permissions_check(&enum_type, &variants, &permissions); + let general_check = build_general_permissions_check(&enum_type, &variants, &permissions); + let check_function = build_full_check_function(&permissions, specific_check, general_check); + + TokenStream::from(quote! { + impl #enum_type{ + #check_function + } + }) +} + +#[derive(Debug)] +struct MsgPermissions { + specific: Vec, + general: Vec, +} + +fn find_permissions(variant: Variant) -> Option<(Ident, MsgPermissions)> { + let (specific, general): (Vec<_>, Vec<_>) = variant + .attrs + .iter() + .filter(|attr| attr.path().is_ident("permission")) + .flat_map(|attr| + { + match attr. + parse_args_with(Punctuated::::parse_terminated){ + Ok(expr) => expr, + _=> panic!("wrong format of 'permission' attribute for variant {}", variant.ident) + } + }) + .map(|expr| match expr { + Expr::Path(path) => (None, Some(path.path)), + Expr::Call(ExprCall { args, func, .. }) => { + let paths = parse_specific_permissions(&variant, args); + if !is_specific_attribute(&func) { + panic!( + "unrecognized permission attribute for variant {}, suggestion: 'Specific(...)'?", + variant.ident + ); + } + (Some(paths), None) + } + expr => + panic!( + "unrecognized permission attribute '{}' for variant {}", + quote! {#expr}, variant.ident + ) + }) + .unzip(); + + let specific: Vec = specific.into_iter().flatten().flatten().collect(); + let general: Vec = general.into_iter().flatten().collect(); + + if !general.iter().all_unique() { + panic!("permissions for variant {} must be unique", variant.ident); + } + + if !specific.iter().all_unique() { + panic!( + "whitelisted addresses for variant {} must be unique", + variant.ident + ); + } + + if general.is_empty() && specific.is_empty() { + panic!( + "permissions for variant {} must not be empty", + variant.ident + ); + } + + if general.iter().any(is_permission_any) && !specific.is_empty() { + panic!( + "whitelisting addresses for variant {} is useless because permission '{:?}' is set", + variant.ident, + Permission::Any + ); + } + + Some((variant.ident, MsgPermissions { specific, general })) +} + +fn is_specific_attribute(func: &Expr) -> bool { + match func { + Expr::Path(path) => path.path.is_ident("Specific"), + _ => false, + } +} + +fn parse_specific_permissions( + variant: &Variant, + args: Punctuated, +) -> impl IntoIterator + '_ { + args.into_iter().map(|arg| match arg { + Expr::Path(path) => path.path, + _ => panic!("wrong format of 'Specific' permission attribute for variant {}, only comma separated identifiers are allowed", variant.ident), + }) +} + +fn is_permission_any(path: &Path) -> bool { + path.get_ident() + .filter(|ident| ident.to_string() == format!("{:?}", Permission::Any)) + .is_some() +} + +fn build_specific_permissions_check( + enum_type: &Ident, + variants: &[Ident], + permissions: &[MsgPermissions], +) -> proc_macro2::TokenStream { + let specific_permissions = permissions.iter().map(|permission| { + let specific_permissions: &[_] = permission.specific.as_ref(); + + if permission.specific.is_empty() { + // don't do anything if there are no specific permissions + quote! {();} + } else { + // load all whitelisted addresses from storage and check if the sender is whitelisted + quote! { + #( + let stored_addr = error_stack::ResultExt::change_context( + #specific_permissions(storage, &self).map_err(|err| error_stack::Report::from(err)), + axelar_wasm_std::permission_control::Error::WhitelistNotFound{sender: sender.clone()})?; + if sender == stored_addr { + return Ok(self); + } + whitelisted.push(stored_addr); + )* + } + } + }); + + // map enum variants to specific permission checks + quote! { + let mut whitelisted = Vec::new(); + match self { + #(#enum_type::#variants {..}=> {#specific_permissions})* + }; + } +} + +fn build_general_permissions_check( + enum_type: &Ident, + variants: &[Ident], + permissions: &[MsgPermissions], +) -> proc_macro2::TokenStream { + let general_permissions_quote = permissions.iter().map(|permission| { + let general_permissions: &[_] = permission.general.as_ref(); + + if general_permissions.is_empty() && !permission.specific.is_empty() { + // getting to this point means the specific check has failed, so we return an error + quote! { + Err(axelar_wasm_std::permission_control::Error::AddressNotWhitelisted { + expected: whitelisted.clone(), + actual: sender.clone(), + }.into()) + } + } else { + // specific permissions have either failed or there were none, so check general permissions + quote! {Ok((#(axelar_wasm_std::permission_control::Permission::#general_permissions )|*).into())} + } + }); + + // map enum variants to general permission checks. Exclude checks for the 'Any' case, + // because it allows any address, compare permissions to the sender's role otherwise. + quote! { + let permission : Result, axelar_wasm_std::permission_control::Error > = match self { + #(#enum_type::#variants {..}=> {#general_permissions_quote})* + }; + + let permission = permission?; + + if permission.contains(axelar_wasm_std::permission_control::Permission::Any) { + return Ok(self); + } + + let role = error_stack::ResultExt::change_context( + axelar_wasm_std::permission_control::sender_role(storage, sender), + axelar_wasm_std::permission_control::Error::PermissionDenied { + expected: permission.clone(), + actual: axelar_wasm_std::permission_control::Permission::NoPrivilege.into(), + }, + )?; + + if (*permission & *role).is_empty() { + return Err(axelar_wasm_std::permission_control::Error::PermissionDenied { + expected: permission, + actual: role, + } + .into()); + } + + Ok(self) + } +} + +fn build_full_check_function( + permissions: &[MsgPermissions], + specific_permission_body: proc_macro2::TokenStream, + general_permission_body: proc_macro2::TokenStream, +) -> proc_macro2::TokenStream { + let unique_specific_permissions = permissions + .iter() + .flat_map(|permission| permission.specific.iter()) + .unique() + .collect::>(); + + let comments = quote! { + /// Ensure the annotated permissions are met by the sender. + /// If the sender does not have the required permissions, an error is returned. + }; + + // the function signature is different depending on how many specific permissions are defined + if unique_specific_permissions.is_empty() { + quote! { + #comments + /// # Arguments + /// * `storage` - The storage to load the sender's role from. + /// * `sender` - The sender's address to check for whitelisting. + pub fn ensure_permissions(self, storage: &dyn cosmwasm_std::Storage, sender: &cosmwasm_std::Addr) + -> error_stack::Result { + + #general_permission_body + } + } + } else { + // due to how rust handles closures, the easiest way to define functions as parameters is with independent generic types, + // one function with one return value for each specific permission + let fs: Vec<_> = (0..unique_specific_permissions.len()) + .map(|i| format_ident!("F{}", i)) + .collect(); + + let cs: Vec<_> = (0..unique_specific_permissions.len()) + .map(|i| format_ident!("C{}", i)) + .collect(); + + let args = quote!(#(#unique_specific_permissions),*); + let format = format!( + "* `{}` - The function(s) to load whitelisted addresses from storage.", + args + ); + quote! { + #comments + /// # Arguments + /// * `storage` - The storage to load the whitelisted addresses and the sender's role from. + /// * `sender` - The sender's address to check for whitelisting. + #[doc = #format] + pub fn ensure_permissions<#(#fs),*, #(#cs),*>( + self, + storage: &dyn cosmwasm_std::Storage, + sender: &cosmwasm_std::Addr, + #(#unique_specific_permissions: #fs),*) + -> error_stack::Result + where + #(#fs:FnOnce(&dyn cosmwasm_std::Storage, &Self) -> error_stack::Result),*, + #(#cs: error_stack::Context),* + { + #specific_permission_body + + #general_permission_body + } + } + } +} diff --git a/packages/msgs-derive/tests/macro.rs b/packages/msgs-derive/tests/macro.rs new file mode 100644 index 000000000..4d4da6c44 --- /dev/null +++ b/packages/msgs-derive/tests/macro.rs @@ -0,0 +1,322 @@ +use std::fmt::Display; + +use axelar_wasm_std::permission_control; +use cosmwasm_std::testing::MockStorage; +use cosmwasm_std::{Addr, Storage}; +use error_stack::{report, Report}; + +#[derive(msgs_derive::EnsurePermissions, Clone, Debug)] +#[allow(dead_code)] // the msg fields are only defined to make sure the derive attribute can handle fields correctly +enum TestMsg { + #[permission(NoPrivilege)] + NoPrivilege, + #[permission(Admin)] + Admin, + #[permission(Governance)] + Governance, + #[permission(Any)] + Any { test: u8 }, + #[permission(Elevated)] + Elevated(bool), + #[permission(Admin, NoPrivilege)] + Multi, +} + +#[derive(msgs_derive::EnsurePermissions, Clone, Debug)] +enum TestMsg2 { + #[permission(Any)] + Any, + #[permission(Specific(gateway1))] + Specific1, + #[permission(Elevated, Specific(gateway1))] + Specific2, + #[permission(Admin, Specific(gateway1), Specific(gateway2), NoPrivilege)] + Specific3, + #[permission(Specific(gateway1, gateway2, gateway3))] + Specific4, +} + +#[test] +fn test_general_ensure_permission() { + let no_privilege = Addr::unchecked("regular user"); + let admin = Addr::unchecked("admin"); + let governance = Addr::unchecked("governance"); + + let mut storage = MockStorage::new(); + permission_control::set_admin(&mut storage, &admin).unwrap(); + permission_control::set_governance(&mut storage, &governance).unwrap(); + + assert!(TestMsg::NoPrivilege + .ensure_permissions(&storage, &no_privilege) + .is_ok()); + assert!(matches!( + TestMsg::NoPrivilege + .ensure_permissions(&storage, &admin) + .unwrap_err() + .current_context(), + permission_control::Error::PermissionDenied { .. } + )); + assert!(matches!( + TestMsg::NoPrivilege + .ensure_permissions(&storage, &governance) + .unwrap_err() + .current_context(), + permission_control::Error::PermissionDenied { .. } + )); + + assert!(matches!( + TestMsg::Admin + .ensure_permissions(&storage, &no_privilege) + .unwrap_err() + .current_context(), + permission_control::Error::PermissionDenied { .. } + )); + assert!(TestMsg::Admin.ensure_permissions(&storage, &admin).is_ok()); + assert!(matches!( + TestMsg::Admin + .ensure_permissions(&storage, &governance) + .unwrap_err() + .current_context(), + permission_control::Error::PermissionDenied { .. } + )); + + assert!(matches!( + TestMsg::Governance + .ensure_permissions(&storage, &no_privilege) + .unwrap_err() + .current_context(), + permission_control::Error::PermissionDenied { .. } + )); + assert!(matches!( + TestMsg::Governance + .ensure_permissions(&storage, &admin) + .unwrap_err() + .current_context(), + permission_control::Error::PermissionDenied { .. } + )); + assert!(TestMsg::Governance + .ensure_permissions(&storage, &governance) + .is_ok()); + + assert!(TestMsg::Any { test: 0 } + .ensure_permissions(&storage, &no_privilege) + .is_ok()); + assert!(TestMsg::Any { test: 0 } + .ensure_permissions(&storage, &admin) + .is_ok()); + assert!(TestMsg::Any { test: 0 } + .ensure_permissions(&storage, &governance) + .is_ok()); + + assert!(matches!( + TestMsg::Elevated(true) + .ensure_permissions(&storage, &no_privilege) + .unwrap_err() + .current_context(), + permission_control::Error::PermissionDenied { .. } + )); + assert!(TestMsg::Elevated(true) + .ensure_permissions(&storage, &admin) + .is_ok()); + assert!(TestMsg::Elevated(true) + .ensure_permissions(&storage, &governance) + .is_ok()); + + assert!(TestMsg::Multi + .ensure_permissions(&storage, &no_privilege) + .is_ok()); + assert!(TestMsg::Multi.ensure_permissions(&storage, &admin).is_ok()); + assert!(matches!( + TestMsg::Multi + .ensure_permissions(&storage, &governance) + .unwrap_err() + .current_context(), + permission_control::Error::PermissionDenied { .. } + )); +} + +#[test] +fn ensure_specific_permissions() { + let no_privilege = Addr::unchecked("regular user"); + let admin = Addr::unchecked("admin"); + let governance = Addr::unchecked("governance"); + + let gateway1_addr = Addr::unchecked("gateway1"); + let gateway2_addr = Addr::unchecked("gateway2"); + let gateway3_addr = Addr::unchecked("gateway3"); + + let gateway1 = + |_: &dyn Storage, _: &TestMsg2| Ok::>(Addr::unchecked("gateway1")); + let gateway2 = + |_: &dyn Storage, _: &TestMsg2| Ok::>(Addr::unchecked("gateway2")); + let gateway3 = + |_: &dyn Storage, _: &TestMsg2| Ok::>(Addr::unchecked("gateway3")); + + let mut storage = MockStorage::new(); + permission_control::set_admin(&mut storage, &admin).unwrap(); + permission_control::set_governance(&mut storage, &governance).unwrap(); + + assert!(TestMsg2::Any + .ensure_permissions(&storage, &no_privilege, gateway1, gateway2, gateway3) + .is_ok()); + assert!(TestMsg2::Any + .ensure_permissions(&storage, &admin, gateway1, gateway2, gateway3) + .is_ok()); + assert!(TestMsg2::Any + .ensure_permissions(&storage, &governance, gateway1, gateway2, gateway3) + .is_ok()); + assert!(TestMsg2::Any + .ensure_permissions(&storage, &gateway1_addr, gateway1, gateway2, gateway3) + .is_ok()); + assert!(TestMsg2::Any + .ensure_permissions(&storage, &gateway2_addr, gateway1, gateway2, gateway3) + .is_ok()); + assert!(TestMsg2::Any + .ensure_permissions(&storage, &gateway3_addr, gateway1, gateway2, gateway3) + .is_ok()); + + assert!(matches!( + TestMsg2::Specific1 + .ensure_permissions(&storage, &no_privilege, gateway1, gateway2, gateway3) + .unwrap_err() + .current_context(), + permission_control::Error::AddressNotWhitelisted { .. } + )); + assert!(matches!( + TestMsg2::Specific1 + .ensure_permissions(&storage, &admin, gateway1, gateway2, gateway3) + .unwrap_err() + .current_context(), + permission_control::Error::AddressNotWhitelisted { .. } + )); + assert!(matches!( + TestMsg2::Specific1 + .ensure_permissions(&storage, &governance, gateway1, gateway2, gateway3) + .unwrap_err() + .current_context(), + permission_control::Error::AddressNotWhitelisted { .. } + )); + assert!(TestMsg2::Specific1 + .ensure_permissions(&storage, &gateway1_addr, gateway1, gateway2, gateway3) + .is_ok()); + assert!(matches!( + TestMsg2::Specific1 + .ensure_permissions(&storage, &gateway2_addr, gateway1, gateway2, gateway3) + .unwrap_err() + .current_context(), + permission_control::Error::AddressNotWhitelisted { .. } + )); + assert!(matches!( + TestMsg2::Specific1 + .ensure_permissions(&storage, &gateway3_addr, gateway1, gateway2, gateway3) + .unwrap_err() + .current_context(), + permission_control::Error::AddressNotWhitelisted { .. } + )); + + assert!(matches!( + TestMsg2::Specific2 + .ensure_permissions(&storage, &no_privilege, gateway1, gateway2, gateway3) + .unwrap_err() + .current_context(), + permission_control::Error::PermissionDenied { .. } + )); + assert!(TestMsg2::Specific2 + .ensure_permissions(&storage, &admin, gateway1, gateway2, gateway3) + .is_ok()); + assert!(TestMsg2::Specific2 + .ensure_permissions(&storage, &governance, gateway1, gateway2, gateway3) + .is_ok()); + assert!(TestMsg2::Specific2 + .ensure_permissions(&storage, &gateway1_addr, gateway1, gateway2, gateway3) + .is_ok()); + assert!(matches!( + TestMsg2::Specific2 + .ensure_permissions(&storage, &gateway2_addr, gateway1, gateway2, gateway3) + .unwrap_err() + .current_context(), + permission_control::Error::PermissionDenied { .. } + )); + assert!(matches!( + TestMsg2::Specific2 + .ensure_permissions(&storage, &gateway3_addr, gateway1, gateway2, gateway3) + .unwrap_err() + .current_context(), + permission_control::Error::PermissionDenied { .. } + )); + + assert!(TestMsg2::Specific3 + .ensure_permissions(&storage, &no_privilege, gateway1, gateway2, gateway3) + .is_ok()); + assert!(TestMsg2::Specific3 + .ensure_permissions(&storage, &admin, gateway1, gateway2, gateway3) + .is_ok()); + assert!(matches!( + TestMsg2::Specific3 + .ensure_permissions(&storage, &governance, gateway1, gateway2, gateway3) + .unwrap_err() + .current_context(), + permission_control::Error::PermissionDenied { .. } + )); + assert!(TestMsg2::Specific3 + .ensure_permissions(&storage, &gateway1_addr, gateway1, gateway2, gateway3) + .is_ok()); + assert!(TestMsg2::Specific3 + .ensure_permissions(&storage, &gateway2_addr, gateway1, gateway2, gateway3) + .is_ok()); + assert!(TestMsg2::Specific3 + .ensure_permissions(&storage, &gateway3_addr, gateway1, gateway2, gateway3) + .is_ok()); // because of NoPrivilege + + assert!(matches!( + TestMsg2::Specific4 + .ensure_permissions(&storage, &no_privilege, gateway1, gateway2, gateway3) + .unwrap_err() + .current_context(), + permission_control::Error::AddressNotWhitelisted { .. } + )); + assert!(matches!( + TestMsg2::Specific4 + .ensure_permissions(&storage, &admin, gateway1, gateway2, gateway3) + .unwrap_err() + .current_context(), + permission_control::Error::AddressNotWhitelisted { .. } + )); + assert!(matches!( + TestMsg2::Specific4 + .ensure_permissions(&storage, &governance, gateway1, gateway2, gateway3) + .unwrap_err() + .current_context(), + permission_control::Error::AddressNotWhitelisted { .. } + )); + assert!(TestMsg2::Specific4 + .ensure_permissions(&storage, &gateway1_addr, gateway1, gateway2, gateway3) + .is_ok()); + assert!(TestMsg2::Specific4 + .ensure_permissions(&storage, &gateway2_addr, gateway1, gateway2, gateway3) + .is_ok()); + assert!(TestMsg2::Specific4 + .ensure_permissions(&storage, &gateway3_addr, gateway1, gateway2, gateway3) + .is_ok()); + + let gateway3 = |_: &dyn Storage, _: &TestMsg2| Err(report!(Error)); + + assert!(matches!( + TestMsg2::Specific4 + .ensure_permissions(&storage, &gateway3_addr, gateway1, gateway2, gateway3) + .unwrap_err() + .current_context(), + permission_control::Error::WhitelistNotFound { .. } + )); +} + +#[derive(Debug)] +struct Error; + +impl std::error::Error for Error {} + +impl Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "error") + } +} diff --git a/packages/report/Cargo.toml b/packages/report/Cargo.toml index 65f56e40b..8a1a94fbb 100644 --- a/packages/report/Cargo.toml +++ b/packages/report/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "report" -version = "0.1.0" +version = "1.0.0" rust-version = { workspace = true } -edition = "2021" +edition = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/packages/report/src/loggable.rs b/packages/report/src/loggable.rs index 35578938d..070bbd4fa 100644 --- a/packages/report/src/loggable.rs +++ b/packages/report/src/loggable.rs @@ -81,7 +81,10 @@ impl From<&Report> for LoggableError { // because of the stack order of attachments we need to reverse them to get the historical order attachments.reverse(); error.attachments = attachments; - errors.push(error) + + if !error.msg.is_empty() || !error.attachments.is_empty() { + errors.push(error) + } } chain_causes(errors).expect("a report must have at least one error") @@ -150,11 +153,13 @@ impl<'a> From<&'a Frame> for FrameType<'a> { #[cfg(test)] mod tests { - use crate::LoggableError; - use error_stack::Report; use std::env; + + use error_stack::Report; use thiserror::Error; + use crate::LoggableError; + #[derive(Error, Debug)] enum Error { #[error("{0}")] diff --git a/packages/router-api/Cargo.toml b/packages/router-api/Cargo.toml index 08286ad7d..dc48843f0 100644 --- a/packages/router-api/Cargo.toml +++ b/packages/router-api/Cargo.toml @@ -1,18 +1,18 @@ [package] name = "router-api" -version = "0.1.0" +version = "1.0.0" rust-version = { workspace = true } -edition = "2021" +edition = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -axelar-wasm-std = { workspace = true } -axelar-wasm-std-derive = { workspace = true } +axelar-wasm-std = { workspace = true, features = ["derive"] } cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } cw-storage-plus = { workspace = true } error-stack = { workspace = true } flagset = { version = "0.4.3", features = ["serde"] } +msgs-derive = { workspace = true } report = { workspace = true } schemars = { workspace = true } serde = { workspace = true } diff --git a/packages/router-api/src/client.rs b/packages/router-api/src/client.rs index e756a318d..6b3182edb 100644 --- a/packages/router-api/src/client.rs +++ b/packages/router-api/src/client.rs @@ -1,3 +1,4 @@ +use axelar_wasm_std::vec::VecExt; use cosmwasm_std::{to_json_binary, Addr, WasmMsg}; use crate::msg::ExecuteMsg; @@ -17,15 +18,7 @@ impl Router { } pub fn route(&self, msgs: Vec) -> Option { - ignore_empty(msgs).map(|msgs| self.execute(&ExecuteMsg::RouteMessages(msgs))) - } -} - -// TODO: unify across contract clients -fn ignore_empty(msgs: Vec) -> Option> { - if msgs.is_empty() { - None - } else { - Some(msgs) + msgs.to_none_if_empty() + .map(|msgs| self.execute(&ExecuteMsg::RouteMessages(msgs))) } } diff --git a/packages/router-api/src/error.rs b/packages/router-api/src/error.rs index 72167a0bf..4347cfacf 100644 --- a/packages/router-api/src/error.rs +++ b/packages/router-api/src/error.rs @@ -1,4 +1,4 @@ -use axelar_wasm_std_derive::IntoContractError; +use axelar_wasm_std::IntoContractError; use cosmwasm_std::StdError; use thiserror::Error; @@ -14,8 +14,8 @@ pub enum Error { #[error(transparent)] Std(#[from] StdError), - #[error("caller is not authorized")] - Unauthorized, + #[error("amplifier routing is disabled")] + RoutingDisabled, #[error("chain already exists")] ChainAlreadyExists, diff --git a/packages/router-api/src/msg.rs b/packages/router-api/src/msg.rs index a075a1aff..05cbe8bce 100644 --- a/packages/router-api/src/msg.rs +++ b/packages/router-api/src/msg.rs @@ -1,47 +1,49 @@ +use std::collections::HashMap; + use axelar_wasm_std::msg_id::MessageIdFormat; use cosmwasm_schema::{cw_serde, QueryResponses}; +use msgs_derive::EnsurePermissions; use crate::primitives::*; #[cw_serde] +#[derive(EnsurePermissions)] pub enum ExecuteMsg { - /* - * Governance Methods - * All the below messages should only be called by governance - */ - // Registers a new chain with the router + /// Registers a new chain with the router + #[permission(Governance)] RegisterChain { chain: ChainName, gateway_address: Address, msg_id_format: MessageIdFormat, }, - // Changes the gateway address associated with a particular chain + /// Changes the gateway address associated with a particular chain + #[permission(Governance)] UpgradeGateway { chain: ChainName, contract_address: Address, }, - - /* - * Router Admin Methods - * All the below messages should only be called by the router admin - */ - // Freezes a chain, in the specified direction. - FreezeChain { - chain: ChainName, - direction: GatewayDirection, + /// Freezes the specified chains in the specified directions. + #[permission(Elevated)] + FreezeChains { + chains: HashMap, }, - // Unfreezes a chain, in the specified direction. - UnfreezeChain { - chain: ChainName, - direction: GatewayDirection, + /// Unfreezes the specified chains in the specified directions. + #[permission(Elevated)] + UnfreezeChains { + chains: HashMap, }, - /* - * Gateway Messages - * The below messages can only be called by registered gateways - */ - // Routes a message to all outgoing gateways registered to the destination domain. - // Called by an incoming gateway + /// Emergency command to stop all amplifier routing. + #[permission(Elevated)] + DisableRouting, + + /// Resumes routing after an emergency shutdown. + #[permission(Elevated)] + EnableRouting, + + /// Routes a message to all outgoing gateways registered to the destination domain. + /// Called by an incoming gateway + #[permission(Specific(gateway))] RouteMessages(Vec), } @@ -49,7 +51,7 @@ pub enum ExecuteMsg { #[derive(QueryResponses)] pub enum QueryMsg { #[returns(ChainEndpoint)] - GetChainInfo(ChainName), + ChainInfo(ChainName), // Returns a list of chains registered with the router // The list is paginated by: @@ -60,4 +62,6 @@ pub enum QueryMsg { start_after: Option, limit: Option, }, + #[returns(bool)] + IsEnabled, } diff --git a/packages/router-api/src/primitives.rs b/packages/router-api/src/primitives.rs index 724d11404..8bc5638b1 100644 --- a/packages/router-api/src/primitives.rs +++ b/packages/router-api/src/primitives.rs @@ -1,14 +1,17 @@ -use std::{any::type_name, fmt, ops::Deref, str::FromStr}; +use std::any::type_name; +use std::fmt; +use std::fmt::Display; +use std::ops::Deref; +use std::str::FromStr; use axelar_wasm_std::flagset::FlagSet; +use axelar_wasm_std::hash::Hash; use axelar_wasm_std::msg_id::MessageIdFormat; -use axelar_wasm_std::{hash::Hash, nonempty, FnExt}; +use axelar_wasm_std::{nonempty, FnExt}; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, Attribute, HexBinary}; -use cosmwasm_std::{StdError, StdResult}; +use cosmwasm_std::{Addr, Attribute, HexBinary, StdError, StdResult}; use cw_storage_plus::{Key, KeyDeserialize, Prefixer, PrimaryKey}; -use error_stack::Report; -use error_stack::ResultExt; +use error_stack::{Context, Report, ResultExt}; use flagset::flags; use schemars::gen::SchemaGenerator; use schemars::schema::Schema; @@ -19,7 +22,9 @@ use valuable::Valuable; use crate::error::*; -pub const CHAIN_NAME_DELIMITER: char = '_'; +/// Delimiter used when concatenating fields to prevent ambiguous encodings. +/// The delimiter must be prevented from being contained in values that are used as fields. +pub const FIELD_DELIMITER: char = '_'; #[cw_serde] #[derive(Eq, Hash)] @@ -38,11 +43,18 @@ pub struct Message { impl Message { pub fn hash(&self) -> Hash { let mut hasher = Keccak256::new(); + let delimiter_bytes = &[FIELD_DELIMITER as u8]; + hasher.update(self.cc_id.to_string()); + hasher.update(delimiter_bytes); hasher.update(self.source_address.as_str()); + hasher.update(delimiter_bytes); hasher.update(self.destination_chain.as_ref()); + hasher.update(delimiter_bytes); hasher.update(self.destination_address.as_str()); + hasher.update(delimiter_bytes); hasher.update(self.payload_hash); + hasher.finalize().into() } } @@ -50,11 +62,11 @@ impl Message { impl From for Vec { fn from(other: Message) -> Self { vec![ - ("id", other.cc_id.id).into(), - ("source_chain", other.cc_id.chain).into(), - ("source_addresses", other.source_address.deref()).into(), + ("message_id", other.cc_id.message_id).into(), + ("source_chain", other.cc_id.source_chain).into(), + ("source_address", other.source_address.deref()).into(), ("destination_chain", other.destination_chain).into(), - ("destination_addresses", other.destination_address.deref()).into(), + ("destination_address", other.destination_address.deref()).into(), ( "payload_hash", HexBinary::from(other.payload_hash).to_string(), @@ -65,6 +77,7 @@ impl From for Vec { } #[cw_serde] +#[serde(try_from = "String")] #[derive(Eq, Hash)] pub struct Address(nonempty::String); @@ -88,6 +101,10 @@ impl TryFrom for Address { type Error = Report; fn try_from(value: String) -> Result { + if value.contains(FIELD_DELIMITER) { + return Err(Report::new(Error::InvalidAddress)); + } + Ok(Address( value .parse::() @@ -99,47 +116,35 @@ impl TryFrom for Address { #[cw_serde] #[derive(Eq, Hash)] pub struct CrossChainId { - pub chain: ChainName, - pub id: nonempty::String, + pub source_chain: ChainNameRaw, + pub message_id: nonempty::String, } -/// todo: remove this when state::NewMessage is used -impl FromStr for CrossChainId { - type Err = Error; - - fn from_str(s: &str) -> Result { - let parts = s.split_once(CHAIN_NAME_DELIMITER); - let (chain, id) = parts - .map(|(chain, id)| { - ( - chain.parse::(), - id.parse::() - .map_err(|_| Error::InvalidMessageId), - ) - }) - .ok_or(Error::InvalidMessageId)?; +impl CrossChainId { + pub fn new( + chain: impl TryInto, + id: impl TryInto, + ) -> error_stack::Result + where + S: Context, + T: Context, + { Ok(CrossChainId { - chain: chain?, - id: id?, + source_chain: chain.try_into().change_context(Error::InvalidChainName)?, + message_id: id.try_into().change_context(Error::InvalidMessageId)?, }) } } -impl fmt::Display for CrossChainId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}{}{}", self.chain, CHAIN_NAME_DELIMITER, *self.id) - } -} - impl PrimaryKey<'_> for CrossChainId { - type Prefix = ChainName; + type Prefix = ChainNameRaw; type SubPrefix = (); type Suffix = String; - type SuperSuffix = (ChainName, String); + type SuperSuffix = (ChainNameRaw, String); fn key(&self) -> Vec { - let mut keys = self.chain.key(); - keys.extend(self.id.key()); + let mut keys = self.source_chain.key(); + keys.extend(self.message_id.key()); keys } } @@ -148,16 +153,32 @@ impl KeyDeserialize for CrossChainId { type Output = Self; fn from_vec(value: Vec) -> StdResult { - let (chain, id) = <(ChainName, String)>::from_vec(value)?; + let (source_chain, id) = <(ChainNameRaw, String)>::from_vec(value)?; Ok(CrossChainId { - chain, - id: id + source_chain, + message_id: id .try_into() .map_err(|err| StdError::parse_err(type_name::(), err))?, }) } } +impl Display for CrossChainId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}{}{}", + self.source_chain, FIELD_DELIMITER, *self.message_id + ) + } +} +/// ChainName represents the identifier used for chains in Amplifier. +/// Certain restrictions apply on the string: +/// - Must not be empty +/// - Must not exceed `ChainName::MAX_LEN` bytes +/// - Only ASCII characters are allowed +/// - Must not contain the `FIELD_DELIMITER` character +/// - The string is lowercased #[cw_serde] #[serde(try_from = "String")] #[derive(Eq, Hash, Valuable)] @@ -167,11 +188,9 @@ impl FromStr for ChainName { type Err = Error; fn from_str(s: &str) -> Result { - if s.contains(CHAIN_NAME_DELIMITER) || s.is_empty() { - return Err(Error::InvalidChainName); - } + let chain_name: ChainNameRaw = s.parse()?; - Ok(ChainName(s.to_lowercase())) + Ok(ChainName(chain_name.0.to_lowercase())) } } @@ -189,15 +208,35 @@ impl TryFrom for ChainName { } } -impl fmt::Display for ChainName { +impl TryFrom<&str> for ChainName { + type Error = Error; + + fn try_from(value: &str) -> Result { + value.parse() + } +} + +impl Display for ChainName { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } +impl PartialEq for ChainName { + fn eq(&self, other: &ChainNameRaw) -> bool { + self == &other.as_ref() + } +} + impl PartialEq for ChainName { fn eq(&self, other: &String) -> bool { - self.0 == other.to_lowercase() + self == &other.as_str() + } +} + +impl PartialEq<&str> for ChainName { + fn eq(&self, other: &&str) -> bool { + self.0 == *other } } @@ -245,6 +284,134 @@ impl AsRef for ChainName { } } +/// ChainNameRaw represents the raw case-sensitive identifier used for source chains in Amplifier. +/// It is backwards compatible with case-sensitive chain names used in axelar-core (e.g. `Ethereum`). +/// +/// Certain restrictions apply on the string: +/// - Must not be empty +/// - Must not exceed `ChainNameRaw::MAX_LEN` bytes +/// - Only ASCII characters are allowed +/// - Must not contain the `FIELD_DELIMITER` character +/// - Case-sensitivity is preserved +#[cw_serde] +#[serde(try_from = "String")] +#[derive(Eq, Hash)] +pub struct ChainNameRaw(String); + +impl From for ChainNameRaw { + fn from(other: ChainName) -> Self { + ChainNameRaw(other.0) + } +} + +impl ChainNameRaw { + /// Maximum length of a chain name (in bytes). + /// This MUST NOT be changed without a corresponding change to the ChainName validation in axelar-core. + const MAX_LEN: usize = 20; +} + +impl FromStr for ChainNameRaw { + type Err = Error; + + fn from_str(s: &str) -> Result { + if s.is_empty() || s.len() > Self::MAX_LEN || !s.is_ascii() || s.contains(FIELD_DELIMITER) { + return Err(Error::InvalidChainName); + } + + Ok(ChainNameRaw(s.to_owned())) + } +} + +impl From for String { + fn from(d: ChainNameRaw) -> Self { + d.0 + } +} + +impl TryFrom for ChainNameRaw { + type Error = Error; + + fn try_from(value: String) -> Result { + value.parse() + } +} + +impl TryFrom<&str> for ChainNameRaw { + type Error = Error; + + fn try_from(value: &str) -> Result { + value.parse() + } +} + +impl Display for ChainNameRaw { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl AsRef for ChainNameRaw { + fn as_ref(&self) -> &str { + self.0.as_str() + } +} + +impl PartialEq for ChainNameRaw { + fn eq(&self, other: &ChainName) -> bool { + self == &other.as_ref() + } +} + +impl PartialEq for ChainNameRaw { + fn eq(&self, other: &String) -> bool { + self == &other.as_str() + } +} + +impl PartialEq<&str> for ChainNameRaw { + fn eq(&self, other: &&str) -> bool { + self.0 == *other + } +} + +impl<'a> PrimaryKey<'a> for ChainNameRaw { + type Prefix = (); + type SubPrefix = (); + type Suffix = Self; + type SuperSuffix = Self; + + fn key(&self) -> Vec { + vec![Key::Ref(self.0.as_bytes())] + } +} + +impl<'a> Prefixer<'a> for ChainNameRaw { + fn prefix(&self) -> Vec { + vec![Key::Ref(self.0.as_bytes())] + } +} + +impl KeyDeserialize for ChainNameRaw { + type Output = Self; + + #[inline(always)] + fn from_vec(value: Vec) -> StdResult { + String::from_utf8(value) + .map_err(StdError::invalid_utf8)? + .then(ChainNameRaw::try_from) + .map_err(StdError::invalid_utf8) + } +} + +impl KeyDeserialize for &ChainNameRaw { + type Output = ChainNameRaw; + + #[inline(always)] + fn from_vec(value: Vec) -> StdResult { + ChainNameRaw::from_vec(value) + } +} + flags! { #[repr(u8)] #[derive(Deserialize, Serialize, Hash)] @@ -291,19 +458,19 @@ impl ChainEndpoint { #[cfg(test)] mod tests { - use super::*; - use cosmwasm_std::to_json_vec; use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; use sha3::{Digest, Sha3_256}; + use super::*; + #[test] // Any modifications to the Message struct fields or their types // will cause this test to fail, indicating that a migration is needed. fn test_message_struct_unchanged() { let expected_message_hash = - "e8052da3a89c90468cc6e4e242a827f8579fb0ea8e298b1650d73a0f7e81abc3"; + "3a0edbeb590d12cf9f71864469d9e7afd52cccf2798db09c55def296af3a8e89"; let msg = dummy_message(); @@ -317,7 +484,7 @@ mod tests { #[test] fn hash_id_unchanged() { let expected_message_hash = - "d30a374a795454706b43259998aafa741267ecbc8b6d5771be8d7b8c9a9db263"; + "e6b9cc9b6962c997b44ded605ebfb4f861e2db2ddff7e8be84a7a79728cea61e"; let msg = dummy_message(); @@ -325,69 +492,209 @@ mod tests { } #[test] - fn should_fail_to_parse_invalid_chain_name() { - // empty + fn should_not_deserialize_invalid_chain_name() { assert_eq!( - "".parse::().unwrap_err(), - Error::InvalidChainName + "chain name is invalid", + serde_json::from_str::("\"\"") + .unwrap_err() + .to_string() ); - // name contains id separator assert_eq!( - format!("chain {CHAIN_NAME_DELIMITER}") - .parse::() - .unwrap_err(), - Error::InvalidChainName + "chain name is invalid", + serde_json::from_str::(format!("\"chain{FIELD_DELIMITER}\"").as_str()) + .unwrap_err() + .to_string() ); } #[test] - fn should_parse_to_case_insensitive_chain_name() { - let rand_str: String = thread_rng() - .sample_iter(&Alphanumeric) - .take(10) - .map(char::from) - .collect(); - - let chain_name: ChainName = rand_str.parse().unwrap(); - - assert_eq!( - chain_name, - rand_str.to_lowercase().parse::().unwrap() - ); - assert_eq!( - chain_name, - rand_str.to_uppercase().parse::().unwrap() - ); + fn ensure_chain_name_parsing_respect_restrictions() { + struct TestCase<'a> { + input: &'a str, + can_parse: bool, + is_normalized: bool, + } + let random_lower = random_chain_name().to_lowercase(); + let random_upper = random_chain_name().to_uppercase(); + + let test_cases = [ + TestCase { + input: "", + can_parse: false, + is_normalized: false, + }, + TestCase { + input: "chain_with_prohibited_symbols", + can_parse: false, + is_normalized: false, + }, + TestCase { + input: "!@#$%^&*()+=-", + can_parse: true, + is_normalized: true, + }, + TestCase { + input: "1234567890", + can_parse: true, + is_normalized: true, + }, + TestCase { + input: "ethereum", + can_parse: true, + is_normalized: true, + }, + TestCase { + input: "ETHEREUM", + can_parse: true, + is_normalized: false, + }, + TestCase { + input: "ethereum-1", + can_parse: true, + is_normalized: true, + }, + TestCase { + input: "ETHEREUM-1", + can_parse: true, + is_normalized: false, + }, + TestCase { + input: "long chain name over max len", + can_parse: false, + is_normalized: false, + }, + TestCase { + input: "UTF-8 ❤", + can_parse: false, + is_normalized: false, + }, + TestCase { + input: random_lower.as_str(), + can_parse: true, + is_normalized: true, + }, + TestCase { + input: random_upper.as_str(), + can_parse: true, + is_normalized: false, + }, + ]; + + let conversions = [ + |input: &str| ChainName::from_str(input), + |input: &str| ChainName::try_from(input), + |input: &str| ChainName::try_from(input.to_string()), + ]; + + let raw_conversions = [ + |input: &str| ChainNameRaw::from_str(input), + |input: &str| ChainNameRaw::try_from(input), + |input: &str| ChainNameRaw::try_from(input.to_string()), + ]; + + for case in test_cases.into_iter() { + for conversion in conversions.into_iter() { + let result = conversion(case.input); + assert_eq!(result.is_ok(), case.can_parse, "input: {}", case.input); + if case.can_parse { + if case.is_normalized { + assert_eq!(result.unwrap(), case.input); + } else { + assert_ne!(result.unwrap(), case.input); + } + } + } + + for conversion in raw_conversions.into_iter() { + let result = conversion(case.input); + assert_eq!(result.is_ok(), case.can_parse, "input: {}", case.input); + if case.can_parse { + assert_eq!(result.unwrap(), case.input); + } + } + } } #[test] - fn should_not_deserialize_invalid_chain_name() { + fn should_not_deserialize_invalid_address() { assert_eq!( - "chain name is invalid", - serde_json::from_str::("\"\"") + "address is invalid", + serde_json::from_str::

("\"\"") .unwrap_err() .to_string() ); assert_eq!( - "chain name is invalid", - serde_json::from_str::(format!("\"chain{CHAIN_NAME_DELIMITER}\"").as_str()) + "address is invalid", + serde_json::from_str::
(format!("\"address{FIELD_DELIMITER}\"").as_str()) .unwrap_err() .to_string() ); } #[test] - fn chain_name_case_insensitive_comparison() { - let chain_name = ChainName::from_str("ethereum").unwrap(); - - assert!(chain_name.eq(&"Ethereum".to_string())); - assert!(chain_name.eq(&"ETHEREUM".to_string())); - assert!(chain_name.eq(&"ethereum".to_string())); - assert!(chain_name.eq(&"ethEReum".to_string())); + fn ensure_address_parsing_respect_restrictions() { + struct TestCase<'a> { + input: &'a str, + can_parse: bool, + } + let random_lower = random_address().to_lowercase(); + let random_upper = random_address().to_uppercase(); - assert!(!chain_name.eq(&"Ethereum-1".to_string())); + let test_cases = [ + TestCase { + input: "", + can_parse: false, + }, + TestCase { + input: "address_with_prohibited_symbols", + can_parse: false, + }, + TestCase { + input: "!@#$%^&*()+=-1234567890", + can_parse: true, + }, + TestCase { + input: "0x4F4495243837681061C4743b74B3eEdf548D56A5", + can_parse: true, + }, + TestCase { + input: "0x4f4495243837681061c4743b74b3eedf548d56a5", + can_parse: true, + }, + TestCase { + input: "GARRAOPAA5MNY3Y5V2OOYXUMBC54UDHHJTUMLRQBY2DIZKT62G5WSJP4Copy", + can_parse: true, + }, + TestCase { + input: "ETHEREUM-1", + can_parse: true, + }, + TestCase { + input: random_lower.as_str(), + can_parse: true, + }, + TestCase { + input: random_upper.as_str(), + can_parse: true, + }, + ]; + + let conversions: [fn(&str) -> Result; 2] = [ + |input: &str| Address::from_str(input), + |input: &str| Address::try_from(input.to_string()), + ]; + + for case in test_cases.into_iter() { + for conversion in conversions.into_iter() { + let result = conversion(case.input); + assert_eq!(result.is_ok(), case.can_parse, "input: {}", case.input); + if case.can_parse { + assert_eq!(result.unwrap().to_string(), case.input); + } + } + } } #[test] @@ -402,14 +709,27 @@ mod tests { fn dummy_message() -> Message { Message { - cc_id: CrossChainId { - id: "hash-index".parse().unwrap(), - chain: "chain".parse().unwrap(), - }, - source_address: "source_address".parse().unwrap(), + cc_id: CrossChainId::new("chain", "hash-index").unwrap(), + source_address: "source-address".parse().unwrap(), destination_chain: "destination-chain".parse().unwrap(), - destination_address: "destination_address".parse().unwrap(), + destination_address: "destination-address".parse().unwrap(), payload_hash: [1; 32], } } + + fn random_chain_name() -> String { + thread_rng() + .sample_iter(&Alphanumeric) + .take(10) + .map(char::from) + .collect() + } + + fn random_address() -> String { + thread_rng() + .sample_iter(&Alphanumeric) + .take(10) + .map(char::from) + .collect() + } } diff --git a/packages/signature-verifier-api/Cargo.toml b/packages/signature-verifier-api/Cargo.toml index 1602a84e2..61acd6e60 100644 --- a/packages/signature-verifier-api/Cargo.toml +++ b/packages/signature-verifier-api/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "signature-verifier-api" -version = "0.1.0" +version = "1.0.0" rust-version = { workspace = true } -edition = "2021" +edition = { workspace = true } [dependencies] cosmwasm-schema = { workspace = true } diff --git a/packages/signature-verifier-api/release.toml b/packages/signature-verifier-api/release.toml new file mode 100644 index 000000000..954564097 --- /dev/null +++ b/packages/signature-verifier-api/release.toml @@ -0,0 +1 @@ +release = false diff --git a/packages/sui-gateway/Cargo.toml b/packages/sui-gateway/Cargo.toml new file mode 100644 index 000000000..b7cb5c160 --- /dev/null +++ b/packages/sui-gateway/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "sui-gateway" +version = "1.0.0" +edition = { workspace = true } +rust-version = { workspace = true } + +[dependencies] +bcs = { workspace = true } +cosmwasm-std = { workspace = true } +error-stack = { workspace = true } +hex = { workspace = true } +multisig = { workspace = true, features = ["library"] } +router-api = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +sha3 = { workspace = true } +sui-types = { workspace = true } +thiserror = { workspace = true } + +[lints] +workspace = true + +[dev-dependencies] +bs58 = "0.5.1" +rand = "0.8.5" diff --git a/packages/sui-gateway/release.toml b/packages/sui-gateway/release.toml new file mode 100644 index 000000000..954564097 --- /dev/null +++ b/packages/sui-gateway/release.toml @@ -0,0 +1 @@ +release = false diff --git a/packages/sui-gateway/src/error.rs b/packages/sui-gateway/src/error.rs new file mode 100644 index 000000000..4437a9540 --- /dev/null +++ b/packages/sui-gateway/src/error.rs @@ -0,0 +1,11 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum Error { + #[error("invalid address: {0}")] + InvalidAddress(String), + #[error("unsupported type of public key")] + UnsupportedPublicKey, + #[error("unsupported type of signature")] + UnsupportedSignature, +} diff --git a/packages/sui-gateway/src/events.rs b/packages/sui-gateway/src/events.rs new file mode 100644 index 000000000..2ef5d086a --- /dev/null +++ b/packages/sui-gateway/src/events.rs @@ -0,0 +1,41 @@ +use serde::{Deserialize, Serialize}; +use sui_types::SuiAddress; + +use super::{Bytes32, WeightedSigners}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct SignersRotated { + pub epoch: u64, + pub signers_hash: Bytes32, + pub signers: WeightedSigners, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct ContractCall { + pub source_id: SuiAddress, + pub destination_chain: String, + pub destination_address: String, + pub payload: Vec, + pub payload_hash: [u8; 32], +} + +#[cfg(test)] +mod tests { + use super::{ContractCall, SignersRotated}; + + #[test] + fn signers_rotated_should_serialize_and_deserialize_correct_bcs() { + let bcs_bytes = bs58::decode("7VmsFWvv3atGxVB1oUDZAx6nRWoE1xiWYxqLP6mcWD3Rf4wFjhfMwjKmxjXHyVc9ZTdCRdt25rUCWxCBRGEuFogKfgXkUXSkHPxirMeS4R8kJn1pgA7bkMwQywgFnrmd1ceeWNCCBQuUGr83Gx8mbyZeN7W2EAct14rKQ5zukNZH6mKv7n4dmBasFm8vT").into_vec().unwrap(); + + let event = bcs::from_bytes::(&bcs_bytes).unwrap(); + assert_eq!(bcs::to_bytes(&event).unwrap(), bcs_bytes); + } + + #[test] + fn contract_call_should_serialize_and_deserialize_correct_bcs() { + let bcs_bytes = bs58::decode("3Q9LRQe3KX2E3VNVjMLzWhDwSrNBB2Js9KPoW1SFuj2HVTgp9SXezEPFtqYhddwb8AvKQSaednVibTWz9upFy51px2nnwKgfNcwK2JDckvMENBdEtTnpKJnfizA2vC9qzxiPtE17ANnK629HzkpMqPKpvJjDueJ9zMw").into_vec().unwrap(); + + let event = bcs::from_bytes::(&bcs_bytes).unwrap(); + assert_eq!(bcs::to_bytes(&event).unwrap(), bcs_bytes); + } +} diff --git a/packages/sui-gateway/src/lib.rs b/packages/sui-gateway/src/lib.rs new file mode 100644 index 000000000..a0b2b7ff8 --- /dev/null +++ b/packages/sui-gateway/src/lib.rs @@ -0,0 +1,184 @@ +use std::str::FromStr; + +use cosmwasm_std::Uint256; +use error_stack::{report, Report, ResultExt}; +use multisig::key::PublicKey; +use multisig::msg::SignerWithSig; +use multisig::verifier_set::VerifierSet; +use serde::{Deserialize, Serialize}; +use sha3::{Digest, Keccak256}; +use sui_types::SuiAddress; + +pub mod error; +pub mod events; + +use error::Error; + +#[repr(u8)] +pub enum CommandType { + ApproveMessages = 0, + RotateSigners = 1, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Bytes32 { + bytes: [u8; 32], +} + +impl AsRef<[u8]> for Bytes32 { + fn as_ref(&self) -> &[u8] { + &self.bytes + } +} + +impl From<[u8; 32]> for Bytes32 { + fn from(bytes: [u8; 32]) -> Self { + Self { bytes } + } +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct WeightedSigner { + pub pub_key: Vec, + pub weight: u128, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct WeightedSigners { + pub signers: Vec, + pub threshold: u128, + pub nonce: Bytes32, +} + +impl TryFrom for WeightedSigners { + type Error = Report; + + fn try_from(verifier_set: VerifierSet) -> Result { + let mut signers = verifier_set + .signers + .values() + .map(|signer| match &signer.pub_key { + PublicKey::Ecdsa(key) => Ok(WeightedSigner { + pub_key: key.to_vec(), + weight: signer.weight.into(), + }), + PublicKey::Ed25519(_) => Err(Report::new(Error::UnsupportedPublicKey)), + }) + .collect::, _>>()?; + signers.sort_by(|signer1, signer2| signer1.pub_key.cmp(&signer2.pub_key)); + + let nonce = Uint256::from(verifier_set.created_at).to_be_bytes().into(); + + Ok(Self { + signers, + threshold: verifier_set.threshold.into(), + nonce, + }) + } +} + +impl WeightedSigners { + pub fn hash(&self) -> [u8; 32] { + let hash = + Keccak256::digest(bcs::to_bytes(&self).expect("failed to serialize WeightedSigners")); + + hash.into() + } +} + +#[derive(Serialize)] +pub struct MessageToSign { + pub domain_separator: Bytes32, + pub signers_hash: Bytes32, + pub data_hash: Bytes32, +} + +impl MessageToSign { + pub fn hash(&self) -> [u8; 32] { + let hash = + Keccak256::digest(bcs::to_bytes(&self).expect("failed to serialize MessageToSign")); + + hash.into() + } +} + +#[derive(Serialize)] +pub struct Message { + source_chain: String, + message_id: String, + source_address: String, + destination_id: SuiAddress, + payload_hash: Bytes32, +} + +impl TryFrom<&router_api::Message> for Message { + type Error = Report; + + fn try_from(value: &router_api::Message) -> Result { + Ok(Self { + source_chain: value.cc_id.source_chain.to_string(), + message_id: value.cc_id.message_id.to_string(), + source_address: value.source_address.to_string(), + destination_id: SuiAddress::from_str(&value.destination_address) + .change_context(Error::InvalidAddress(value.destination_address.to_string()))?, + payload_hash: value.payload_hash.into(), + }) + } +} + +#[derive(Serialize)] +pub struct Signature { + bytes: Vec, +} + +impl TryFrom for Signature { + type Error = Report; + + fn try_from(signature: multisig::key::Signature) -> Result { + match signature { + // The move contracts require recoverable signatures. This should + // only be called after the proper conversion during encoding. + multisig::key::Signature::EcdsaRecoverable(signature) => Ok(Self { + bytes: signature.as_ref().to_vec(), + }), + _ => Err(report!(Error::UnsupportedSignature)), + } + } +} + +#[derive(Serialize)] +pub struct Proof { + signers: WeightedSigners, + signatures: Vec, +} + +impl TryFrom<(VerifierSet, Vec)> for Proof { + type Error = Report; + + fn try_from( + (verifier_set, mut signatures): (VerifierSet, Vec), + ) -> Result { + signatures.sort_by(|signer1, signer2| signer1.signer.pub_key.cmp(&signer2.signer.pub_key)); + + Ok(Self { + signers: verifier_set.try_into()?, + signatures: signatures + .into_iter() + .map(|signer| signer.signature) + .map(TryInto::try_into) + .collect::, _>>()?, + }) + } +} + +#[derive(Serialize, Deserialize)] +pub struct ExecuteData { + pub payload: Vec, + pub proof: Vec, +} + +impl ExecuteData { + pub fn new(payload: Vec, proof: Vec) -> Self { + Self { payload, proof } + } +} diff --git a/packages/sui-types/Cargo.toml b/packages/sui-types/Cargo.toml new file mode 100644 index 000000000..f5744a8f4 --- /dev/null +++ b/packages/sui-types/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "sui-types" +version = "1.0.0" +edition = { workspace = true } +rust-version = { workspace = true } + +[dependencies] +error-stack = { workspace = true } +hex = { workspace = true } +serde = { workspace = true } +thiserror = { workspace = true } + +[dev-dependencies] +rand = "0.8.5" + +[lints] +workspace = true diff --git a/packages/sui-types/src/lib.rs b/packages/sui-types/src/lib.rs new file mode 100644 index 000000000..baa6b7aa1 --- /dev/null +++ b/packages/sui-types/src/lib.rs @@ -0,0 +1,107 @@ +use std::str::FromStr; + +use error_stack::{ensure, report, Report, Result, ResultExt}; +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +const ADDRESS_PREFIX: &str = "0x"; +const SUI_ADDRESS_LENGTH: usize = 32; + +#[derive(Error, Debug)] +pub enum Error { + #[error("invalid address length: {:?}", .0)] + InvalidAddressLength(Vec), + #[error("invalid address: {0}")] + InvalidAddress(String), +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct SuiAddress([u8; SUI_ADDRESS_LENGTH]); + +impl SuiAddress { + pub fn as_bytes(&self) -> &[u8; SUI_ADDRESS_LENGTH] { + &self.0 + } +} + +impl TryFrom<&[u8]> for SuiAddress { + type Error = Report; + + fn try_from(bytes: &[u8]) -> Result { + bytes + .try_into() + .map(Self) + .change_context(Error::InvalidAddressLength(bytes.to_vec())) + } +} + +impl FromStr for SuiAddress { + type Err = Report; + + fn from_str(s: &str) -> Result { + let hex = s + .strip_prefix(ADDRESS_PREFIX) + .ok_or(report!(Error::InvalidAddress(s.to_string())))?; + // disallow uppercase characters for the sui addresses + ensure!( + hex.to_lowercase() == hex, + Error::InvalidAddress(s.to_string()) + ); + + hex::decode(hex) + .change_context(Error::InvalidAddress(s.to_string()))? + .as_slice() + .try_into() + } +} + +#[cfg(test)] +mod tests { + use rand::RngCore; + + use super::*; + + #[test] + fn sui_address_try_from() { + let mut bytes = [0u8; SUI_ADDRESS_LENGTH]; + rand::thread_rng().fill_bytes(&mut bytes); + let address = SuiAddress::try_from(&bytes[..]).unwrap(); + assert_eq!(address.as_bytes(), &bytes); + + let mut bytes = [0u8; SUI_ADDRESS_LENGTH + 1]; + rand::thread_rng().fill_bytes(&mut bytes); + assert!(SuiAddress::try_from(&bytes[..]).is_err()); + + let mut bytes = [0u8; SUI_ADDRESS_LENGTH - 1]; + rand::thread_rng().fill_bytes(&mut bytes); + assert!(SuiAddress::try_from(&bytes[..]).is_err()); + } + + #[test] + fn sui_address_from_str() { + let mut bytes = [0u8; SUI_ADDRESS_LENGTH]; + rand::thread_rng().fill_bytes(&mut bytes); + let address = SuiAddress::from_str(format!("0x{}", hex::encode(bytes)).as_str()).unwrap(); + assert_eq!(address.as_bytes(), &bytes); + + let mut bytes = [0u8; SUI_ADDRESS_LENGTH + 1]; + rand::thread_rng().fill_bytes(&mut bytes); + assert!(SuiAddress::from_str(format!("0x{}", hex::encode(bytes)).as_str()).is_err()); + + let mut bytes = [0u8; SUI_ADDRESS_LENGTH - 1]; + rand::thread_rng().fill_bytes(&mut bytes); + assert!(SuiAddress::from_str(format!("0x{}", hex::encode(bytes)).as_str()).is_err()); + + let mut bytes = [0u8; SUI_ADDRESS_LENGTH]; + rand::thread_rng().fill_bytes(&mut bytes); + assert!(SuiAddress::from_str(format!("0x0x{}", hex::encode(bytes)).as_str()).is_err()); + + let mut bytes = [0u8; SUI_ADDRESS_LENGTH]; + rand::thread_rng().fill_bytes(&mut bytes); + assert!(SuiAddress::from_str(format!("test{}", hex::encode(bytes)).as_str()).is_err()); + + let mut bytes = [0u8; SUI_ADDRESS_LENGTH]; + rand::thread_rng().fill_bytes(&mut bytes); + assert!(SuiAddress::from_str(hex::encode(bytes).as_str()).is_err()); + } +} diff --git a/release.toml b/release.toml index da7a87a48..7259e24f6 100644 --- a/release.toml +++ b/release.toml @@ -1,4 +1,4 @@ -pre-release-commit-message = "chore: release {{crate_name}} {{version}} [skip ci]" +pre-release-commit-message = "chore: release {{crate_name}} {{version}}" consolidate-commits = false release = true publish = false diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 000000000..3a3f3f1dd --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,2 @@ +imports_granularity = "Module" +group_imports = "StdExternalCrate"