diff --git a/.changelog/v0.20.0/bug-fixes/1667-faucet-limit-fix.md b/.changelog/v0.20.0/bug-fixes/1667-faucet-limit-fix.md new file mode 100644 index 0000000000..49c3647b55 --- /dev/null +++ b/.changelog/v0.20.0/bug-fixes/1667-faucet-limit-fix.md @@ -0,0 +1,2 @@ +- Fix genesis `faucet_withdrawal_limit` parser to respect tokens' denomination. + ([\#1667](https://github.com/anoma/namada/pull/1667)) \ No newline at end of file diff --git a/.changelog/v0.20.0/bug-fixes/1686-delete-prefix.md b/.changelog/v0.20.0/bug-fixes/1686-delete-prefix.md new file mode 100644 index 0000000000..dc8a2dd175 --- /dev/null +++ b/.changelog/v0.20.0/bug-fixes/1686-delete-prefix.md @@ -0,0 +1,3 @@ +- PoS: ensure that the size of genesis validator set + is limited by the `max_validator_slots` parameter. + ([\#1686](https://github.com/anoma/namada/pull/1686)) \ No newline at end of file diff --git a/.changelog/v0.20.0/bug-fixes/1709-fix_changes_before_commit.md b/.changelog/v0.20.0/bug-fixes/1709-fix_changes_before_commit.md new file mode 100644 index 0000000000..33eb543193 --- /dev/null +++ b/.changelog/v0.20.0/bug-fixes/1709-fix_changes_before_commit.md @@ -0,0 +1,2 @@ +- Fix inconsistency state before commit + ([\#1709](https://github.com/anoma/namada/issues/1709)) \ No newline at end of file diff --git a/.changelog/v0.20.0/bug-fixes/1729-pos-fix-rewards-boundary.md b/.changelog/v0.20.0/bug-fixes/1729-pos-fix-rewards-boundary.md new file mode 100644 index 0000000000..3b6fcaa903 --- /dev/null +++ b/.changelog/v0.20.0/bug-fixes/1729-pos-fix-rewards-boundary.md @@ -0,0 +1,3 @@ +- PoS: Fixed an epoch boundary issue in which a validator who's being slashed + on a start of a new epoch is disregarded during processing of block votes. + ([\#1729](https://github.com/anoma/namada/pull/1729)) \ No newline at end of file diff --git a/.changelog/v0.20.0/improvements/1129-clear-out-validator-sets-for-old-epochs.md b/.changelog/v0.20.0/improvements/1129-clear-out-validator-sets-for-old-epochs.md new file mode 100644 index 0000000000..e7fb6e9063 --- /dev/null +++ b/.changelog/v0.20.0/improvements/1129-clear-out-validator-sets-for-old-epochs.md @@ -0,0 +1,2 @@ +- PoS: purge validator sets for old epochs from the storage; store total + validator stake ([\#1129](https://github.com/anoma/namada/issues/1129)) \ No newline at end of file diff --git a/.changelog/v0.20.0/improvements/1173-token-balance-query.md b/.changelog/v0.20.0/improvements/1173-token-balance-query.md new file mode 100644 index 0000000000..e5a9ea2322 --- /dev/null +++ b/.changelog/v0.20.0/improvements/1173-token-balance-query.md @@ -0,0 +1,2 @@ +- Added a reusable token balance query method. + ([\#1173](https://github.com/anoma/namada/pull/1173)) \ No newline at end of file diff --git a/.changelog/v0.20.0/improvements/1605-win-build.md b/.changelog/v0.20.0/improvements/1605-win-build.md new file mode 100644 index 0000000000..ee4a8d026e --- /dev/null +++ b/.changelog/v0.20.0/improvements/1605-win-build.md @@ -0,0 +1,2 @@ +- Replaced file-lock with fd-lock dependency to support Windows build. + ([\#1605](https://github.com/anoma/namada/pull/1605)) \ No newline at end of file diff --git a/.changelog/v0.20.0/improvements/1621-utils-next-epoch.md b/.changelog/v0.20.0/improvements/1621-utils-next-epoch.md new file mode 100644 index 0000000000..5f663a8e00 --- /dev/null +++ b/.changelog/v0.20.0/improvements/1621-utils-next-epoch.md @@ -0,0 +1,2 @@ +- Added a command to wait for the next epoch: `client utils epoch-sleep`. + ([\#1621](https://github.com/anoma/namada/pull/1621)) \ No newline at end of file diff --git a/.changelog/v0.20.0/improvements/1656-pos-cli-queries.md b/.changelog/v0.20.0/improvements/1656-pos-cli-queries.md new file mode 100644 index 0000000000..a114c6f2f6 --- /dev/null +++ b/.changelog/v0.20.0/improvements/1656-pos-cli-queries.md @@ -0,0 +1,2 @@ +- Added a client query for `validator-state` and improved the slashes query to + show more info. ([\#1656](https://github.com/anoma/namada/pull/1656)) \ No newline at end of file diff --git a/.changelog/v0.20.0/improvements/1670-remove-unused-assoc-ty.md b/.changelog/v0.20.0/improvements/1670-remove-unused-assoc-ty.md new file mode 100644 index 0000000000..b4db773566 --- /dev/null +++ b/.changelog/v0.20.0/improvements/1670-remove-unused-assoc-ty.md @@ -0,0 +1,4 @@ +- Removed associated type on `masp::ShieldedUtils`. This type was an + attempt to reduce the number of generic parameters needed when interacting + with MASP but resulted in making code re-use extremely difficult. + ([\#1670](https://github.com/anoma/namada/pull/1670)) \ No newline at end of file diff --git a/.changelog/v0.20.0/improvements/1692-rm-from-u64-on-ethbridge-stake.md b/.changelog/v0.20.0/improvements/1692-rm-from-u64-on-ethbridge-stake.md new file mode 100644 index 0000000000..4c6813670c --- /dev/null +++ b/.changelog/v0.20.0/improvements/1692-rm-from-u64-on-ethbridge-stake.md @@ -0,0 +1,2 @@ +- Removed `impl From for EthBridgeVotingPower` and replaced it with a + `TryFrom`. ([\#1692](https://github.com/anoma/namada/pull/1692)) \ No newline at end of file diff --git a/.changelog/v0.20.0/improvements/1695-update-sysinfo.md b/.changelog/v0.20.0/improvements/1695-update-sysinfo.md new file mode 100644 index 0000000000..13dd88558c --- /dev/null +++ b/.changelog/v0.20.0/improvements/1695-update-sysinfo.md @@ -0,0 +1,2 @@ +- Updated sysinfo dependency. + ([\#1695](https://github.com/anoma/namada/pull/1695)) \ No newline at end of file diff --git a/.changelog/v0.20.0/improvements/1717-storage-refactor.md b/.changelog/v0.20.0/improvements/1717-storage-refactor.md new file mode 100644 index 0000000000..ae44ad180a --- /dev/null +++ b/.changelog/v0.20.0/improvements/1717-storage-refactor.md @@ -0,0 +1,2 @@ +- Refactored storage code to only use an immutable reference when reading and + writing to a batch. ([\#1717](https://github.com/anoma/namada/pull/1717)) \ No newline at end of file diff --git a/.changelog/v0.20.0/miscellaneous/1693-ibc-multitoken.md b/.changelog/v0.20.0/miscellaneous/1693-ibc-multitoken.md new file mode 100644 index 0000000000..1c90f3a4b8 --- /dev/null +++ b/.changelog/v0.20.0/miscellaneous/1693-ibc-multitoken.md @@ -0,0 +1,2 @@ +- Replaced token sub-prefix with a multitoken address and native VP for IBC and + ETH bridge. ([\#1693](https://github.com/anoma/namada/pull/1693)) \ No newline at end of file diff --git a/.changelog/v0.20.0/miscellaneous/1733-pos-data-history.md b/.changelog/v0.20.0/miscellaneous/1733-pos-data-history.md new file mode 100644 index 0000000000..aa3adccc66 --- /dev/null +++ b/.changelog/v0.20.0/miscellaneous/1733-pos-data-history.md @@ -0,0 +1,2 @@ +- PoS: Keep the data for last two epochs by default. + ([\#1733](https://github.com/anoma/namada/pull/1733)) \ No newline at end of file diff --git a/.changelog/v0.20.0/miscellaneous/1738-refactor-cli.md b/.changelog/v0.20.0/miscellaneous/1738-refactor-cli.md new file mode 100644 index 0000000000..ef201074db --- /dev/null +++ b/.changelog/v0.20.0/miscellaneous/1738-refactor-cli.md @@ -0,0 +1,2 @@ +- Refactored CLI into libraries for future re-use in integration tests and + to enable generic IO. ([\#1738](https://github.com/anoma/namada/pull/1738)) \ No newline at end of file diff --git a/.changelog/v0.20.0/summary.md b/.changelog/v0.20.0/summary.md new file mode 100644 index 0000000000..56e6ca5932 --- /dev/null +++ b/.changelog/v0.20.0/summary.md @@ -0,0 +1,2 @@ +Namada 0.20.0 is a minor releasing addressing several improvements to the PoS system and the ledger +stability. diff --git a/.changelog/v0.20.0/testing/1714-refactor-e2e-tests.md b/.changelog/v0.20.0/testing/1714-refactor-e2e-tests.md new file mode 100644 index 0000000000..7bf4e78be3 --- /dev/null +++ b/.changelog/v0.20.0/testing/1714-refactor-e2e-tests.md @@ -0,0 +1,3 @@ +- Added integration testing infrastructure for node, client and + the wallet and replaced MASP E2E tests with integration tests. + ([\#1714](https://github.com/anoma/namada/pull/1714)) \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE/new_topic.md b/.github/PULL_REQUEST_TEMPLATE/new_topic.md deleted file mode 100644 index 8364cf2c64..0000000000 --- a/.github/PULL_REQUEST_TEMPLATE/new_topic.md +++ /dev/null @@ -1,10 +0,0 @@ -## Describe your changes - -## Indicate on which other PRs this topic is based on, if any - -## Checklist before merging to `draft` -- [ ] I have checked that the following e2e test are working locally - - `masp_incentives` - - `masp_txs_and_queries` - - `proposal_submission` -- [ ] I have added a changelog \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000..dd74355249 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,7 @@ +## Describe your changes + +## Indicate on which release or other PRs this topic is based on + +## Checklist before merging to `draft` +- [ ] I have added a changelog +- [ ] Git history is in acceptable state diff --git a/.github/workflows/automation.yml b/.github/workflows/automation.yml index 48accef46c..a594d2aa88 100644 --- a/.github/workflows/automation.yml +++ b/.github/workflows/automation.yml @@ -38,12 +38,6 @@ jobs: command: spawn-devnet.py logs: 'false' timeout: 25 - - name: Load tester - comment: pls load test - command: load-test.py - logs: 'true' - logs_path: /tmp/namada-load-tester/logs/ - timeout: 360 steps: - name: Configure AWS Credentials @@ -79,13 +73,6 @@ jobs: GITHUB_DISPATCH_TOKEN: ${{ secrets.GT_DISPATCH }} SLACK_DEVNET_SECRET: ${{ secrets.SLACK_DEVNET_SECRET }} BINARIES_COMMIT_SHA: ${{ steps.comment-branch.outputs.head_sha }} - - name: Upload load tester logs - if: ${{ matrix.make.logs == 'true' && steps.check.outputs.triggered == 'true' }} - uses: actions/upload-artifact@v3 - with: - name: logs-load-tester-${{ github.event.pull_request.head.sha || github.sha }} - path: ${{ matrix.make.logs_path }} - retention-days: 5 - name: Comment not found if: steps.check.outputs.triggered != 'true' run: echo "Comment $COMMENT not found" diff --git a/.github/workflows/build-and-test-bridge.yml b/.github/workflows/build-and-test-bridge.yml deleted file mode 100644 index c6bf2ead2f..0000000000 --- a/.github/workflows/build-and-test-bridge.yml +++ /dev/null @@ -1,517 +0,0 @@ -name: Build and Test Ethereum Bridge - -on: - push: - branches: - - eth-bridge-integration - # Run in PRs with conflicts (https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request) - pull_request_target: - branches: - - eth-bridge-integration - - "**/ethbridge/**" - types: [opened, synchronize, reopened] - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number }} - cancel-in-progress: true - -permissions: - id-token: write - contents: read - packages: read - -env: - GIT_LFS_SKIP_SMUDGE: 1 - - -jobs: - build-wasm: - timeout-minutes: 30 - runs-on: ${{ matrix.os }} - container: - image: ghcr.io/anoma/namada:wasm-main - strategy: - fail-fast: false - matrix: - os: [ubuntu-20.04] - wasm_cache_version: ["v2"] - mold_version: [1.11.0] - - steps: - - name: Checkout repo - uses: actions/checkout@v3 - if: ${{ github.event_name != 'pull_request_target' }} - - name: Checkout PR - uses: actions/checkout@v3 - if: ${{ github.event_name == 'pull_request_target' }} - # From https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target: - # "This event runs in the context of the base of the pull request, - # rather than in the context of the merge commit, as the pull_request - # event does." - # We set the ref to the head commit of the PR instead. - # For this, we have to make sure that we're not running CI on untrusted - # code (more info in the link above), so the repo MUST be configured - # to disallow that. - with: - ref: ${{ github.event.pull_request.head.sha }} - - name: Build WASM - run: | - make build-wasm-scripts - - name: Upload wasm artifacts - uses: actions/upload-artifact@v3 - with: - name: wasm-${{ github.event.pull_request.head.sha || github.sha }} - path: | - wasm/tx_*.wasm - wasm/vp_*.wasm - wasm/checksums.json - - test-wasm: - timeout-minutes: 30 - runs-on: ${{ matrix.os }} - needs: [build-wasm] - container: - image: ghcr.io/anoma/namada:wasm-main - strategy: - fail-fast: false - matrix: - os: [ubuntu-20.04] - wasm_cache_version: ["v2"] - mold_version: [1.11.0] - - steps: - - name: Checkout repo - uses: actions/checkout@v3 - if: ${{ github.event_name != 'pull_request_target' }} - - name: Checkout PR - uses: actions/checkout@v3 - if: ${{ github.event_name == 'pull_request_target' }} - with: - ref: ${{ github.event.pull_request.head.sha }} - - name: Duplicate checksums file - run: cp wasm/checksums.json wasm/original-checksums.json - - name: Install mold linker - run: | - wget -q -O- https://github.com/rui314/mold/releases/download/v${{ matrix.mold_version }}/mold-${{ matrix.mold_version }}-x86_64-linux.tar.gz | tar -xz - mv mold-${{ matrix.mold_version }}-x86_64-linux/bin/mold /usr/local/bin - - name: Download wasm artifacts - uses: actions/download-artifact@v3 - with: - name: wasm-${{ github.event.pull_request.head.sha|| github.sha }} - path: ./wasm - env: - RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=/usr/local/bin/mold" - - name: Test Wasm - run: make test-wasm - - name: Check wasm up-to-date - run: cmp -- wasm/checksums.json wasm/original-checksums.json || true - - name: Print diff - run: diff -y -W 150 wasm/checksums.json wasm/original-checksums.json --suppress-common-lines || true - - update-wasm: - runs-on: ${{ matrix.os }} - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.build-wasm.result == 'success' }} - timeout-minutes: 30 - needs: [build-wasm] - strategy: - fail-fast: false - matrix: - os: [ubuntu-20.04] - - steps: - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v1 - with: - role-to-assume: arn:aws:iam::375643557360:role/anoma-github-action-ci-master - aws-region: eu-west-1 - - name: Download wasm artifacts - uses: actions/download-artifact@v3 - with: - name: wasm-${{ github.event.pull_request.head.sha || github.sha }} - path: ./wasm - - name: Update WASM - run: aws s3 sync wasm s3://$BUCKET_NAME --acl public-read --exclude "*" --include "*.wasm" --exclude "*/*" --region $AWS_REGION - env: - BUCKET_NAME: namada-wasm-master - AWS_REGION: eu-west-1 - - namada-eth: - runs-on: ${{ matrix.os }} - timeout-minutes: 80 - needs: [build-wasm] - strategy: - fail-fast: false - matrix: - os: [ubuntu-20.04] - nightly_version: [nightly-2023-06-01] - mold_version: [1.11.0] - make: - - name: ABCI - suffix: '' - cache_key: namada - cache_version: v2 - - env: - CARGO_INCREMENTAL: 0 - RUST_BACKTRACE: full - RUSTC_WRAPPER: sccache - SCCACHE_CACHE_SIZE: 100G - SCCACHE_BUCKET: namada-sccache-master - - steps: - - name: Checkout repo - uses: actions/checkout@v3 - if: ${{ github.event_name != 'pull_request_target' }} - - name: Checkout PR - uses: actions/checkout@v3 - if: ${{ github.event_name == 'pull_request_target' }} - # See comment in build-and-test.yml - with: - ref: ${{ github.event.pull_request.head.sha }} - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v1 - with: - role-to-assume: arn:aws:iam::375643557360:role/anoma-github-action-ci-master - aws-region: eu-west-1 - - name: Install Protoc - uses: arduino/setup-protoc@v1 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - - name: Install sccache (ubuntu-20.04) - if: matrix.os == 'ubuntu-20.04' - env: - LINK: https://github.com/mozilla/sccache/releases/download - SCCACHE_VERSION: v0.3.0 - run: | - SCCACHE_FILE=sccache-$SCCACHE_VERSION-x86_64-unknown-linux-musl - mkdir -p $HOME/.local/bin - curl -L "$LINK/$SCCACHE_VERSION/$SCCACHE_FILE.tar.gz" | tar xz - mv -f $SCCACHE_FILE/sccache $HOME/.local/bin/sccache - chmod +x $HOME/.local/bin/sccache - echo "$HOME/.local/bin" >> $GITHUB_PATH - - name: Install sccache (macos-latest) - if: matrix.os == 'macos-latest' - run: | - brew update - brew install sccache - - name: Setup rust toolchain - uses: oxidecomputer/actions-rs_toolchain@ad3f86084a8a5acf2c09cb691421b31cf8af7a36 - with: - profile: default - override: true - - name: Setup rust nightly - uses: oxidecomputer/actions-rs_toolchain@ad3f86084a8a5acf2c09cb691421b31cf8af7a36 - with: - toolchain: ${{ matrix.nightly_version }} - profile: default - - name: Cache cargo registry - uses: actions/cache@v3 - continue-on-error: false - with: - path: | - ~/.cargo/registry - ~/.cargo/git - key: ${{ runner.os }}-${{ matrix.make.cache_key }}-${{ matrix.make.cache_version }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: ${{ runner.os }}-${{ matrix.make.cache_key }}-${{ matrix.make.cache_version }}-cargo- - - name: Start sccache server - run: sccache --start-server - - name: Install mold linker - run: | - wget -q -O- https://github.com/rui314/mold/releases/download/v${{ matrix.mold_version }}/mold-${{ matrix.mold_version }}-x86_64-linux.tar.gz | tar -xz - mv mold-${{ matrix.mold_version }}-x86_64-linux/bin/mold /usr/local/bin - - name: Download wasm artifacts - uses: actions/download-artifact@v3 - with: - name: wasm-${{ github.event.pull_request.head.sha|| github.sha }} - path: ./wasm - - uses: taiki-e/install-action@cargo-llvm-cov - - name: Run unit test - run: make test-unit-coverage${{ matrix.make.suffix }} - env: - RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=/usr/local/bin/mold" - - name: Upload coverage - uses: actions/upload-artifact@v3 - with: - name: coverage${{ matrix.make.suffix }}-${{ github.event.pull_request.head.sha || github.sha }} - path: target/html - retention-days: 3 - - name: Print sccache stats - if: always() - run: sccache --show-stats - - name: Stop sccache server - if: always() - run: sccache --stop-server || true - - namada-release-eth: - runs-on: ${{ matrix.os }} - timeout-minutes: 25 - strategy: - fail-fast: false - matrix: - os: [ubuntu-20.04] - mold_version: [1.11.0] - make: - - name: ABCI Release build - suffix: '' - cache_key: namada-e2e-release - cache_version: "v2" - - env: - CARGO_INCREMENTAL: 0 - RUST_BACKTRACE: full - RUSTC_WRAPPER: sccache - SCCACHE_CACHE_SIZE: 100G - SCCACHE_BUCKET: namada-sccache-master - - steps: - - name: Checkout repo - uses: actions/checkout@v3 - if: ${{ github.event_name != 'pull_request_target' }} - - name: Checkout PR - uses: actions/checkout@v3 - if: ${{ github.event_name == 'pull_request_target' }} - # See comment in build-and-test.yml - with: - ref: ${{ github.event.pull_request.head.sha }} - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v1 - with: - role-to-assume: arn:aws:iam::375643557360:role/anoma-github-action-ci-master - aws-region: eu-west-1 - - name: Install Protoc - uses: arduino/setup-protoc@v1 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - - name: Install sccache (ubuntu-20.04) - if: matrix.os == 'ubuntu-20.04' - env: - LINK: https://github.com/mozilla/sccache/releases/download - SCCACHE_VERSION: v0.3.0 - run: | - SCCACHE_FILE=sccache-$SCCACHE_VERSION-x86_64-unknown-linux-musl - mkdir -p $HOME/.local/bin - curl -L "$LINK/$SCCACHE_VERSION/$SCCACHE_FILE.tar.gz" | tar xz - mv -f $SCCACHE_FILE/sccache $HOME/.local/bin/sccache - chmod +x $HOME/.local/bin/sccache - echo "$HOME/.local/bin" >> $GITHUB_PATH - - name: Install sccache (macos-latest) - if: matrix.os == 'macos-latest' - run: | - brew update - brew install sccache - - name: Setup rust toolchain - uses: oxidecomputer/actions-rs_toolchain@ad3f86084a8a5acf2c09cb691421b31cf8af7a36 - with: - profile: default - override: true - - name: Cache cargo registry - uses: actions/cache@v3 - continue-on-error: false - with: - path: | - ~/.cargo/registry - ~/.cargo/git - key: ${{ runner.os }}-${{ matrix.make.cache_key }}-${{ matrix.make.cache_version }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: ${{ runner.os }}-${{ matrix.make.cache_key }}-${{ matrix.make.cache_version }}-cargo- - - name: Install mold linker - run: | - wget -q -O- https://github.com/rui314/mold/releases/download/v${{ matrix.mold_version }}/mold-${{ matrix.mold_version }}-x86_64-linux.tar.gz | tar -xz - mv mold-${{ matrix.mold_version }}-x86_64-linux/bin/mold /usr/local/bin - - name: Start sccache server - run: sccache --start-server - - name: Build - run: make build-release${{ matrix.make.suffix }} - env: - RUSTFLAGS: "-C linker=clang -C debug_assertions=true -C link-arg=-fuse-ld=/usr/local/bin/mold" - - name: Upload target binaries - uses: actions/upload-artifact@v3 - with: - name: binaries${{ matrix.make.suffix }}-${{ github.event.pull_request.head.sha || github.sha }} - path: | - target/release/namada - target/release/namadac - target/release/namadaw - target/release/namadan - - name: Upload build timing report - if: success() || failure() - uses: actions/upload-artifact@v3 - with: - name: build-timings-${{ github.event.pull_request.head.sha || github.sha }} - path: | - target/cargo-timings/cargo-timing.html - retention-days: 5 - - name: Print sccache stats - if: always() - run: sccache --show-stats - - name: Stop sccache server - if: always() - run: sccache --stop-server || true - - - namada-e2e-eth: - runs-on: ${{ matrix.os }} - timeout-minutes: 80 - strategy: - fail-fast: false - matrix: - os: [ubuntu-20.04] - nightly_version: [nightly-2023-06-01] - mold_version: [1.11.0] - make: - - name: e2e - suffix: '' - index: 0 - cache_key: namada - cache_version: v2 - wait_for: namada-release-eth (ubuntu-20.04, 1.7.0, ABCI Release build, namada-e2e-release, v2) - - name: e2e - suffix: '' - index: 1 - cache_key: namada - cache_version: v2 - wait_for: namada-release-eth (ubuntu-20.04, 1.7.0, ABCI Release build, namada-e2e-release, v2) - - env: - CARGO_INCREMENTAL: 0 - RUST_BACKTRACE: full - RUSTC_WRAPPER: sccache - SCCACHE_CACHE_SIZE: 100G - SCCACHE_BUCKET: namada-sccache-master - - steps: - - name: Checkout repo - uses: actions/checkout@v3 - if: ${{ github.event_name != 'pull_request_target' }} - - name: Checkout PR - uses: actions/checkout@v3 - if: ${{ github.event_name == 'pull_request_target' }} - # See comment in build-and-test.yml - with: - ref: ${{ github.event.pull_request.head.sha }} - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v1 - with: - role-to-assume: arn:aws:iam::375643557360:role/anoma-github-action-ci-master - aws-region: eu-west-1 - - name: Install Protoc - uses: arduino/setup-protoc@v1 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - - name: Install sccache (ubuntu-20.04) - if: matrix.os == 'ubuntu-20.04' - env: - LINK: https://github.com/mozilla/sccache/releases/download - SCCACHE_VERSION: v0.3.0 - run: | - SCCACHE_FILE=sccache-$SCCACHE_VERSION-x86_64-unknown-linux-musl - mkdir -p $HOME/.local/bin - curl -L "$LINK/$SCCACHE_VERSION/$SCCACHE_FILE.tar.gz" | tar xz - mv -f $SCCACHE_FILE/sccache $HOME/.local/bin/sccache - chmod +x $HOME/.local/bin/sccache - echo "$HOME/.local/bin" >> $GITHUB_PATH - - name: Install sccache (macos-latest) - if: matrix.os == 'macos-latest' - run: | - brew update - brew install sccache - - name: Setup rust toolchain - uses: oxidecomputer/actions-rs_toolchain@ad3f86084a8a5acf2c09cb691421b31cf8af7a36 - with: - profile: default - override: true - - name: Setup rust nightly - uses: oxidecomputer/actions-rs_toolchain@ad3f86084a8a5acf2c09cb691421b31cf8af7a36 - with: - toolchain: ${{ matrix.nightly_version }} - profile: default - - name: Cache cargo registry - uses: actions/cache@v3 - continue-on-error: false - with: - path: | - ~/.cargo/registry - ~/.cargo/git - key: ${{ runner.os }}-${{ matrix.make.cache_key }}-${{ matrix.make.cache_version }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: ${{ runner.os }}-${{ matrix.make.cache_key }}-${{ matrix.make.cache_version }}-cargo- - - name: Start sccache server - run: sccache --start-server - - name: Install mold linker - run: | - wget -q -O- https://github.com/rui314/mold/releases/download/v${{ matrix.mold_version }}/mold-${{ matrix.mold_version }}-x86_64-linux.tar.gz | tar -xz - mv mold-${{ matrix.mold_version }}-x86_64-linux/bin/mold /usr/local/bin - - name: Build - run: make build - env: - RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=/usr/local/bin/mold" - - name: Build test - run: make build-test - env: - RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=/usr/local/bin/mold" - - name: Wait for release binaries - uses: lewagon/wait-on-check-action@v1.2.0 - with: - ref: ${{ github.event.pull_request.head.sha || github.ref }} - check-name: ${{ matrix.make.wait_for }} - repo-token: ${{ secrets.GITHUB_TOKEN }} - wait-interval: 30 - allowed-conclusions: success - - name: Download wasm artifacts - uses: actions/download-artifact@v3 - with: - name: wasm-${{ github.event.pull_request.head.sha|| github.sha }} - path: ./wasm - - name: Download namada binaries - uses: actions/download-artifact@v3 - with: - name: binaries${{ matrix.make.suffix }}-${{ github.event.pull_request.head.sha || github.sha }} - path: ./target/release/ - - name: Download tendermint & cometbft - run: | - curl -o cometbft.tar.gz -LO https://github.com/cometbft/cometbft/releases/download/v0.37.2/cometbft_0.37.2_linux_amd64.tar.gz - tar -xvzf cometbft.tar.gz - mv cometbft /usr/local/bin - curl -o tendermint.tar.gz -LO https://github.com/heliaxdev/tendermint/releases/download/v0.1.4-abciplus/tendermint_0.1.4-abciplus_linux_amd64.tar.gz - tar -xvzf tendermint.tar.gz - mv tendermint /usr/local/bin - - name: Change permissions - run: | - chmod +x target/release/namada - chmod +x target/release/namadaw - chmod +x target/release/namadan - chmod +x target/release/namadac - chmod +x /usr/local/bin/tendermint - chmod +x /usr/local/bin/cometbft - - name: Run e2e test - run: | - mkdir -p /home/runner/work/masp-params - curl -o /home/runner/work/masp-params/masp-spend.params -LO https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-spend.params?raw=true - curl -o /home/runner/work/masp-params/masp-output.params -LO https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-output.params?raw=true - curl -o /home/runner/work/masp-params/masp-convert.params -LO https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-convert.params?raw=true - ls -l /home/runner/work/masp-params - shasum /home/runner/work/masp-params/*.params - python3 .github/workflows/scripts/schedule-e2e.py - env: - NAMADA_E2E_USE_PREBUILT_BINARIES: "true" - NAMADA_E2E_KEEP_TEMP: "true" - NAMADA_TM_STDOUT: "false" - NAMADA_LOG_COLOR: "false" - # NAMADA_MASP_PARAMS_DIR: "/home/runner/work/masp-params" - NAMADA_LOG: "info" - RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=/usr/local/bin/mold" - INDEX: ${{ matrix.make.index }} - - name: Upload e2e logs - if: success() || failure() - uses: actions/upload-artifact@v3 - with: - name: logs-e2e-${{ matrix.make.index }}-${{ github.event.pull_request.head.sha || github.sha }} - path: | - /tmp/.*/logs/ - /tmp/.*/e2e-test.*/setup/validator-*/.namada/logs/*.log - retention-days: 5 - - name: Print sccache stats - if: always() - run: sccache --show-stats - - name: Stop sccache server - if: always() - run: sccache --stop-server || true diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index b9929e95da..e274106660 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -392,21 +392,18 @@ jobs: index: 0 cache_key: namada cache_version: v2 - tendermint_artifact: tendermint-unreleased-v0.1.4-abciplus wait_for: namada-release (ubuntu-20.04, 1.11.0, ABCI Release build, namada-e2e-release, v2) - name: e2e suffix: '' index: 1 cache_key: namada cache_version: v2 - tendermint_artifact: tendermint-unreleased-v0.1.4-abciplus wait_for: namada-release (ubuntu-20.04, 1.11.0, ABCI Release build, namada-e2e-release, v2) - name: e2e suffix: '' index: 2 cache_key: namada cache_version: v2 - tendermint_artifact: tendermint-unreleased-v0.1.4-abciplus wait_for: namada-release (ubuntu-20.04, 1.11.0, ABCI Release build, namada-e2e-release, v2) env: @@ -509,38 +506,26 @@ jobs: with: name: binaries${{ matrix.make.suffix }}-${{ github.event.pull_request.head.sha || github.sha }} path: ./target/release/ - - name: Download tendermint & cometbft + - name: Download CometBFT run: | curl -o cometbft.tar.gz -LO https://github.com/cometbft/cometbft/releases/download/v0.37.2/cometbft_0.37.2_linux_amd64.tar.gz tar -xvzf cometbft.tar.gz mv cometbft /usr/local/bin - curl -o tendermint.tar.gz -LO https://github.com/heliaxdev/tendermint/releases/download/v0.1.4-abciplus/tendermint_0.1.4-abciplus_linux_amd64.tar.gz - tar -xvzf tendermint.tar.gz - mv tendermint /usr/local/bin - name: Change permissions run: | chmod +x target/release/namada chmod +x target/release/namadaw chmod +x target/release/namadan chmod +x target/release/namadac - chmod +x /usr/local/bin/tendermint chmod +x /usr/local/bin/cometbft - name: Run e2e test run: | - mkdir -p /home/runner/work/masp-params - curl -o /home/runner/work/masp-params/masp-spend.params -LO https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-spend.params?raw=true - curl -o /home/runner/work/masp-params/masp-output.params -LO https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-output.params?raw=true - curl -o /home/runner/work/masp-params/masp-convert.params -LO https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-convert.params?raw=true - ls -l /home/runner/work/masp-params - shasum /home/runner/work/masp-params/*.params python3 .github/workflows/scripts/schedule-e2e.py env: - NAMADA_TENDERMINT_WEBSOCKET_TIMEOUT: 20 NAMADA_E2E_USE_PREBUILT_BINARIES: "true" NAMADA_E2E_KEEP_TEMP: "true" NAMADA_TM_STDOUT: "false" NAMADA_LOG_COLOR: "false" - # NAMADA_MASP_PARAMS_DIR: "/home/runner/work/masp-params" NAMADA_LOG: "info" RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=/usr/local/bin/mold" INDEX: ${{ matrix.make.index }} diff --git a/.github/workflows/build-tendermint.yml b/.github/workflows/build-tendermint.yml deleted file mode 100644 index d7977a4b3b..0000000000 --- a/.github/workflows/build-tendermint.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Build tendermint binaries - -on: - schedule: - - cron: "0 0 * * *" - workflow_dispatch: - -permissions: - id-token: write - contents: read - -env: - GIT_LFS_SKIP_SMUDGE: 1 - - -jobs: - tendermint: - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [ubuntu-20.04] - make: - - name: tendermint-unreleased - repository: heliaxdev/tendermint - tendermint_version: v0.1.4-abciplus - - steps: - - name: Build ${{ matrix.make.name }} - run: | - git clone https://github.com/${{ matrix.make.repository }}.git && cd tendermint - git checkout ${{ matrix.make.tendermint_version }} && make build - - name: Upload ${{ matrix.make.name }} binary - uses: actions/upload-artifact@v3 - with: - name: ${{ matrix.make.name }}-${{ matrix.make.tendermint_version }} - path: tendermint/build/tendermint diff --git a/.github/workflows/scripts/e2e.json b/.github/workflows/scripts/e2e.json index bd516e8932..db009b60f6 100644 --- a/.github/workflows/scripts/e2e.json +++ b/.github/workflows/scripts/e2e.json @@ -21,6 +21,7 @@ "e2e::ledger_tests::test_namada_shuts_down_if_tendermint_dies": 2, "e2e::ledger_tests::test_genesis_validators": 14, "e2e::ledger_tests::test_node_connectivity_and_consensus": 28, + "e2e::ledger_tests::test_epoch_sleep": 12, "e2e::wallet_tests::wallet_address_cmds": 1, "e2e::wallet_tests::wallet_encrypted_key_cmds": 1, "e2e::wallet_tests::wallet_encrypted_key_cmds_env_var": 1, diff --git a/CHANGELOG.md b/CHANGELOG.md index 33f8ff322a..fe22259365 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,61 @@ # CHANGELOG +## v0.20.0 + +Namada 0.20.0 is a minor releasing addressing several improvements to the PoS system and the ledger +stability. + +### BUG FIXES + +- Fix genesis `faucet_withdrawal_limit` parser to respect tokens' denomination. + ([\#1667](https://github.com/anoma/namada/pull/1667)) +- PoS: ensure that the size of genesis validator set + is limited by the `max_validator_slots` parameter. + ([\#1686](https://github.com/anoma/namada/pull/1686)) +- Fix inconsistency state before commit + ([\#1709](https://github.com/anoma/namada/issues/1709)) +- PoS: Fixed an epoch boundary issue in which a validator who's being slashed + on a start of a new epoch is disregarded during processing of block votes. + ([\#1729](https://github.com/anoma/namada/pull/1729)) + +### IMPROVEMENTS + +- PoS: purge validator sets for old epochs from the storage; store total + validator stake ([\#1129](https://github.com/anoma/namada/issues/1129)) +- Added a reusable token balance query method. + ([\#1173](https://github.com/anoma/namada/pull/1173)) +- Replaced file-lock with fd-lock dependency to support Windows build. + ([\#1605](https://github.com/anoma/namada/pull/1605)) +- Added a command to wait for the next epoch: `client utils epoch-sleep`. + ([\#1621](https://github.com/anoma/namada/pull/1621)) +- Added a client query for `validator-state` and improved the slashes query to + show more info. ([\#1656](https://github.com/anoma/namada/pull/1656)) +- Removed associated type on `masp::ShieldedUtils`. This type was an + attempt to reduce the number of generic parameters needed when interacting + with MASP but resulted in making code re-use extremely difficult. + ([\#1670](https://github.com/anoma/namada/pull/1670)) +- Removed `impl From for EthBridgeVotingPower` and replaced it with a + `TryFrom`. ([\#1692](https://github.com/anoma/namada/pull/1692)) +- Updated sysinfo dependency. + ([\#1695](https://github.com/anoma/namada/pull/1695)) +- Refactored storage code to only use an immutable reference when reading and + writing to a batch. ([\#1717](https://github.com/anoma/namada/pull/1717)) + +### MISCELLANEOUS + +- Replaced token sub-prefix with a multitoken address and native VP for IBC and + ETH bridge. ([\#1693](https://github.com/anoma/namada/pull/1693)) +- PoS: Keep the data for last two epochs by default. + ([\#1733](https://github.com/anoma/namada/pull/1733)) +- Refactored CLI into libraries for future re-use in integration tests and + to enable generic IO. ([\#1738](https://github.com/anoma/namada/pull/1738)) + +### TESTING + +- Added integration testing infrastructure for node, client and + the wallet and replaced MASP E2E tests with integration tests. + ([\#1714](https://github.com/anoma/namada/pull/1714)) + ## v0.19.0 Namada 0.19.0 is a minor releasing addressing the integration with the namada trustless ethereum bridge. diff --git a/Cargo.lock b/Cargo.lock index 568de9a745..9517b5cf7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,11 +14,11 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.17.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" dependencies = [ - "gimli", + "gimli 0.27.2", ] [[package]] @@ -34,7 +34,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ "crypto-common", - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -51,9 +51,9 @@ dependencies = [ [[package]] name = "aes" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ "cfg-if 1.0.0", "cipher 0.4.4", @@ -66,7 +66,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.9", "once_cell", "version_check", ] @@ -149,9 +149,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.65" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "ark-bls12-381" @@ -277,9 +277,9 @@ dependencies = [ [[package]] name = "arrayref" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" @@ -321,34 +321,35 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-stream" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" dependencies = [ "async-stream-impl", "futures-core", + "pin-project-lite", ] [[package]] name = "async-stream-impl" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] name = "async-trait" -version = "0.1.58" +version = "0.1.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -380,9 +381,9 @@ dependencies = [ [[package]] name = "auto_impl" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a8c1df849285fbacd587de7818cc7d13be6cd2cbcd47a04fb1801b0e2706e33" +checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" dependencies = [ "proc-macro-error", "proc-macro2", @@ -398,13 +399,13 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.6.7" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fb79c228270dcf2426e74864cabc94babb5dbab01a4314e702d2f16540e1591" +checksum = "f8175979259124331c1d7bf6586ee7e0da434155e4b2d48ec2c8386281d8df39" dependencies = [ "async-trait", "axum-core", - "bitflags 1.3.2", + "bitflags 1.2.1", "bytes", "futures-util", "http", @@ -417,19 +418,18 @@ dependencies = [ "percent-encoding", "pin-project-lite", "rustversion", - "serde 1.0.147", + "serde 1.0.163", "sync_wrapper", "tower", - "tower-http", "tower-layer", "tower-service", ] [[package]] name = "axum-core" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2f958c80c248b34b9a877a643811be8dbca03ca5ba827f2b63baf3a81e5fc4e" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" dependencies = [ "async-trait", "bytes", @@ -444,16 +444,16 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" dependencies = [ "addr2line", "cc", "cfg-if 1.0.0", "libc", - "miniz_oxide", - "object 0.29.0", + "miniz_oxide 0.6.2", + "object 0.30.3", "rustc-demangle", ] @@ -463,6 +463,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base58" version = "0.1.0" @@ -475,22 +481,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" -[[package]] -name = "base58check" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee2fe4c9a0c84515f136aaae2466744a721af6d63339c18689d9e995d74d99b" -dependencies = [ - "base58 0.1.0", - "sha2 0.8.2", -] - -[[package]] -name = "base64" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" - [[package]] name = "base64" version = "0.13.1" @@ -536,9 +526,9 @@ dependencies = [ "bitvec 1.0.1", "blake2s_simd", "byteorder", - "crossbeam-channel 0.5.6", - "ff", - "group", + "crossbeam-channel 0.5.8", + "ff 0.12.1", + "group 0.12.1", "lazy_static", "log", "num_cpus", @@ -550,11 +540,11 @@ dependencies = [ [[package]] name = "bimap" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0455254eb5c6964c4545d8bac815e1a1be4f3afe0ae695ea539c12d728d44b" +checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -563,7 +553,7 @@ version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -572,19 +562,19 @@ version = "0.65.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" dependencies = [ - "bitflags 1.3.2", + "bitflags 1.2.1", "cexpr", "clang-sys", "lazy_static", "lazycell", "peeking_take_while", - "prettyplease 0.2.6", + "prettyplease 0.2.4", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", - "syn 2.0.16", + "syn 2.0.15", ] [[package]] @@ -625,7 +615,7 @@ dependencies = [ "bech32 0.9.1", "bitcoin_hashes", "secp256k1", - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -634,14 +624,14 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", ] [[package]] name = "bitflags" -version = "1.3.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "bitflags" @@ -673,11 +663,11 @@ dependencies = [ [[package]] name = "blake2" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b12e5fd123190ce1c2e559308a94c9bacad77907d4c6005d9e58fe1a0689e55e" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "digest 0.10.7", + "digest 0.10.6", ] [[package]] @@ -692,9 +682,9 @@ dependencies = [ [[package]] name = "blake2b_simd" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72936ee4afc7f8f736d1c38383b56480b5497b4617b4a77bdbf1d2ababc76127" +checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" dependencies = [ "arrayref", "arrayvec 0.7.2", @@ -703,9 +693,9 @@ dependencies = [ [[package]] name = "blake2s_simd" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db539cc2b5f6003621f1cd9ef92d7ded8ea5232c7de0f9faa2de251cd98730d4" +checksum = "6637f448b9e61dfadbdcbae9a885fadee1f3eaffb1f8d3c1965d3ade8bdfd44f" dependencies = [ "arrayref", "arrayvec 0.7.2", @@ -714,16 +704,16 @@ dependencies = [ [[package]] name = "blake3" -version = "1.3.1" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08e53fc5a564bb15bfe6fae56bd71522205f1f91893f9c0116edad6496c183f" +checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef" dependencies = [ "arrayref", "arrayvec 0.7.2", "cc", "cfg-if 1.0.0", "constant_time_eq", - "digest 0.10.7", + "digest 0.10.6", ] [[package]] @@ -744,16 +734,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -787,8 +777,8 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3c196a77437e7cc2fb515ce413a6401291578b5afc8ecb29a3c7ab957f05941" dependencies = [ - "ff", - "group", + "ff 0.12.1", + "group 0.12.1", "pairing", "rand_core 0.6.4", "subtle 2.4.1", @@ -840,6 +830,9 @@ 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 = "bstr" @@ -852,21 +845,11 @@ dependencies = [ "regex-automata", ] -[[package]] -name = "buf_redux" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" -dependencies = [ - "memchr", - "safemem", -] - [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b" [[package]] name = "byte-slice-cast" @@ -882,29 +865,30 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "byte-unit" -version = "4.0.17" +version = "4.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581ad4b3d627b0c09a0ccb2912148f839acaca0b93cf54cbe42b6c674e86079c" +checksum = "da78b32057b8fdfc352504708feeba7216dcd65a2c9ab02978cbd288d1279b6c" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", "utf8-width", ] [[package]] name = "bytecheck" -version = "0.6.9" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d11cac2c12b5adc6570dad2ee1b87eff4955dac476fe12d81e5fdd352e52406f" +checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627" dependencies = [ "bytecheck_derive", "ptr_meta", + "simdutf8", ] [[package]] name = "bytecheck_derive" -version = "0.6.9" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e576ebe98e605500b3c8041bb888e966653577172df6dd97398714eb30b9bf" +checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" dependencies = [ "proc-macro2", "quote", @@ -929,7 +913,7 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -945,11 +929,11 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.1" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ad0e1e3e88dd237a156ab9f571021b8a158caa0ae44b1968a241efb5144c1e" +checksum = "c530edf18f37068ac2d977409ed5cd50d53d73bc653c7647b48eb78976ac9ae2" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -958,7 +942,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -970,29 +954,29 @@ dependencies = [ "camino", "cargo-platform", "semver 1.0.17", - "serde 1.0.147", + "serde 1.0.163", "serde_json", ] [[package]] name = "cargo_metadata" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a1ec454bc3eead8719cb56e15dbbfecdbc14e4b3a3ae4936cc6e31f5fc0d07" +checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", "semver 1.0.17", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "thiserror", ] [[package]] name = "cc" -version = "1.0.76" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" dependencies = [ "jobserver", ] @@ -1003,7 +987,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom 7.1.1", + "nom 7.1.3", ] [[package]] @@ -1055,9 +1039,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" dependencies = [ "iana-time-zone", "num-integer", @@ -1067,9 +1051,9 @@ dependencies = [ [[package]] name = "chunked_transfer" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e" +checksum = "cca491388666e04d7248af3f60f0c40cfb0991c72205595d7c396e3510207d1a" [[package]] name = "cipher" @@ -1077,7 +1061,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -1102,9 +1086,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.4.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" dependencies = [ "glob", "libc", @@ -1113,21 +1097,22 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.10" +version = "4.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384e169cc618c613d5e3ca6404dda77a8685a63e08660dcc64abaf7da7cb0c7a" +checksum = "2686c4115cb0810d9a984776e197823d08ec94f176549a89a9efded477c456dc" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.3.10" +version = "4.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef137bbe35aab78bdb468ccfba75a5f4d8321ae011d34063770780545176af2d" +checksum = "2e53afce1efce6ed1f633cf0e57612fe51db54a1ee4fd8f8503d078fe02d69ae" dependencies = [ "anstream", "anstyle", + "bitflags 1.2.1", "clap_lex", "strsim", ] @@ -1143,47 +1128,37 @@ name = "clru" version = "0.5.0" source = "git+https://github.com/marmeladema/clru-rs.git?rev=71ca566#71ca566915f21f3c308091ca7756a91b0f8b5afc" -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - [[package]] name = "coins-bip32" -version = "0.7.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634c509653de24b439672164bbf56f5f582a2ab0e313d3b0f6af0b7345cf2560" +checksum = "b30a84aab436fcb256a2ab3c80663d8aec686e6bae12827bb05fef3e1e439c9f" dependencies = [ "bincode", "bs58", "coins-core", - "digest 0.10.7", - "getrandom 0.2.8", + "digest 0.10.6", + "getrandom 0.2.9", "hmac 0.12.1", - "k256", + "k256 0.13.1", "lazy_static", - "serde 1.0.147", + "serde 1.0.163", "sha2 0.10.6", "thiserror", ] [[package]] name = "coins-bip39" -version = "0.7.0" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a11892bcac83b4c6e95ab84b5b06c76d9d70ad73548dd07418269c5c7977171" +checksum = "84f4d04ee18e58356accd644896aeb2094ddeafb6a713e056cef0c0a8e468c15" dependencies = [ "bitvec 0.17.4", "coins-bip32", - "getrandom 0.2.8", - "hex", + "getrandom 0.2.9", "hmac 0.12.1", - "pbkdf2 0.11.0", + "once_cell", + "pbkdf2 0.12.1", "rand 0.8.5", "sha2 0.10.6", "thiserror", @@ -1191,19 +1166,18 @@ dependencies = [ [[package]] name = "coins-core" -version = "0.7.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c94090a6663f224feae66ab01e41a2555a8296ee07b5f20dab8888bdefc9f617" +checksum = "9b949a1c63fb7eb591eb7ba438746326aedf0ae843e51ec92ba6bec5bb382c4f" dependencies = [ - "base58check", - "base64 0.12.3", + "base64 0.21.0", "bech32 0.7.3", - "blake2", - "digest 0.10.7", - "generic-array 0.14.6", + "bs58", + "digest 0.10.6", + "generic-array 0.14.7", "hex", "ripemd", - "serde 1.0.147", + "serde 1.0.163", "serde_derive", "sha2 0.10.6", "sha3", @@ -1233,7 +1207,7 @@ checksum = "b6eee477a4a8a72f4addd4de416eb56d54bc307b284d6601bafdee1f4ea462d1" dependencies = [ "once_cell", "owo-colors 1.3.0", - "tracing-core 0.1.30", + "tracing-core 0.1.31", "tracing-error", ] @@ -1260,12 +1234,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b1b9d958c2b1368a663f05538fc1b5975adce1e19f435acceae987aceeeb369" dependencies = [ "lazy_static", - "nom 5.1.2", + "nom 5.1.3", "rust-ini", - "serde 1.0.147", + "serde 1.0.163", "serde-hjson", "serde_json", - "toml", + "toml 0.5.9", "yaml-rust", ] @@ -1275,20 +1249,20 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b72b06487a0d4683349ad74d62e87ad639b09667082b3c495c5b6bab7d84b3da" dependencies = [ - "windows", + "windows 0.44.0", ] [[package]] name = "const-oid" -version = "0.9.1" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" +checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747" [[package]] name = "constant_time_eq" -version = "0.1.5" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" [[package]] name = "contracts" @@ -1301,15 +1275,6 @@ dependencies = [ "syn 1.0.109", ] -[[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 = "core-foundation" version = "0.9.3" @@ -1322,9 +1287,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "corosensei" @@ -1341,9 +1306,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" dependencies = [ "libc", ] @@ -1367,7 +1332,7 @@ dependencies = [ "cranelift-codegen-meta", "cranelift-codegen-shared", "cranelift-entity", - "gimli", + "gimli 0.26.2", "log", "regalloc", "smallvec", @@ -1428,35 +1393,35 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.6" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.12", + "crossbeam-utils 0.8.15", ] [[package]] name = "crossbeam-deque" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ "cfg-if 1.0.0", "crossbeam-epoch", - "crossbeam-utils 0.8.12", + "crossbeam-utils 0.8.15", ] [[package]] name = "crossbeam-epoch" -version = "0.9.11" +version = "0.9.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" dependencies = [ "autocfg", "cfg-if 1.0.0", - "crossbeam-utils 0.8.12", - "memoffset 0.6.5", + "crossbeam-utils 0.8.15", + "memoffset 0.8.0", "scopeguard", ] @@ -1473,9 +1438,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.12" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" dependencies = [ "cfg-if 1.0.0", ] @@ -1492,7 +1457,19 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle 2.4.1", + "zeroize", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" +dependencies = [ + "generic-array 0.14.7", "rand_core 0.6.4", "subtle 2.4.1", "zeroize", @@ -1504,7 +1481,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "typenum", ] @@ -1524,7 +1501,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "subtle 2.4.1", ] @@ -1534,7 +1511,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "subtle 2.4.1", ] @@ -1604,55 +1581,11 @@ dependencies = [ "zeroize", ] -[[package]] -name = "cxx" -version = "1.0.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97abf9f0eca9e52b7f81b945524e76710e6cb2366aead23b7d4fbf72e281f888" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc32cc5fea1d894b77d269ddb9f192110069a8a9c1f1d441195fba90553dea3" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn 1.0.109", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca220e4794c934dc6b1207c3b42856ad4c302f2df1712e9f8d2eec5afaacf1f" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b846f081361125bfc8dc9d3940c84e1fd83ba54bbca7b17cd29483c828be0704" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "darling" -version = "0.14.2" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" +checksum = "0558d22a7b463ed0241e993f76f09f30b126687447751a8638587b864e4b3944" dependencies = [ "darling_core", "darling_macro", @@ -1660,39 +1593,48 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.2" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" +checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] name = "darling_macro" -version = "0.14.2" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" +checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" dependencies = [ "darling_core", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] name = "data-encoding" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" +checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" [[package]] name = "der" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", +] + +[[package]] +name = "der" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7ed52955ce76b1554f509074bb357d3fb8ac9b51288a65a3fd480d1dfba946" dependencies = [ "const-oid", "zeroize", @@ -1753,16 +1695,17 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] name = "digest" -version = "0.10.7" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ - "block-buffer 0.10.3", + "block-buffer 0.10.4", + "const-oid", "crypto-common", "subtle 2.4.1", ] @@ -1816,7 +1759,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.15", ] [[package]] @@ -1833,9 +1776,9 @@ checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" [[package]] name = "dunce" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" [[package]] name = "dyn-clone" @@ -1849,7 +1792,7 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" dependencies = [ - "bitflags 1.3.2", + "bitflags 1.2.1", "byteorder", "lazy_static", "proc-macro-error", @@ -1875,10 +1818,24 @@ version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", + "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.7", + "digest 0.10.6", + "elliptic-curve 0.13.5", + "rfc6979 0.4.0", + "signature 2.1.0", + "spki", ] [[package]] @@ -1887,8 +1844,8 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ - "serde 1.0.147", - "signature", + "serde 1.0.163", + "signature 1.6.4", ] [[package]] @@ -1900,7 +1857,7 @@ dependencies = [ "curve25519-dalek-ng", "hex", "rand_core 0.6.4", - "serde 1.0.147", + "serde 1.0.163", "sha2 0.9.9", "thiserror", "zeroize", @@ -1916,7 +1873,7 @@ dependencies = [ "ed25519", "merlin", "rand 0.7.3", - "serde 1.0.147", + "serde 1.0.163", "serde_bytes", "sha2 0.9.9", "zeroize", @@ -1936,9 +1893,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "elliptic-curve" @@ -1946,44 +1903,61 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ - "base16ct", - "crypto-bigint", - "der", - "digest 0.10.7", - "ff", - "generic-array 0.14.6", - "group", + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest 0.10.6", + "ff 0.12.1", + "generic-array 0.14.7", + "group 0.12.1", + "rand_core 0.6.4", + "sec1 0.3.0", + "subtle 2.4.1", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.2", + "digest 0.10.6", + "ff 0.13.0", + "generic-array 0.14.7", + "group 0.13.0", "pkcs8", "rand_core 0.6.4", - "sec1", + "sec1 0.7.2", "subtle 2.4.1", "zeroize", ] [[package]] name = "encoding_rs" -version = "0.8.31" +version = "0.8.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" dependencies = [ "cfg-if 1.0.0", ] [[package]] name = "enr" -version = "0.7.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "492a7e5fc2504d5fdce8e124d3e263b244a68b283cac67a69eda0cd43e0aebad" +checksum = "cf56acd72bb22d2824e66ae8e9e5ada4d0de17a69c7fd35569dde2ada8ec9116" dependencies = [ "base64 0.13.1", - "bs58", "bytes", "hex", - "k256", + "k256 0.13.1", "log", "rand 0.8.5", "rlp", - "serde 1.0.147", + "serde 1.0.163", "sha3", "zeroize", ] @@ -2010,43 +1984,38 @@ dependencies = [ [[package]] name = "enumset" -version = "1.0.12" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19be8061a06ab6f3a6cf21106c873578bf01bd42ad15e0311a9c76161cb1c753" +checksum = "e875f1719c16de097dee81ed675e2d9bb63096823ed3f0ca827b7dea3028bbbb" dependencies = [ "enumset_derive", ] [[package]] name = "enumset_derive" -version = "0.6.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e7b551eba279bf0fa88b83a46330168c1560a52a94f5126f892f0b364ab3e0" +checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" dependencies = [ "darling", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] -name = "erased-serde" -version = "0.3.25" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2b0c2380453a92ea8b6c8e5f64ecaafccddde8ceab55ff7a8ac1029f894569" -dependencies = [ - "serde 1.0.147", -] +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] -name = "errno" -version = "0.2.8" +name = "erased-serde" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "4f2b0c2380453a92ea8b6c8e5f64ecaafccddde8ceab55ff7a8ac1029f894569" dependencies = [ - "errno-dragonfly", - "libc", - "winapi", + "serde 1.0.163", ] [[package]] @@ -2087,7 +2056,7 @@ checksum = "f5584ba17d7ab26a8a7284f13e5bd196294dd2f2d79773cff29b9e9edef601a6" dependencies = [ "log", "once_cell", - "serde 1.0.147", + "serde 1.0.163", "serde_json", ] @@ -2097,15 +2066,15 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" dependencies = [ - "aes 0.8.2", + "aes 0.8.3", "ctr", - "digest 0.10.7", + "digest 0.10.6", "hex", "hmac 0.12.1", "pbkdf2 0.11.0", "rand 0.8.5", "scrypt", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "sha2 0.10.6", "sha3", @@ -2123,7 +2092,7 @@ dependencies = [ "hex", "once_cell", "regex", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "sha3", "thiserror", @@ -2243,21 +2212,21 @@ dependencies = [ [[package]] name = "ethers-addressbook" -version = "2.0.0" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1e010165c08a2a3fa43c0bb8bc9d596f079a021aaa2cc4e8d921df09709c95" +checksum = "7b856b7b8ff5c961093cb8efe151fbcce724b451941ce20781de11a531ccd578" dependencies = [ "ethers-core", "once_cell", - "serde 1.0.147", + "serde 1.0.163", "serde_json", ] [[package]] name = "ethers-contract" -version = "2.0.0" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be33fd47a06cc8f97caf614cf7cf91af9dd6dcd767511578895fa884b430c4b8" +checksum = "e066a0d9cfc70c454672bf16bb433b0243427420076dc5b2f49c448fb5a10628" dependencies = [ "ethers-contract-abigen", "ethers-contract-derive", @@ -2267,83 +2236,73 @@ dependencies = [ "hex", "once_cell", "pin-project", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "thiserror", ] [[package]] name = "ethers-contract-abigen" -version = "2.0.0" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60d9f9ecb4a18c1693de954404b66e0c9df31dac055b411645e38c4efebf3dbc" +checksum = "c113e3e86b6bc16d98484b2c3bb2d01d6fed9f489fe2e592e5cc87c3024d616b" dependencies = [ "Inflector", - "cfg-if 1.0.0", "dunce", "ethers-core", - "ethers-etherscan", "eyre", - "getrandom 0.2.8", "hex", - "prettyplease 0.1.24", + "prettyplease 0.2.4", "proc-macro2", "quote", "regex", - "reqwest", - "serde 1.0.147", + "serde 1.0.163", "serde_json", - "syn 1.0.109", - "tokio", - "toml", - "url", + "syn 2.0.15", + "toml 0.7.6", "walkdir", ] [[package]] name = "ethers-contract-derive" -version = "2.0.0" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "001b33443a67e273120923df18bab907a0744ad4b5fef681a8b0691f2ee0f3de" +checksum = "8c3fb5adee25701c79ec58fcf2c63594cd8829bc9ad6037ff862d5a111101ed2" dependencies = [ + "Inflector", "ethers-contract-abigen", "ethers-core", - "eyre", "hex", "proc-macro2", "quote", "serde_json", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] name = "ethers-core" -version = "2.0.0" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5925cba515ac18eb5c798ddf6069cc33ae00916cb08ae64194364a1b35c100b" +checksum = "6da5fa198af0d3be20c19192df2bd9590b92ce09a8421e793bec8851270f1b05" dependencies = [ "arrayvec 0.7.2", "bytes", - "cargo_metadata 0.15.3", + "cargo_metadata 0.15.4", "chrono", - "convert_case", - "elliptic-curve", + "elliptic-curve 0.13.5", "ethabi", - "generic-array 0.14.6", - "getrandom 0.2.8", + "generic-array 0.14.7", "hex", - "k256", + "k256 0.13.1", "num_enum", "once_cell", "open-fastrlp", - "proc-macro2", "rand 0.8.5", "rlp", - "rlp-derive", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "strum", - "syn 1.0.109", + "syn 2.0.15", "tempfile", "thiserror", "tiny-keccak", @@ -2352,16 +2311,14 @@ dependencies = [ [[package]] name = "ethers-etherscan" -version = "2.0.0" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d769437fafd0b47ea8b95e774e343c5195c77423f0f54b48d11c0d9ed2148ad" +checksum = "84ebb401ba97c6f5af278c2c9936c4546cad75dec464b439ae6df249906f4caa" dependencies = [ "ethers-core", - "getrandom 0.2.8", "reqwest", "semver 1.0.17", - "serde 1.0.147", - "serde-aux", + "serde 1.0.163", "serde_json", "thiserror", "tracing 0.1.37", @@ -2369,9 +2326,9 @@ dependencies = [ [[package]] name = "ethers-middleware" -version = "2.0.0" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7dd311b76eab9d15209e4fd16bb419e25543709cbdf33079e8923dfa597517c" +checksum = "740f4a773c19dd6d6a68c8c2e0996c096488d38997d524e21dc612c55da3bd24" dependencies = [ "async-trait", "auto_impl", @@ -2380,11 +2337,12 @@ dependencies = [ "ethers-etherscan", "ethers-providers", "ethers-signers", + "futures-channel", "futures-locks", "futures-util", "instant", "reqwest", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "thiserror", "tokio", @@ -2395,27 +2353,27 @@ dependencies = [ [[package]] name = "ethers-providers" -version = "2.0.0" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed7174af93619e81844d3d49887106a3721e5caecdf306e0b824bfb4316db3be" +checksum = "56b498fd2a6c019d023e43e83488cd1fb0721f299055975aa6bac8dbf1e95f2c" dependencies = [ "async-trait", "auto_impl", "base64 0.21.0", + "bytes", "enr", "ethers-core", "futures-core", "futures-timer", "futures-util", - "getrandom 0.2.8", "hashers", "hex", "http", + "instant", "once_cell", - "parking_lot 0.11.2", "pin-project", "reqwest", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "thiserror", "tokio", @@ -2424,21 +2382,20 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-timer", "web-sys", "ws_stream_wasm", ] [[package]] name = "ethers-signers" -version = "2.0.0" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d45ff294473124fd5bb96be56516ace179eef0eaec5b281f68c953ddea1a8bf" +checksum = "02c4b7e15f212fa7cc2e1251868320221d4ff77a3d48068e69f47ce1c491df2d" dependencies = [ "async-trait", "coins-bip32", "coins-bip39", - "elliptic-curve", + "elliptic-curve 0.13.5", "eth-keystore", "ethers-core", "hex", @@ -2455,7 +2412,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b36f34b0325008d05b0e9be8361bfa8a0fb905f10de0d951c2621c59e811cb91" dependencies = [ "conpty", - "nix 0.26.2", + "nix", "ptyprocess", "regex", ] @@ -2484,13 +2441,24 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] +[[package]] +name = "fd-lock" +version = "3.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" +dependencies = [ + "cfg-if 1.0.0", + "rustix 0.38.4", + "windows-sys 0.48.0", +] + [[package]] name = "ferveo" version = "0.1.1" @@ -2508,7 +2476,7 @@ dependencies = [ "blake2", "blake2b_simd", "borsh", - "digest 0.10.7", + "digest 0.10.6", "ed25519-dalek", "either", "ferveo-common", @@ -2520,7 +2488,7 @@ dependencies = [ "num 0.4.0", "rand 0.7.3", "rand 0.8.5", - "serde 1.0.147", + "serde 1.0.163", "serde_bytes", "serde_json", "subproductdomain", @@ -2537,7 +2505,7 @@ dependencies = [ "ark-ec", "ark-serialize", "ark-std", - "serde 1.0.147", + "serde 1.0.163", "serde_bytes", ] @@ -2553,22 +2521,20 @@ dependencies = [ ] [[package]] -name = "file-lock" -version = "2.1.6" +name = "ff" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0815fc2a1924e651b71ae6d13df07b356a671a09ecaf857dbd344a2ba937a496" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ - "cc", - "libc", - "mktemp", - "nix 0.24.2", + "rand_core 0.6.4", + "subtle 2.4.1", ] [[package]] name = "file-serve" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e43addbb09a5dcb5609cb44a01a79e67716fe40b50c109f50112ef201a8c7c59" +checksum = "547ebf393d987692a02b5d2be1c0b398b16a5b185c23a047c1d3fc3050d6d803" dependencies = [ "log", "mime_guess", @@ -2577,14 +2543,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.18" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b9663d381d07ae25dc88dbdf27df458faa83a9b25336bcac83d5e452b5fc9d3" +checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", - "windows-sys 0.42.0", + "redox_syscall 0.2.16", + "windows-sys 0.48.0", ] [[package]] @@ -2607,12 +2573,12 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.7.1", ] [[package]] @@ -2671,9 +2637,9 @@ dependencies = [ [[package]] name = "fs_extra" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "funty" @@ -2747,7 +2713,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.15", ] [[package]] @@ -2767,6 +2733,10 @@ 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" @@ -2806,12 +2776,13 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -2829,9 +2800,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -2847,17 +2818,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" dependencies = [ "fallible-iterator", - "indexmap", + "indexmap 1.9.3", "stable_deref_trait", ] +[[package]] +name = "gimli" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" + [[package]] name = "git2" version = "0.13.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f29229cc1b24c0e6062f6e742aa3e256492a5323365e5ed3413599f8a5eff7d6" dependencies = [ - "bitflags 1.3.2", + "bitflags 1.2.1", "libc", "libgit2-sys", "log", @@ -2868,9 +2845,21 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.0" +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 = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] [[package]] name = "group" @@ -2878,18 +2867,29 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ - "ff", + "ff 0.12.1", "memuse", "rand_core 0.6.4", "subtle 2.4.1", ] [[package]] -name = "group-threshold-cryptography" -version = "0.1.0" -source = "git+https://github.com/anoma/ferveo?rev=e5abd0acc938da90140351a65a26472eb495ce4d#e5abd0acc938da90140351a65a26472eb495ce4d" -dependencies = [ - "anyhow", +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 2.4.1", +] + +[[package]] +name = "group-threshold-cryptography" +version = "0.1.0" +source = "git+https://github.com/anoma/ferveo?rev=e5abd0acc938da90140351a65a26472eb495ce4d#e5abd0acc938da90140351a65a26472eb495ce4d" +dependencies = [ + "anyhow", "ark-bls12-381", "ark-ec", "ark-ff", @@ -2930,9 +2930,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.15" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" dependencies = [ "bytes", "fnv", @@ -2940,10 +2940,10 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", - "tokio-util 0.7.4", + "tokio-util 0.7.8", "tracing 0.1.37", ] @@ -2971,6 +2971,12 @@ 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" @@ -2982,9 +2988,9 @@ dependencies = [ [[package]] name = "hdpath" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dafb09e5d85df264339ad786a147d9de1da13687a3697c52244297e5e7c32d9c" +checksum = "dfa5bc9db2c17d2660f53ce217b778d06d68de13d1cd01c0f4e5de4b7918935f" dependencies = [ "byteorder", ] @@ -3006,7 +3012,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" dependencies = [ "base64 0.13.1", - "bitflags 1.3.2", + "bitflags 1.2.1", "bytes", "headers-core", "http", @@ -3026,24 +3032,24 @@ dependencies = [ [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.1.19" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" dependencies = [ "libc", ] [[package]] name = "hermit-abi" -version = "0.3.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" [[package]] name = "hex" @@ -3087,7 +3093,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.7", + "digest 0.10.6", ] [[package]] @@ -3108,7 +3114,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" dependencies = [ "digest 0.9.0", - "generic-array 0.14.6", + "generic-array 0.14.7", "hmac 0.8.1", ] @@ -3120,9 +3126,9 @@ checksum = "77e806677ce663d0a199541030c816847b36e8dc095f70dae4a4f4ad63da5383" [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -3140,12 +3146,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "http-range-header" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" - [[package]] name = "httparse" version = "1.8.0" @@ -3171,14 +3171,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" dependencies = [ "humantime", - "serde 1.0.147", + "serde 1.0.163", ] [[package]] name = "hyper" -version = "0.14.23" +version = "0.14.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" dependencies = [ "bytes", "futures-channel", @@ -3209,7 +3209,7 @@ dependencies = [ "headers", "http", "hyper", - "hyper-rustls 0.22.1", + "hyper-rustls", "rustls-native-certs 0.5.0", "tokio", "tokio-rustls 0.22.0", @@ -3235,19 +3235,6 @@ dependencies = [ "webpki-roots 0.21.1", ] -[[package]] -name = "hyper-rustls" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" -dependencies = [ - "http", - "hyper", - "rustls 0.20.7", - "tokio", - "tokio-rustls 0.23.4", -] - [[package]] name = "hyper-timeout" version = "0.4.1" @@ -3275,26 +3262,25 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi", + "windows 0.48.0", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", ] [[package]] @@ -3311,11 +3297,11 @@ dependencies = [ "ibc-proto", "ics23", "num-traits 0.2.15", - "parking_lot 0.12.1", + "parking_lot", "primitive-types", "prost", "safe-regex", - "serde 1.0.147", + "serde 1.0.163", "serde_derive", "serde_json", "sha2 0.10.6", @@ -3338,7 +3324,7 @@ dependencies = [ "bytes", "flex-error", "prost", - "serde 1.0.147", + "serde 1.0.163", "subtle-encoding", "tendermint-proto", "tonic", @@ -3355,15 +3341,15 @@ dependencies = [ "bitcoin", "bs58", "bytes", - "crossbeam-channel 0.5.6", - "digest 0.10.7", + "crossbeam-channel 0.5.8", + "digest 0.10.6", "dirs-next", "ed25519", "ed25519-dalek", "ed25519-dalek-bip32", "flex-error", "futures", - "generic-array 0.14.6", + "generic-array 0.14.7", "hdpath", "hex", "http", @@ -3381,11 +3367,11 @@ dependencies = [ "ripemd", "secp256k1", "semver 1.0.17", - "serde 1.0.147", + "serde 1.0.163", "serde_derive", "serde_json", "sha2 0.10.6", - "signature", + "signature 1.6.4", "strum", "subtle-encoding", "tendermint", @@ -3397,10 +3383,10 @@ dependencies = [ "tiny-bip39 1.0.0", "tiny-keccak", "tokio", - "toml", + "toml 0.5.9", "tonic", "tracing 0.1.37", - "uuid 1.2.1", + "uuid 1.3.2", ] [[package]] @@ -3420,7 +3406,7 @@ dependencies = [ "primitive-types", "prost", "safe-regex", - "serde 1.0.147", + "serde 1.0.163", "serde_derive", "serde_json", "subtle-encoding", @@ -3499,7 +3485,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -3519,7 +3505,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5ad43a3f5795945459d577f6589cf62a476e92c79b75e70cd954364e14ce17b" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -3534,18 +3520,28 @@ version = "0.7.1" source = "git+https://github.com/heliaxdev/index-set?tag=v0.7.1#dc24cdbbe3664514d59f1a4c4031863fc565f1c2" dependencies = [ "borsh", - "serde 1.0.147", + "serde 1.0.163", ] [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", - "serde 1.0.147", + "serde 1.0.163", +] + +[[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", ] [[package]] @@ -3554,7 +3550,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -3589,28 +3585,30 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.4" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ + "hermit-abi 0.3.1", "libc", - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] name = "ipnet" -version = "2.5.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f88c5561171189e69df9d98bcf18fd5f9558300f7ea7b801eb8a0fd748bd8745" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" [[package]] name = "is-terminal" -version = "0.4.8" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24fddda5af7e54bf7da53067d6e802dbcc381d0a8eef629df528e3ebf68755cb" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ - "hermit-abi 0.3.2", - "rustix 0.38.3", + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix 0.37.13", "windows-sys 0.48.0", ] @@ -3625,24 +3623,24 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "jobserver" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "68c16e1bfd491478ab155fd8b4896b86f9ede344949b641e61501e07c2b8b4d5" dependencies = [ "wasm-bindgen", ] @@ -3655,8 +3653,8 @@ checksum = "a575df5f985fe1cd5b2b05664ff6accfc46559032b954529fd225a2168d27b0f" dependencies = [ "bitvec 1.0.1", "bls12_381", - "ff", - "group", + "ff 0.12.1", + "group 0.12.1", "rand_core 0.6.4", "subtle 2.4.1", ] @@ -3668,17 +3666,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" dependencies = [ "cfg-if 1.0.0", - "ecdsa", - "elliptic-curve", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", "sha2 0.10.6", - "sha3", +] + +[[package]] +name = "k256" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +dependencies = [ + "cfg-if 1.0.0", + "ecdsa 0.16.7", + "elliptic-curve 0.13.5", + "once_cell", + "sha2 0.10.6", + "signature 2.1.0", ] [[package]] name = "keccak" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9b7d56ba4a8344d6be9729995e6b06f928af29998cdf79fe390cbf6b1fee838" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] [[package]] name = "lazy_static" @@ -3705,7 +3719,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" dependencies = [ "arrayvec 0.5.2", - "bitflags 1.3.2", + "bitflags 1.2.1", "cfg-if 1.0.0", "ryu", "static_assertions", @@ -3792,7 +3806,7 @@ dependencies = [ "libsecp256k1-gen-ecmult", "libsecp256k1-gen-genmult", "rand 0.8.5", - "serde 1.0.147", + "serde 1.0.163", "sha2 0.9.9", "typenum", ] @@ -3839,9 +3853,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" +checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db" dependencies = [ "cc", "libc", @@ -3849,29 +3863,20 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "link-cplusplus" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" -dependencies = [ - "cc", -] - [[package]] name = "linked-hash-map" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", ] [[package]] name = "linux-raw-sys" -version = "0.1.4" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" [[package]] name = "linux-raw-sys" @@ -3891,12 +3896,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if 1.0.0", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "loupe" @@ -3904,7 +3906,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b6a72dfa44fe15b5e76b94307eeb2ff995a8c5b283b55008940c02e0c5b634d" dependencies = [ - "indexmap", + "indexmap 1.9.3", "loupe-derive", "rustversion", ] @@ -3928,16 +3930,25 @@ dependencies = [ "libc", ] +[[package]] +name = "mach2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8" +dependencies = [ + "libc", +] + [[package]] name = "madato" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c36112680b23ee7a5d2a10e56b59a74e4028963e9080b8abec4e69d0d773dc06" dependencies = [ - "indexmap", + "indexmap 1.9.3", "linked-hash-map", "regex", - "serde 1.0.147", + "serde 1.0.163", "serde_derive", "serde_yaml", ] @@ -3968,9 +3979,9 @@ dependencies = [ "bls12_381", "borsh", "byteorder", - "ff", + "ff 0.12.1", "fpe", - "group", + "group 0.12.1", "hex", "incrementalmerkletree", "jubjub", @@ -3994,8 +4005,8 @@ dependencies = [ "blake2b_simd", "bls12_381", "directories", - "getrandom 0.2.8", - "group", + "getrandom 0.2.9", + "group 0.12.1", "itertools", "jubjub", "lazy_static", @@ -4046,9 +4057,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memmap2" -version = "0.5.8" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" dependencies = [ "libc", ] @@ -4071,6 +4082,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + [[package]] name = "memuse" version = "0.2.1" @@ -4097,9 +4117,9 @@ dependencies = [ [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" @@ -4119,36 +4139,45 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.5.4" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] [[package]] name = "minreq" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c785bc6027fd359756e538541c8624012ba3776d3d3fe123885643092ed4132" +checksum = "cb6c6973f78ef55d0e5fc04fdb8f9ad67c87c9e86bca0ff77b6a3102b0eb36b7" dependencies = [ - "lazy_static", "log", - "rustls 0.20.7", + "once_cell", + "rustls 0.20.8", "webpki 0.22.0", - "webpki-roots 0.22.5", + "webpki-roots 0.22.6", ] [[package]] name = "mio" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -4157,27 +4186,18 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94c7128ba23c81f6471141b90f17654f89ef44a56e14b8a4dd0fddfccd655277" -[[package]] -name = "mktemp" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "975de676448231fcde04b9149d2543077e166b78fc29eae5aa219e7928410da2" -dependencies = [ - "uuid 0.8.2", -] - [[package]] name = "moka" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b9268097a2cf211ac9955b1cc95e80fa84fff5c2d13ba292916445dc8a311f" +checksum = "19ca9b167ed904bc89a2f64c4be5014615c26fd9c4ddd2042c6094744c7df11a" dependencies = [ - "crossbeam-channel 0.5.6", + "crossbeam-channel 0.5.8", "crossbeam-epoch", - "crossbeam-utils 0.8.12", + "crossbeam-utils 0.8.15", "num_cpus", "once_cell", - "parking_lot 0.12.1", + "parking_lot", "quanta", "rustc_version 0.4.0", "scheduled-thread-pool", @@ -4186,7 +4206,7 @@ dependencies = [ "tagptr", "thiserror", "triomphe", - "uuid 1.2.1", + "uuid 1.3.2", ] [[package]] @@ -4196,32 +4216,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" [[package]] -name = "multimap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" - -[[package]] -name = "multipart" -version = "0.18.0" +name = "multer" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182" +checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" dependencies = [ - "buf_redux", + "bytes", + "encoding_rs", + "futures-util", + "http", "httparse", "log", + "memchr", "mime", - "mime_guess", - "quick-error", - "rand 0.8.5", - "safemem", - "tempfile", - "twoway", + "spin 0.9.8", + "version_check", ] +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + [[package]] name = "namada" -version = "0.19.0" +version = "0.20.0" dependencies = [ "assert_matches", "async-trait", @@ -4261,7 +4281,7 @@ dependencies = [ "rand_core 0.6.4", "rayon", "ripemd", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "sha2 0.9.9", "slip10_ed25519", @@ -4272,9 +4292,9 @@ dependencies = [ "tiny-bip39 0.8.2", "tiny-hderive", "tokio", - "toml", + "toml 0.5.9", "tracing 0.1.37", - "tracing-subscriber 0.3.16", + "tracing-subscriber 0.3.17", "wasm-instrument", "wasmer", "wasmer-cache", @@ -4289,7 +4309,7 @@ dependencies = [ [[package]] name = "namada_apps" -version = "0.19.0" +version = "0.20.0" dependencies = [ "ark-serialize", "ark-std", @@ -4316,13 +4336,14 @@ dependencies = [ "ethbridge-events", "ethbridge-governance-events", "eyre", + "fd-lock", "ferveo", "ferveo-common", - "file-lock", "flate2", "futures", "git2", "itertools", + "lazy_static", "libc", "libloading", "masp_primitives", @@ -4348,7 +4369,7 @@ dependencies = [ "rlimit", "rocksdb", "rpassword", - "serde 1.0.147", + "serde 1.0.163", "serde_bytes", "serde_json", "sha2 0.9.9", @@ -4362,14 +4383,14 @@ dependencies = [ "thiserror", "tokio", "tokio-test", - "toml", + "toml 0.5.9", "tonic", "tower", "tower-abci", "tracing 0.1.37", "tracing-appender", "tracing-log", - "tracing-subscriber 0.3.16", + "tracing-subscriber 0.3.17", "warp", "winapi", "zeroize", @@ -4377,7 +4398,7 @@ dependencies = [ [[package]] name = "namada_core" -version = "0.19.0" +version = "0.20.0" dependencies = [ "ark-bls12-381", "ark-ec", @@ -4415,7 +4436,7 @@ dependencies = [ "rand 0.8.5", "rand_core 0.6.4", "rayon", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "sha2 0.9.9", "sparse-merkle-tree", @@ -4424,17 +4445,17 @@ dependencies = [ "test-log", "thiserror", "tiny-keccak", - "toml", + "toml 0.5.9", "tonic-build", "tracing 0.1.37", - "tracing-subscriber 0.3.16", + "tracing-subscriber 0.3.17", "uint", "zeroize", ] [[package]] name = "namada_encoding_spec" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "itertools", @@ -4445,7 +4466,7 @@ dependencies = [ [[package]] name = "namada_ethereum_bridge" -version = "0.19.0" +version = "0.20.0" dependencies = [ "assert_matches", "borsh", @@ -4458,18 +4479,18 @@ dependencies = [ "namada_macros", "namada_proof_of_stake", "rand 0.8.5", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "tendermint", "tendermint-proto", "tendermint-rpc", - "toml", + "toml 0.5.9", "tracing 0.1.37", ] [[package]] name = "namada_macros" -version = "0.19.0" +version = "0.20.0" dependencies = [ "proc-macro2", "quote", @@ -4478,7 +4499,7 @@ dependencies = [ [[package]] name = "namada_proof_of_stake" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "data-encoding", @@ -4491,12 +4512,12 @@ dependencies = [ "test-log", "thiserror", "tracing 0.1.37", - "tracing-subscriber 0.3.16", + "tracing-subscriber 0.3.17", ] [[package]] name = "namada_test_utils" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "namada_core", @@ -4505,11 +4526,13 @@ dependencies = [ [[package]] name = "namada_tests" -version = "0.19.0" +version = "0.20.0" dependencies = [ "assert_cmd", + "async-trait", "borsh", "chrono", + "clap", "color-eyre", "concat-idents", "data-encoding", @@ -4523,12 +4546,14 @@ dependencies = [ "ibc-relayer", "ibc-relayer-types", "itertools", + "lazy_static", "namada", "namada_apps", "namada_core", "namada_test_utils", "namada_tx_prelude", "namada_vp_prelude", + "num-traits 0.2.15", "once_cell", "pretty_assertions", "proptest", @@ -4541,14 +4566,14 @@ dependencies = [ "tempfile", "test-log", "tokio", - "toml", + "toml 0.5.9", "tracing 0.1.37", - "tracing-subscriber 0.3.16", + "tracing-subscriber 0.3.17", ] [[package]] name = "namada_tx_prelude" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "masp_primitives", @@ -4562,7 +4587,7 @@ dependencies = [ [[package]] name = "namada_vm_env" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "masp_primitives", @@ -4571,7 +4596,7 @@ dependencies = [ [[package]] name = "namada_vp_prelude" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "namada_core", @@ -4600,24 +4625,13 @@ dependencies = [ "tempfile", ] -[[package]] -name = "nix" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" -dependencies = [ - "bitflags 1.3.2", - "cfg-if 1.0.0", - "libc", -] - [[package]] name = "nix" version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ - "bitflags 1.3.2", + "bitflags 1.2.1", "cfg-if 1.0.0", "libc", "memoffset 0.7.1", @@ -4627,9 +4641,9 @@ dependencies = [ [[package]] name = "nom" -version = "5.1.2" +version = "5.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +checksum = "08959a387a676302eebf4ddbcbc611da04285579f76f88ee0506c63b1a61dd4b" dependencies = [ "lexical-core", "memchr", @@ -4638,9 +4652,9 @@ dependencies = [ [[package]] name = "nom" -version = "7.1.1" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", @@ -4654,9 +4668,9 @@ checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" [[package]] name = "ntapi" -version = "0.3.7" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" dependencies = [ "winapi", ] @@ -4692,7 +4706,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" dependencies = [ "num-bigint 0.4.3", - "num-complex 0.4.2", + "num-complex 0.4.3", "num-integer", "num-iter", "num-rational 0.4.1", @@ -4719,7 +4733,7 @@ dependencies = [ "autocfg", "num-integer", "num-traits 0.2.15", - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -4734,9 +4748,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" +checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" dependencies = [ "num-traits 0.2.15", ] @@ -4795,7 +4809,7 @@ dependencies = [ "num-bigint 0.4.3", "num-integer", "num-traits 0.2.15", - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -4827,39 +4841,39 @@ dependencies = [ "num 0.4.0", "num-derive", "num-traits 0.2.15", - "serde 1.0.147", + "serde 1.0.163", "serde_derive", ] [[package]] name = "num_cpus" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi 0.1.19", + "hermit-abi 0.2.6", "libc", ] [[package]] name = "num_enum" -version = "0.5.11" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.11" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ - "proc-macro-crate 1.2.1", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -4870,15 +4884,15 @@ checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" dependencies = [ "crc32fast", "hashbrown 0.11.2", - "indexmap", + "indexmap 1.9.3", "memchr", ] [[package]] name = "object" -version = "0.29.0" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" dependencies = [ "memchr", ] @@ -4928,11 +4942,11 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.42" +version = "0.10.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" +checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" dependencies = [ - "bitflags 1.3.2", + "bitflags 1.2.1", "cfg-if 1.0.0", "foreign-types", "libc", @@ -4943,13 +4957,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -4960,11 +4974,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.77" +version = "0.9.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03b84c3b2d099b81f0953422b4d4ad58761589d0229b5506356afca05a3670a" +checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" dependencies = [ - "autocfg", "cc", "libc", "pkg-config", @@ -4978,7 +4991,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6624905ddd92e460ff0685567539ed1ac985b2dee4c92c7edcd64fce905b00c" dependencies = [ "ct-codecs", - "getrandom 0.2.8", + "getrandom 0.2.9", "subtle 2.4.1", "zeroize", ] @@ -5016,30 +5029,30 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "135590d8bdba2b31346f9cd1fb2a912329f5135e832a4f422942eb6ead8b6b3b" dependencies = [ - "group", + "group 0.12.1", ] [[package]] name = "parity-scale-codec" -version = "3.2.1" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "366e44391a8af4cfd6002ef6ba072bae071a96aafca98d7d448a34c5dca38b6a" +checksum = "5ddb756ca205bd108aee3c62c6d3c994e1df84a59b9d6d4a5ea42ee1fd5a9a28" dependencies = [ "arrayvec 0.7.2", "bitvec 1.0.1", "byte-slice-cast", "impl-trait-for-tuples", "parity-scale-codec-derive", - "serde 1.0.147", + "serde 1.0.163", ] [[package]] name = "parity-scale-codec-derive" -version = "3.1.3" +version = "3.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9299338969a3d2f491d65f140b00ddec470858402f888af98e8642fb5e8965cd" +checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" dependencies = [ - "proc-macro-crate 1.2.1", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 1.0.109", @@ -5051,17 +5064,6 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - [[package]] name = "parking_lot" version = "0.12.1" @@ -5069,34 +5071,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.4", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if 1.0.0", - "instant", - "libc", - "redox_syscall", - "smallvec", - "winapi", + "parking_lot_core", ] [[package]] name = "parking_lot_core" -version = "0.9.4" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -5121,22 +5109,11 @@ dependencies = [ "subtle 2.4.1", ] -[[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 2.4.1", -] - [[package]] name = "paste" -version = "1.0.9" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" [[package]] name = "pbkdf2" @@ -5154,7 +5131,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739" dependencies = [ "crypto-mac 0.11.1", - "password-hash 0.3.2", + "password-hash", ] [[package]] @@ -5163,10 +5140,17 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "digest 0.10.7", + "digest 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 0.12.1", - "password-hash 0.4.2", - "sha2 0.10.6", ] [[package]] @@ -5210,21 +5194,22 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.1.3" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +checksum = "e68e84bfb01f0507134eac1e9b410a12ba379d064eab48c50ba4ce329a527b70" dependencies = [ + "thiserror", "ucd-trie", ] [[package]] name = "petgraph" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" +checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 1.9.3", ] [[package]] @@ -5239,22 +5224,22 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.12" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -5271,19 +5256,19 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkcs8" -version = "0.9.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der", + "der 0.7.7", "spki", ] [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "poly1305" @@ -5304,9 +5289,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "predicates" -version = "2.1.3" +version = "2.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6bd09a7f7e68f3f0bf710fb7ab9c4615a488b58b5f653382a687701e458c92" +checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" dependencies = [ "difflib", "itertools", @@ -5315,15 +5300,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f883590242d3c6fc5bf50299011695fa6590c2c70eac95ee1bdb9a733ad1a2" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" [[package]] name = "predicates-tree" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54ff541861505aabf6ea722d2131ee980b8276e10a1297b94e896dd8b621850d" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" dependencies = [ "predicates-core", "termtree", @@ -5343,9 +5328,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebcd279d20a4a0a2404a33056388e950504d891c855c7975b9a8fef75f3bf04" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" dependencies = [ "proc-macro2", "syn 1.0.109", @@ -5353,12 +5338,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.6" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b69d39aab54d069e7f2fe8cb970493e7834601ca2d8c65fd7bbd183578080d1" +checksum = "1ceca8aaf45b5c46ec7ed39fff75f57290368c1846d33d24a122ca81416ab058" dependencies = [ "proc-macro2", - "syn 2.0.16", + "syn 2.0.15", ] [[package]] @@ -5381,18 +5366,17 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" dependencies = [ - "toml", + "toml 0.5.9", ] [[package]] name = "proc-macro-crate" -version = "1.2.1" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "thiserror", - "toml", + "toml_edit", ] [[package]] @@ -5421,9 +5405,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.58" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -5435,14 +5419,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" dependencies = [ "bit-set", - "bitflags 1.3.2", + "bitflags 1.2.1", "byteorder", "lazy_static", "num-traits 0.2.15", "rand 0.8.5", "rand_chacha 0.3.1", "rand_xorshift", - "regex-syntax 0.6.28", + "regex-syntax 0.6.29", "rusty-fork", "tempfile", "unarray", @@ -5480,7 +5464,7 @@ dependencies = [ "log", "multimap", "petgraph", - "prettyplease 0.1.24", + "prettyplease 0.1.25", "prost", "prost-types", "regex", @@ -5537,7 +5521,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e05aef7befb11a210468a2d77d978dde2c6381a0381e33beb575e91f57fe8cf" dependencies = [ - "nix 0.26.2", + "nix", ] [[package]] @@ -5546,23 +5530,23 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34f197a544b0c9ab3ae46c359a7ec9cbbb5c7bf97054266fecb7ead794a181d6" dependencies = [ - "bitflags 1.3.2", + "bitflags 1.2.1", "memchr", "unicase", ] [[package]] name = "quanta" -version = "0.10.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e31331286705f455e56cca62e0e717158474ff02b7936c1fa596d983f4ae27" +checksum = "a17e662a7a8291a865152364c20c7abc5e60486ab2001e8ec10b24862de0b9ab" dependencies = [ - "crossbeam-utils 0.8.12", + "crossbeam-utils 0.8.15", "libc", - "mach", + "mach2", "once_cell", "raw-cpuid", - "wasi 0.10.2+wasi-snapshot-preview1", + "wasi 0.11.0+wasi-snapshot-preview1", "web-sys", "winapi", ] @@ -5653,7 +5637,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.9", ] [[package]] @@ -5676,11 +5660,11 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "10.6.0" +version = "10.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6823ea29436221176fe662da99998ad3b4db2c7f31e7b6f5fe43adccd6320bb" +checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" dependencies = [ - "bitflags 1.3.2", + "bitflags 1.2.1", ] [[package]] @@ -5697,13 +5681,13 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.9.3" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ - "crossbeam-channel 0.5.6", + "crossbeam-channel 0.5.8", "crossbeam-deque", - "crossbeam-utils 0.8.12", + "crossbeam-utils 0.8.15", "num_cpus", ] @@ -5718,7 +5702,7 @@ dependencies = [ "digest 0.9.0", "jubjub", "rand_core 0.6.4", - "serde 1.0.147", + "serde 1.0.163", "thiserror", "zeroize", ] @@ -5729,7 +5713,16 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags 1.3.2", + "bitflags 1.2.1", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.2.1", ] [[package]] @@ -5738,8 +5731,8 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.8", - "redox_syscall", + "getrandom 0.2.9", + "redox_syscall 0.2.16", "thiserror", ] @@ -5771,14 +5764,14 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax 0.6.28", + "regex-syntax 0.6.29", ] [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" @@ -5792,7 +5785,7 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" dependencies = [ - "bitflags 1.3.2", + "bitflags 1.2.1", "libc", "mach", "winapi", @@ -5800,18 +5793,18 @@ dependencies = [ [[package]] name = "rend" -version = "0.3.6" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79af64b4b6362ffba04eef3a4e10829718a4896dac19daa741851c86781edf95" +checksum = "581008d2099240d37fb08d77ad713bcaec2c4d89d50b5b21a8bb1996bbab68ab" dependencies = [ "bytecheck", ] [[package]] name = "reqwest" -version = "0.11.14" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ "base64 0.21.0", "bytes", @@ -5822,7 +5815,6 @@ dependencies = [ "http", "http-body", "hyper", - "hyper-rustls 0.23.2", "hyper-tls", "ipnet", "js-sys", @@ -5832,20 +5824,16 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.20.7", - "rustls-pemfile 1.0.2", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "serde_urlencoded", "tokio", "tokio-native-tls", - "tokio-rustls 0.23.4", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 0.22.5", "winreg", ] @@ -5861,11 +5849,21 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" dependencies = [ - "crypto-bigint", + "crypto-bigint 0.4.9", "hmac 0.12.1", "zeroize", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac 0.12.1", + "subtle 2.4.1", +] + [[package]] name = "ring" version = "0.16.20" @@ -5875,7 +5873,7 @@ dependencies = [ "cc", "libc", "once_cell", - "spin", + "spin 0.5.2", "untrusted", "web-sys", "winapi", @@ -5887,7 +5885,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" dependencies = [ - "digest 0.10.7", + "digest 0.10.6", ] [[package]] @@ -5903,23 +5901,26 @@ dependencies = [ [[package]] name = "rkyv" -version = "0.7.39" +version = "0.7.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cec2b3485b07d96ddfd3134767b8a447b45ea4eb91448d0a35180ec0ffd5ed15" +checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58" dependencies = [ + "bitvec 1.0.1", "bytecheck", "hashbrown 0.12.3", "ptr_meta", "rend", "rkyv_derive", "seahash", + "tinyvec", + "uuid 1.3.2", ] [[package]] name = "rkyv_derive" -version = "0.7.39" +version = "0.7.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eaedadc88b53e36dd32d940ed21ae4d850d5916f2581526921f553a72ac34c4" +checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d" dependencies = [ "proc-macro2", "quote", @@ -5942,6 +5943,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" dependencies = [ "bytes", + "rlp-derive", "rustc-hex", ] @@ -5984,9 +5986,9 @@ checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" [[package]] name = "rustc-demangle" -version = "0.1.21" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc-hash" @@ -6020,26 +6022,26 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.5" +version = "0.37.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588" +checksum = "f79bef90eb6d984c72722595b5b1348ab39275a5e5123faca6863bf07d75a4e0" dependencies = [ - "bitflags 1.3.2", - "errno 0.2.8", + "bitflags 1.2.1", + "errno", "io-lifetimes", "libc", - "linux-raw-sys 0.1.4", - "windows-sys 0.42.0", + "linux-raw-sys 0.3.7", + "windows-sys 0.48.0", ] [[package]] name = "rustix" -version = "0.38.3" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" dependencies = [ "bitflags 2.3.3", - "errno 0.3.1", + "errno", "libc", "linux-raw-sys 0.4.3", "windows-sys 0.48.0", @@ -6060,9 +6062,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.20.7" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" dependencies = [ "log", "ring", @@ -6084,39 +6086,30 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", - "rustls-pemfile 1.0.2", + "rustls-pemfile", "schannel", "security-framework", ] [[package]] name = "rustls-pemfile" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" -dependencies = [ - "base64 0.13.1", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ "base64 0.21.0", ] [[package]] name = "rustversion" -version = "1.0.9" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" [[package]] name = "rusty-fork" @@ -6132,9 +6125,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "safe-proc-macro2" @@ -6183,12 +6176,6 @@ dependencies = [ "safe-regex-compiler", ] -[[package]] -name = "safemem" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" - [[package]] name = "salsa20" version = "0.10.2" @@ -6209,9 +6196,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.3.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "001cf62ece89779fd16105b5f515ad0e5cedcd5440d3dd806bb067978e7c3608" +checksum = "35c0a159d0c45c12b20c5a844feb1fe4bea86e28f17b92a5f0c42193634d3782" dependencies = [ "cfg-if 1.0.0", "derive_more", @@ -6221,11 +6208,11 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.3.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "303959cf613a6f6efd19ed4b4ad5bf79966a13352716299ad532cfb115f4205c" +checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29" dependencies = [ - "proc-macro-crate 1.2.1", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 1.0.109", @@ -6233,21 +6220,20 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" dependencies = [ - "lazy_static", - "windows-sys 0.36.1", + "windows-sys 0.42.0", ] [[package]] name = "scheduled-thread-pool" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "977a7519bff143a44f842fd07e80ad1329295bd71686457f18e496736f4bf9bf" +checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" dependencies = [ - "parking_lot 0.12.1", + "parking_lot", ] [[package]] @@ -6262,12 +6248,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "scratch" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" - [[package]] name = "scrypt" version = "0.10.0" @@ -6312,9 +6292,22 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ - "base16ct", - "der", - "generic-array 0.14.6", + "base16ct 0.1.1", + "der 0.6.1", + "generic-array 0.14.7", + "subtle 2.4.1", + "zeroize", +] + +[[package]] +name = "sec1" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e" +dependencies = [ + "base16ct 0.2.0", + "der 0.7.7", + "generic-array 0.14.7", "pkcs8", "subtle 2.4.1", "zeroize", @@ -6329,7 +6322,7 @@ dependencies = [ "bitcoin_hashes", "rand 0.8.5", "secp256k1-sys", - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -6347,7 +6340,7 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23a2ac85147a3a11d77ecf1bc7166ec0b92febfa4461c37944e180f319ece467" dependencies = [ - "bitflags 1.3.2", + "bitflags 1.2.1", "core-foundation", "core-foundation-sys", "libc", @@ -6356,9 +6349,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" dependencies = [ "core-foundation-sys", "libc", @@ -6379,7 +6372,7 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -6391,6 +6384,12 @@ dependencies = [ "pest", ] +[[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" @@ -6405,23 +6404,13 @@ checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" [[package]] name = "serde" -version = "1.0.147" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" dependencies = [ "serde_derive", ] -[[package]] -name = "serde-aux" -version = "4.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c599b3fd89a75e0c18d6d2be693ddb12cccaf771db4ff9e39097104808a014c0" -dependencies = [ - "serde 1.0.147", - "serde_json", -] - [[package]] name = "serde-hjson" version = "0.9.1" @@ -6436,11 +6425,11 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.7" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfc50e8183eeeb6178dcb167ae34a8051d63535023ae38b5d8d12beae193d37b" +checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -6450,40 +6439,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" dependencies = [ "half", - "serde 1.0.147", + "serde 1.0.163", ] [[package]] name = "serde_derive" -version = "1.0.147" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" +checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] name = "serde_json" -version = "1.0.87" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", - "serde 1.0.147", + "serde 1.0.163", ] [[package]] name = "serde_repr" -version = "0.1.9" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca" +checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", +] + +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde 1.0.163", ] [[package]] @@ -6495,7 +6493,7 @@ dependencies = [ "form_urlencoded", "itoa", "ryu", - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -6506,7 +6504,7 @@ checksum = "ef8099d3df28273c99a1728190c7a9f19d444c941044f64adf986bee7ec53051" dependencies = [ "dtoa", "linked-hash-map", - "serde 1.0.147", + "serde 1.0.163", "yaml-rust", ] @@ -6523,17 +6521,6 @@ dependencies = [ "opaque-debug 0.3.0", ] -[[package]] -name = "sha-1" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" -dependencies = [ - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.10.7", -] - [[package]] name = "sha1" version = "0.10.5" @@ -6542,7 +6529,7 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.7", + "digest 0.10.6", ] [[package]] @@ -6578,16 +6565,16 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.7", + "digest 0.10.6", ] [[package]] name = "sha3" -version = "0.10.6" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest 0.10.7", + "digest 0.10.6", "keccak", ] @@ -6608,9 +6595,9 @@ checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] name = "signal-hook" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" +checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" dependencies = [ "libc", "signal-hook-registry", @@ -6618,9 +6605,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] @@ -6631,10 +6618,26 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ - "digest 0.10.7", + "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 = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + [[package]] name = "simple-error" version = "0.2.3" @@ -6658,9 +6661,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] @@ -6682,9 +6685,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -6708,14 +6711,20 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "spki" -version = "0.6.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" dependencies = [ "base64ct", - "der", + "der 0.7.7", ] [[package]] @@ -6811,9 +6820,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.16" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", @@ -6826,23 +6835,11 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-xid", -] - [[package]] name = "sysinfo" -version = "0.21.1" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb6c2c4a6ca462f07ca89841a2618dca6e405304d19ae238997e64915d89f513" +checksum = "751e810399bba86e9326f5762b7f32ac5a085542df78da6a78d94e07d14d7c11" dependencies = [ "cfg-if 1.0.0", "core-foundation-sys", @@ -6877,21 +6874,21 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.5" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d" +checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5" [[package]] name = "tempfile" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if 1.0.0", "fastrand", - "redox_syscall", - "rustix 0.36.5", - "windows-sys 0.42.0", + "redox_syscall 0.3.5", + "rustix 0.37.13", + "windows-sys 0.45.0", ] [[package]] @@ -6905,18 +6902,18 @@ dependencies = [ "ed25519-dalek", "flex-error", "futures", - "k256", + "k256 0.11.6", "num-traits 0.2.15", "once_cell", "prost", "prost-types", "ripemd160", - "serde 1.0.147", + "serde 1.0.163", "serde_bytes", "serde_json", "serde_repr", "sha2 0.9.9", - "signature", + "signature 1.6.4", "subtle 2.4.1", "subtle-encoding", "tendermint-proto", @@ -6930,10 +6927,10 @@ version = "0.23.6" source = "git+https://github.com/heliaxdev/tendermint-rs.git?rev=b7d1e5afc6f2ccb3fd1545c2174bab1cc48d7fa7#b7d1e5afc6f2ccb3fd1545c2174bab1cc48d7fa7" dependencies = [ "flex-error", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "tendermint", - "toml", + "toml 0.5.9", "url", ] @@ -6947,7 +6944,7 @@ dependencies = [ "derive_more", "flex-error", "futures", - "serde 1.0.147", + "serde 1.0.163", "serde_cbor", "serde_derive", "static_assertions", @@ -6965,7 +6962,7 @@ source = "git+https://github.com/heliaxdev/tendermint-rs.git?rev=b7d1e5afc6f2ccb dependencies = [ "derive_more", "flex-error", - "serde 1.0.147", + "serde 1.0.163", "tendermint", "time", ] @@ -6981,7 +6978,7 @@ dependencies = [ "num-traits 0.2.15", "prost", "prost-types", - "serde 1.0.147", + "serde 1.0.163", "serde_bytes", "subtle-encoding", "time", @@ -6997,14 +6994,14 @@ dependencies = [ "bytes", "flex-error", "futures", - "getrandom 0.2.8", + "getrandom 0.2.9", "http", "hyper", "hyper-proxy", - "hyper-rustls 0.22.1", + "hyper-rustls", "peg", "pin-project", - "serde 1.0.147", + "serde 1.0.163", "serde_bytes", "serde_json", "subtle-encoding", @@ -7027,7 +7024,7 @@ source = "git+https://github.com/heliaxdev/tendermint-rs.git?rev=b7d1e5afc6f2ccb dependencies = [ "ed25519-dalek", "gumdrop", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "simple-error", "tempfile", @@ -7035,20 +7032,11 @@ dependencies = [ "time", ] -[[package]] -name = "termcolor" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" -dependencies = [ - "winapi-util", -] - [[package]] name = "termtree" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "test-log" @@ -7063,41 +7051,41 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if 1.0.0", "once_cell", ] [[package]] name = "tikv-jemalloc-sys" -version = "0.5.2+5.3.0-patched" +version = "0.5.3+5.3.0-patched" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec45c14da997d0925c7835883e4d5c181f196fa142f8c19d7643d1e9af2592c3" +checksum = "a678df20055b43e57ef8cddde41cdfda9a3c1a060b67f4c5836dfb1d78543ba8" dependencies = [ "cc", - "fs_extra", "libc", ] @@ -7108,7 +7096,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" dependencies = [ "itoa", - "serde 1.0.147", + "serde 1.0.163", "time-core", "time-macros", ] @@ -7189,15 +7177,14 @@ dependencies = [ [[package]] name = "tiny_http" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0d6ef4e10d23c1efb862eecad25c5054429a71958b4eeef85eb5e7170b477ca" +checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82" dependencies = [ "ascii", "chunked_transfer", + "httpdate", "log", - "time", - "url", ] [[package]] @@ -7211,28 +7198,27 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.21.2" +version = "1.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" dependencies = [ "autocfg", "bytes", "libc", - "memchr", "mio", "num_cpus", - "parking_lot 0.12.1", + "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -7247,20 +7233,20 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.8.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] name = "tokio-native-tls" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", @@ -7283,16 +7269,16 @@ version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ - "rustls 0.20.7", + "rustls 0.20.8", "tokio", "webpki 0.22.0", ] [[package]] name = "tokio-stream" -version = "0.1.11" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite", @@ -7314,14 +7300,14 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.17.2" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" +checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd" dependencies = [ "futures-util", "log", "tokio", - "tungstenite 0.17.3", + "tungstenite 0.18.0", ] [[package]] @@ -7340,9 +7326,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -7358,7 +7344,41 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", +] + +[[package]] +name = "toml" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +dependencies = [ + "serde 1.0.163", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde 1.0.163", +] + +[[package]] +name = "toml_edit" +version = "0.19.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78" +dependencies = [ + "indexmap 2.0.0", + "serde 1.0.163", + "serde_spanned", + "toml_datetime", + "winnow", ] [[package]] @@ -7383,12 +7403,12 @@ dependencies = [ "pin-project", "prost", "prost-derive", - "rustls-native-certs 0.6.2", - "rustls-pemfile 1.0.2", + "rustls-native-certs 0.6.3", + "rustls-pemfile", "tokio", "tokio-rustls 0.23.4", "tokio-stream", - "tokio-util 0.7.4", + "tokio-util 0.7.8", "tower", "tower-layer", "tower-service", @@ -7402,7 +7422,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bf5e9b9c0f7e0a7c027dcfaba7b2c60816c7049171f679d99ee2ff65d0de8c4" dependencies = [ - "prettyplease 0.1.24", + "prettyplease 0.1.25", "proc-macro2", "prost-build", "quote", @@ -7418,13 +7438,13 @@ dependencies = [ "futures-core", "futures-util", "hdrhistogram", - "indexmap", + "indexmap 1.9.3", "pin-project", "pin-project-lite", "rand 0.8.5", "slab", "tokio", - "tokio-util 0.7.4", + "tokio-util 0.7.8", "tower-layer", "tower-service", "tracing 0.1.37", @@ -7448,25 +7468,6 @@ dependencies = [ "tracing-tower", ] -[[package]] -name = "tower-http" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" -dependencies = [ - "bitflags 1.3.2", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-range-header", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", -] - [[package]] name = "tower-layer" version = "0.3.2" @@ -7508,8 +7509,8 @@ dependencies = [ "cfg-if 1.0.0", "log", "pin-project-lite", - "tracing-attributes 0.1.23", - "tracing-core 0.1.30", + "tracing-attributes 0.1.24", + "tracing-core 0.1.31", ] [[package]] @@ -7518,9 +7519,9 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09d48f71a791638519505cefafe162606f706c25592e4bde4d97600c0195312e" dependencies = [ - "crossbeam-channel 0.5.6", + "crossbeam-channel 0.5.8", "time", - "tracing-subscriber 0.3.16", + "tracing-subscriber 0.3.17", ] [[package]] @@ -7535,13 +7536,13 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -7554,9 +7555,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", "valuable", @@ -7599,7 +7600,7 @@ checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" dependencies = [ "lazy_static", "log", - "tracing-core 0.1.30", + "tracing-core 0.1.31", ] [[package]] @@ -7608,8 +7609,8 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" dependencies = [ - "serde 1.0.147", - "tracing-core 0.1.30", + "serde 1.0.163", + "tracing-core 0.1.31", ] [[package]] @@ -7620,25 +7621,25 @@ checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" dependencies = [ "sharded-slab", "thread_local", - "tracing-core 0.1.30", + "tracing-core 0.1.31", ] [[package]] name = "tracing-subscriber" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ "matchers", "nu-ansi-term", "once_cell", "regex", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "sharded-slab", "thread_local", "tracing 0.1.37", - "tracing-core 0.1.30", + "tracing-core 0.1.31", "tracing-log", "tracing-serde", ] @@ -7659,15 +7660,15 @@ dependencies = [ [[package]] name = "triomphe" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1ee9bd9239c339d714d657fac840c6d2a4f9c45f4f9ec7b0975113458be78db" +checksum = "0eee8098afad3fb0c54a9007aab6804558410503ad676d4633f9c2559a00ac0f" [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "tungstenite" @@ -7683,16 +7684,16 @@ dependencies = [ "input_buffer", "log", "rand 0.8.5", - "sha-1 0.9.8", + "sha-1", "url", "utf-8", ] [[package]] name = "tungstenite" -version = "0.17.3" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" +checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788" dependencies = [ "base64 0.13.1", "byteorder", @@ -7701,26 +7702,17 @@ dependencies = [ "httparse", "log", "rand 0.8.5", - "sha-1 0.10.0", + "sha1", "thiserror", "url", "utf-8", ] -[[package]] -name = "twoway" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" -dependencies = [ - "memchr", -] - [[package]] name = "typenum" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "ucd-trie" @@ -7757,15 +7749,15 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-normalization" @@ -7776,12 +7768,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-segmentation" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" - [[package]] name = "unicode-width" version = "0.1.10" @@ -7845,17 +7831,17 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "getrandom 0.2.8", - "serde 1.0.147", + "getrandom 0.2.9", + "serde 1.0.163", ] [[package]] name = "uuid" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83" +checksum = "4dad5567ad0cf5b760e5665964bec1b47dfd077ba8a2544b513f3556d3d239a2" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.9", ] [[package]] @@ -7937,12 +7923,11 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi", "winapi-util", ] @@ -7958,9 +7943,9 @@ dependencies = [ [[package]] name = "warp" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed7b8be92646fc3d18b06147664ebc5f48d222686cb11a8755e561a735aacc6d" +checksum = "ba431ef570df1287f7f8b07e376491ad54f84d26ac473489427231e1718e1f69" dependencies = [ "bytes", "futures-channel", @@ -7971,18 +7956,18 @@ dependencies = [ "log", "mime", "mime_guess", - "multipart", + "multer", "percent-encoding", "pin-project", - "rustls-pemfile 0.2.1", + "rustls-pemfile", "scoped-tls", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "serde_urlencoded", "tokio", "tokio-stream", "tokio-tungstenite", - "tokio-util 0.7.4", + "tokio-util 0.7.8", "tower-service", "tracing 0.1.37", ] @@ -7993,12 +7978,6 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -8007,9 +7986,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "5b6cb788c4e39112fbe1822277ef6fb3c55cd86b95cb3d3c4c1c9597e4ac74b4" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -8017,24 +7996,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "35e522ed4105a9d626d885b35d62501b30d9666283a5c8be12c14a8bdafe7822" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.33" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +checksum = "083abe15c5d88556b77bdf7aef403625be9e327ad37c62c4e4129af740168163" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -8044,9 +8023,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "358a79a0cb89d21db8120cbfb91392335913e4890665b1a7981d9e956903b434" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -8054,28 +8033,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "4783ce29f09b9d93134d41297aded3a712b7b979e9c6f28c32cb88c973a94869" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "a901d592cafaa4d711bc324edfaff879ac700b19c3dfd60058d2b445be2691eb" [[package]] name = "wasm-encoder" -version = "0.19.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9424cdab516a16d4ea03c8f4a01b14e7b2d04a129dcc2bcdde5bcc5f68f06c41" +checksum = "d05d0b6fcd0aeb98adf16e7975331b3c17222aa815148f5b976370ce589d80ef" dependencies = [ "leb128", ] @@ -8089,21 +8068,6 @@ dependencies = [ "parity-wasm", ] -[[package]] -name = "wasm-timer" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" -dependencies = [ - "futures", - "js-sys", - "parking_lot 0.11.2", - "pin-utils", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - [[package]] name = "wasmer" version = "2.3.0" @@ -8111,7 +8075,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea8d8361c9d006ea3d7797de7bd6b1492ffd0f91a22430cfda6c1658ad57bedf" dependencies = [ "cfg-if 1.0.0", - "indexmap", + "indexmap 1.9.3", "js-sys", "loupe", "more-asserts", @@ -8165,7 +8129,7 @@ dependencies = [ "enumset", "loupe", "rkyv", - "serde 1.0.147", + "serde 1.0.163", "serde_bytes", "smallvec", "target-lexicon", @@ -8183,7 +8147,7 @@ dependencies = [ "cranelift-codegen", "cranelift-entity", "cranelift-frontend", - "gimli", + "gimli 0.26.2", "loupe", "more-asserts", "rayon", @@ -8203,7 +8167,7 @@ dependencies = [ "byteorder", "dynasm", "dynasmrt", - "gimli", + "gimli 0.26.2", "lazy_static", "loupe", "more-asserts", @@ -8238,7 +8202,7 @@ dependencies = [ "memmap2", "more-asserts", "rustc-demangle", - "serde 1.0.147", + "serde 1.0.163", "serde_bytes", "target-lexicon", "thiserror", @@ -8262,7 +8226,7 @@ dependencies = [ "loupe", "object 0.28.4", "rkyv", - "serde 1.0.147", + "serde 1.0.163", "tempfile", "tracing 0.1.37", "wasmer-artifact", @@ -8330,11 +8294,11 @@ checksum = "39df01ea05dc0a9bab67e054c7cb01521e53b35a7bb90bd02eca564ed0b2667f" dependencies = [ "backtrace", "enum-iterator", - "indexmap", + "indexmap 1.9.3", "loupe", "more-asserts", "rkyv", - "serde 1.0.147", + "serde 1.0.163", "thiserror", ] @@ -8349,7 +8313,7 @@ dependencies = [ "cfg-if 1.0.0", "corosensei", "enum-iterator", - "indexmap", + "indexmap 1.9.3", "lazy_static", "libc", "loupe", @@ -8359,7 +8323,7 @@ dependencies = [ "region", "rkyv", "scopeguard", - "serde 1.0.147", + "serde 1.0.163", "thiserror", "wasmer-artifact", "wasmer-types", @@ -8378,7 +8342,7 @@ version = "0.107.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29e3ac9b780c7dda0cac7a52a5d6d2d6707cc6e3451c9db209b6c758f40d7acb" dependencies = [ - "indexmap", + "indexmap 1.9.3", "semver 1.0.17", ] @@ -8390,7 +8354,7 @@ checksum = "5f656cd8858a5164932d8a90f936700860976ec21eb00e0fe2aa8cab13f6b4cf" dependencies = [ "futures", "js-sys", - "parking_lot 0.12.1", + "parking_lot", "pin-utils", "slab", "wasm-bindgen", @@ -8398,9 +8362,9 @@ dependencies = [ [[package]] name = "wast" -version = "49.0.0" +version = "57.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ef81fcd60d244cafffeafac3d17615fdb2fddda6aca18f34a8ae233353587c" +checksum = "6eb0f5ed17ac4421193c7477da05892c2edafd67f9639e3c11a82086416662dc" dependencies = [ "leb128", "memchr", @@ -8410,18 +8374,18 @@ dependencies = [ [[package]] name = "wat" -version = "1.0.51" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c347c4460ffb311e95aafccd8c29e4888f241b9e4b3bb0e0ccbd998de2c8c0d" +checksum = "ab9ab0d87337c3be2bb6fc5cd331c4ba9fd6bcb4ee85048a0dd59ed9ecf92e53" dependencies = [ "wast", ] [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "16b5f940c7edfdc6d12126d98c9ef4d1b3d470011c47c76a6581df47ad9ba721" dependencies = [ "js-sys", "wasm-bindgen", @@ -8458,18 +8422,18 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.22.5" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" dependencies = [ "webpki 0.22.0", ] [[package]] name = "which" -version = "4.3.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" dependencies = [ "either", "libc", @@ -8516,6 +8480,15 @@ dependencies = [ "windows-targets 0.42.2", ] +[[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.33.0" @@ -8529,19 +8502,6 @@ dependencies = [ "windows_x86_64_msvc 0.33.0", ] -[[package]] -name = "windows-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", -] - [[package]] name = "windows-sys" version = "0.42.0" @@ -8557,13 +8517,22 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] +[[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.2", +] + [[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.1", + "windows-targets 0.48.0", ] [[package]] @@ -8583,9 +8552,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ "windows_aarch64_gnullvm 0.48.0", "windows_aarch64_msvc 0.48.0", @@ -8614,12 +8583,6 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807" -[[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -8638,12 +8601,6 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e" -[[package]] -name = "windows_i686_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" - [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -8662,12 +8619,6 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0" -[[package]] -name = "windows_i686_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" - [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -8686,12 +8637,6 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784" -[[package]] -name = "windows_x86_64_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -8722,12 +8667,6 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa" -[[package]] -name = "windows_x86_64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -8740,6 +8679,15 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "winnow" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.10.1" @@ -8761,7 +8709,7 @@ dependencies = [ "log", "pharos", "rustc_version 0.4.0", - "send_wrapper", + "send_wrapper 0.6.0", "thiserror", "wasm-bindgen", "wasm-bindgen-futures", @@ -8806,31 +8754,31 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", - "synstructure", + "syn 2.0.15", ] [[package]] name = "zstd-sys" -version = "2.0.1+zstd.1.5.2" +version = "2.0.8+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fd07cbbc53846d9145dbffdf6dd09a7a0aa52be46741825f5c97bdd4f73f12b" +checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" dependencies = [ "cc", "libc", + "pkg-config", ] diff --git a/Cargo.toml b/Cargo.toml index a7bec0a2c4..16442008f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ keywords = ["blockchain", "privacy", "crypto", "protocol", "network"] license = "GPL-3.0" readme = "README.md" repository = "https://github.com/anoma/namada" -version = "0.19.0" +version = "0.20.0" [workspace.dependencies] ark-bls12-381 = {version = "0.3"} @@ -68,9 +68,9 @@ ethabi = "18.0.0" ethers = "2.0.0" expectrl = "0.7.0" eyre = "0.6.5" +fd-lock = "3.0.12" ferveo = {git = "https://github.com/anoma/ferveo", rev = "e5abd0acc938da90140351a65a26472eb495ce4d"} ferveo-common = {git = "https://github.com/anoma/ferveo", rev = "e5abd0acc938da90140351a65a26472eb495ce4d"} -file-lock = "2.0.2" file-serve = "0.2.0" flate2 = "1.0.22" fs_extra = "1.2.0" @@ -81,6 +81,7 @@ ibc-relayer-types = {git = "https://github.com/heliaxdev/hermes.git", rev = "fea ics23 = "0.9.0" index-set = {git = "https://github.com/heliaxdev/index-set", tag = "v0.7.1", features = ["serialize-borsh", "serialize-serde"]} itertools = "0.10.0" +lazy_static = "1.4.0" libc = "0.2.97" libloading = "0.7.2" libsecp256k1 = {git = "https://github.com/heliaxdev/libsecp256k1", rev = "bbb3bd44a49db361f21d9db80f9a087c194c0ae9", default-features = false, features = ["std", "static-context"]} @@ -116,7 +117,7 @@ sha2 = "0.9.3" signal-hook = "0.3.9" slip10_ed25519 = "0.1.3" # sysinfo with disabled multithread feature -sysinfo = {version = "=0.21.1", default-features = false} +sysinfo = {version = "0.29.4", default-features = false} tar = "0.4.37" tempfile = {version = "3.2.0"} tendermint-config = {git = "https://github.com/heliaxdev/tendermint-rs.git", rev = "b7d1e5afc6f2ccb3fd1545c2174bab1cc48d7fa7"} diff --git a/Makefile b/Makefile index a0d9f4a600..a347ff8e27 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ build: $(cargo) build $(jobs) build-test: - $(cargo) build --tests $(jobs) + $(cargo) +$(nightly) build --tests $(jobs) build-release: NAMADA_DEV=false $(cargo) build $(jobs) --release --package namada_apps --manifest-path Cargo.toml @@ -119,7 +119,7 @@ test-unit-coverage: $(cargo) +$(nightly) llvm-cov --output-dir target \ --features namada/testing \ --html \ - -- --skip e2e -Z unstable-options --report-time + -- --skip e2e --skip integration -Z unstable-options --report-time # NOTE: `TEST_FILTER` is prepended with `e2e::`. Since filters in `cargo test` # work with a substring search, TEST_FILTER only works if it contains a string @@ -135,11 +135,18 @@ test-e2e: --test-threads=1 \ -Z unstable-options --report-time +test-integration: + RUST_BACKTRACE=$(RUST_BACKTRACE) \ + $(cargo) +$(nightly) test integration::$(TEST_FILTER) \ + -Z unstable-options \ + -- \ + -Z unstable-options --report-time + test-unit: $(cargo) +$(nightly) test \ $(TEST_FILTER) \ $(jobs) \ - -- --skip e2e \ + -- --skip e2e --skip integration \ -Z unstable-options --report-time test-unit-mainnet: @@ -147,14 +154,14 @@ test-unit-mainnet: --features "mainnet" \ $(TEST_FILTER) \ $(jobs) \ - -- --skip e2e \ + -- --skip e2e --skip integration \ -Z unstable-options --report-time test-unit-debug: $(debug-cargo) +$(nightly) test \ $(jobs) \ $(TEST_FILTER) \ - -- --skip e2e \ + -- --skip e2e --skip integration \ --nocapture \ -Z unstable-options --report-time diff --git a/apps/Cargo.toml b/apps/Cargo.toml index b82a4966b7..992abbef33 100644 --- a/apps/Cargo.toml +++ b/apps/Cargo.toml @@ -64,6 +64,7 @@ abciplus = [ "namada/tendermint-rpc", ] + [dependencies] namada = {path = "../shared", features = ["ferveo-tpke", "masp-tx-gen", "multicore", "http-client"]} ark-serialize.workspace = true @@ -90,12 +91,13 @@ ethbridge-bridge-events = {git = "https://github.com/heliaxdev/ethbridge-rs", ta ethbridge-events = {git = "https://github.com/heliaxdev/ethbridge-rs", tag = "v0.18.0"} ethbridge-governance-events = {git = "https://github.com/heliaxdev/ethbridge-rs", tag = "v0.18.0"} eyre.workspace = true +fd-lock.workspace = true ferveo-common.workspace = true ferveo.workspace = true -file-lock.workspace = true flate2.workspace = true futures.workspace = true itertools.workspace = true +lazy_static.workspace= true libc.workspace = true libloading.workspace = true masp_primitives = { workspace = true, features = ["transparent-inputs"] } @@ -125,6 +127,7 @@ sha2.workspace = true signal-hook.workspace = true sysinfo.workspace = true tar.workspace = true +tempfile.workspace = true tendermint-config.workspace = true thiserror.workspace = true tokio = {workspace = true, features = ["full"]} @@ -148,7 +151,6 @@ namada = {path = "../shared", default-features = false, features = ["testing", " namada_test_utils = {path = "../test_utils"} bit-set.workspace = true proptest.workspace = true -tempfile.workspace = true test-log.workspace = true tokio-test.workspace = true diff --git a/apps/src/bin/namada-client/cli.rs b/apps/src/bin/namada-client/cli.rs index 5154e3c443..2c421ae22d 100644 --- a/apps/src/bin/namada-client/cli.rs +++ b/apps/src/bin/namada-client/cli.rs @@ -262,6 +262,20 @@ pub async fn main() -> Result<()> { .await?; } } + Sub::TxUnjailValidator(TxUnjailValidator(mut args)) => { + let client = HttpClient::new(utils::take_config_address( + &mut args.tx.ledger_address, + )) + .unwrap(); + wait_until_node_is_synched(&client) + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_unjail_validator::( + &client, ctx, args, + ) + .await?; + } // Ledger queries Sub::QueryEpoch(QueryEpoch(mut args)) => { let client = HttpClient::new(utils::take_config_address( @@ -353,6 +367,22 @@ pub async fn main() -> Result<()> { let args = args.to_sdk(&mut ctx); rpc::query_bonded_stake(&client, args).await; } + Sub::QueryValidatorState(QueryValidatorState(mut args)) => { + let client = HttpClient::new(utils::take_config_address( + &mut args.query.ledger_address, + )) + .unwrap(); + wait_until_node_is_synched(&client) + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_and_print_validator_state( + &client, + &mut ctx.wallet, + args, + ) + .await; + } Sub::QueryCommissionRate(QueryCommissionRate(mut args)) => { let client = HttpClient::new(utils::take_config_address( &mut args.query.ledger_address, @@ -493,6 +523,15 @@ pub async fn main() -> Result<()> { Utils::DefaultBaseDir(DefaultBaseDir(args)) => { utils::default_base_dir(global_args, args) } + Utils::EpochSleep(EpochSleep(args)) => { + let mut ctx = cli::Context::new(global_args) + .expect("expected to construct a context"); + let ledger_address = args.ledger_address.clone(); + wait_until_node_is_synched(&ledger_address).await; + let client = HttpClient::new(ledger_address).unwrap(); + let args = args.to_sdk(&mut ctx); + rpc::epoch_sleep(&client, args).await; + } }, } Ok(()) diff --git a/apps/src/bin/namada-client/main.rs b/apps/src/bin/namada-client/main.rs index ccdc0bb2eb..a9e1fb4948 100644 --- a/apps/src/bin/namada-client/main.rs +++ b/apps/src/bin/namada-client/main.rs @@ -1,7 +1,7 @@ -mod cli; - use color_eyre::eyre::Result; -use namada_apps::logging; +use namada_apps::cli::api::CliApi; +use namada_apps::facade::tendermint_rpc::HttpClient; +use namada_apps::{cli, logging}; use tracing_subscriber::filter::LevelFilter; #[tokio::main] @@ -13,5 +13,9 @@ async fn main() -> Result<()> { let _log_guard = logging::init_from_env_or(LevelFilter::INFO)?; // run the CLI - cli::main().await + CliApi::<()>::handle_client_command::( + None, + cli::namada_client_cli()?, + ) + .await } diff --git a/apps/src/bin/namada-relayer/cli.rs b/apps/src/bin/namada-relayer/cli.rs deleted file mode 100644 index fa816dbeae..0000000000 --- a/apps/src/bin/namada-relayer/cli.rs +++ /dev/null @@ -1,143 +0,0 @@ -//! Namada relayer CLI. - -use std::sync::Arc; - -use color_eyre::eyre::{eyre, Report, Result}; -use namada::eth_bridge::ethers::providers::{Http, Provider}; -use namada::ledger::eth_bridge::{bridge_pool, validator_set}; -use namada::ledger::rpc::wait_until_node_is_synched; -use namada::types::control_flow::ProceedOrElse; -use namada_apps::cli::args::CliToSdkCtxless; -use namada_apps::cli::{self, cmds}; -use namada_apps::client::utils; -use namada_apps::facade::tendermint_rpc::HttpClient; - -fn error() -> Report { - eyre!("Fatal error") -} - -pub async fn main() -> Result<()> { - let (cmd, _) = cli::namada_relayer_cli()?; - match cmd { - cmds::NamadaRelayer::EthBridgePool(sub) => match sub { - cmds::EthBridgePool::RecommendBatch(mut args) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk_ctxless(); - bridge_pool::recommend_batch(&client, args) - .await - .proceed_or_else(error)?; - } - cmds::EthBridgePool::ConstructProof(mut args) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk_ctxless(); - bridge_pool::construct_proof(&client, args) - .await - .proceed_or_else(error)?; - } - cmds::EthBridgePool::RelayProof(mut args) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let eth_client = Arc::new( - Provider::::try_from(&args.eth_rpc_endpoint).unwrap(), - ); - let args = args.to_sdk_ctxless(); - bridge_pool::relay_bridge_pool_proof(eth_client, &client, args) - .await - .proceed_or_else(error)?; - } - cmds::EthBridgePool::QueryPool(mut query) => { - let client = HttpClient::new(utils::take_config_address( - &mut query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - bridge_pool::query_bridge_pool(&client).await; - } - cmds::EthBridgePool::QuerySigned(mut query) => { - let client = HttpClient::new(utils::take_config_address( - &mut query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - bridge_pool::query_signed_bridge_pool(&client) - .await - .proceed_or_else(error)?; - } - cmds::EthBridgePool::QueryRelays(mut query) => { - let client = HttpClient::new(utils::take_config_address( - &mut query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - bridge_pool::query_relay_progress(&client).await; - } - }, - cmds::NamadaRelayer::ValidatorSet(sub) => match sub { - cmds::ValidatorSet::ConsensusValidatorSet(mut args) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk_ctxless(); - validator_set::query_validator_set_args(&client, args).await; - } - cmds::ValidatorSet::ValidatorSetProof(mut args) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk_ctxless(); - validator_set::query_validator_set_update_proof(&client, args) - .await; - } - cmds::ValidatorSet::ValidatorSetUpdateRelay(mut args) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let eth_client = Arc::new( - Provider::::try_from(&args.eth_rpc_endpoint).unwrap(), - ); - let args = args.to_sdk_ctxless(); - validator_set::relay_validator_set_update( - eth_client, &client, args, - ) - .await - .proceed_or_else(error)?; - } - }, - } - Ok(()) -} diff --git a/apps/src/bin/namada-relayer/main.rs b/apps/src/bin/namada-relayer/main.rs index 73876fe7d2..0b314cb9fa 100644 --- a/apps/src/bin/namada-relayer/main.rs +++ b/apps/src/bin/namada-relayer/main.rs @@ -1,7 +1,7 @@ -mod cli; - use color_eyre::eyre::Result; -use namada_apps::logging; +use namada::tendermint_rpc::HttpClient; +use namada_apps::cli::api::CliApi; +use namada_apps::{cli, logging}; use tracing_subscriber::filter::LevelFilter; #[tokio::main] @@ -12,6 +12,7 @@ async fn main() -> Result<()> { // init logging logging::init_from_env_or(LevelFilter::INFO)?; + let (cmd, _) = cli::namada_relayer_cli()?; // run the CLI - cli::main().await + CliApi::<()>::handle_relayer_command::(None, cmd).await } diff --git a/apps/src/bin/namada-wallet/main.rs b/apps/src/bin/namada-wallet/main.rs index 252ecb7b88..7459234c79 100644 --- a/apps/src/bin/namada-wallet/main.rs +++ b/apps/src/bin/namada-wallet/main.rs @@ -1,9 +1,10 @@ -mod cli; use color_eyre::eyre::Result; +use namada_apps::cli; +use namada_apps::cli::api::CliApi; pub fn main() -> Result<()> { color_eyre::install()?; - + let (cmd, ctx) = cli::namada_wallet_cli()?; // run the CLI - cli::main() + CliApi::<()>::handle_wallet_command(cmd, ctx) } diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 688494179e..ae90aac6d8 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -6,13 +6,17 @@ //! client can be dispatched via `namada node ...` or `namada client ...`, //! respectively. +pub mod api; +pub mod client; pub mod context; +pub mod relayer; mod utils; +pub mod wallet; use clap::{ArgGroup, ArgMatches, ColorChoice}; use color_eyre::eyre::Result; -pub use utils::safe_exit; use utils::*; +pub use utils::{dispatch_prompt, safe_exit, Cmd, TESTIN}; pub use self::context::Context; @@ -27,6 +31,7 @@ const WALLET_CMD: &str = "wallet"; const RELAYER_CMD: &str = "relayer"; pub mod cmds { + use super::utils::*; use super::{ args, ArgMatches, CLIENT_CMD, NODE_CMD, RELAYER_CMD, WALLET_CMD, @@ -217,6 +222,7 @@ pub mod cmds { .subcommand(TxVoteProposal::def().display_order(1)) // PoS transactions .subcommand(TxInitValidator::def().display_order(2)) + .subcommand(TxUnjailValidator::def().display_order(2)) .subcommand(Bond::def().display_order(2)) .subcommand(Unbond::def().display_order(2)) .subcommand(Withdraw::def().display_order(2)) @@ -240,6 +246,7 @@ pub mod cmds { .subcommand(QueryProposal::def().display_order(4)) .subcommand(QueryProposalResult::def().display_order(4)) .subcommand(QueryProtocolParameters::def().display_order(4)) + .subcommand(QueryValidatorState::def().display_order(4)) // Utils .subcommand(Utils::def().display_order(5)) } @@ -254,6 +261,8 @@ pub mod cmds { let tx_init_account = Self::parse_with_ctx(matches, TxInitAccount); let tx_init_validator = Self::parse_with_ctx(matches, TxInitValidator); + let tx_unjail_validator = + Self::parse_with_ctx(matches, TxUnjailValidator); let tx_reveal_pk = Self::parse_with_ctx(matches, TxRevealPk); let tx_init_proposal = Self::parse_with_ctx(matches, TxInitProposal); @@ -286,6 +295,8 @@ pub mod cmds { Self::parse_with_ctx(matches, QueryProposalResult); let query_protocol_parameters = Self::parse_with_ctx(matches, QueryProtocolParameters); + let query_validator_state = + Self::parse_with_ctx(matches, QueryValidatorState); let add_to_eth_bridge_pool = Self::parse_with_ctx(matches, AddToEthBridgePool); let utils = SubCmd::parse(matches).map(Self::WithoutContext); @@ -299,6 +310,7 @@ pub mod cmds { .or(tx_vote_proposal) .or(tx_init_validator) .or(tx_commission_rate_change) + .or(tx_unjail_validator) .or(bond) .or(unbond) .or(withdraw) @@ -318,6 +330,7 @@ pub mod cmds { .or(query_proposal) .or(query_proposal_result) .or(query_protocol_parameters) + .or(query_validator_state) .or(query_account) .or(utils) } @@ -364,6 +377,7 @@ pub mod cmds { TxInitAccount(TxInitAccount), TxInitValidator(TxInitValidator), TxCommissionRateChange(TxCommissionRateChange), + TxUnjailValidator(TxUnjailValidator), TxInitProposal(TxInitProposal), TxVoteProposal(TxVoteProposal), TxRevealPk(TxRevealPk), @@ -387,6 +401,7 @@ pub mod cmds { QueryProposal(QueryProposal), QueryProposalResult(QueryProposalResult), QueryProtocolParameters(QueryProtocolParameters), + QueryValidatorState(QueryValidatorState), } #[allow(clippy::large_enum_variant)] @@ -1288,6 +1303,27 @@ pub mod cmds { } } + #[derive(Clone, Debug)] + pub struct TxUnjailValidator(pub args::TxUnjailValidator); + + impl SubCmd for TxUnjailValidator { + const CMD: &'static str = "unjail-validator"; + + fn parse(matches: &ArgMatches) -> Option { + matches.subcommand_matches(Self::CMD).map(|matches| { + TxUnjailValidator(args::TxUnjailValidator::parse(matches)) + }) + } + + fn def() -> App { + App::new(Self::CMD) + .about( + "Send a signed transaction to unjail a jailed validator.", + ) + .add_args::>() + } + } + #[derive(Clone, Debug)] pub struct Bond(pub args::Bond); @@ -1481,6 +1517,27 @@ pub mod cmds { } } + #[derive(Clone, Debug)] + pub struct QueryValidatorState( + pub args::QueryValidatorState, + ); + + impl SubCmd for QueryValidatorState { + const CMD: &'static str = "validator-state"; + + fn parse(matches: &ArgMatches) -> Option { + matches.subcommand_matches(Self::CMD).map(|matches| { + QueryValidatorState(args::QueryValidatorState::parse(matches)) + }) + } + + fn def() -> App { + App::new(Self::CMD) + .about("Query the state of a PoS validator.") + .add_args::>() + } + } + #[derive(Clone, Debug)] pub struct QueryTransfers(pub args::QueryTransfers); @@ -1516,7 +1573,7 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) - .about("Query commission rate.") + .about("Query a validator's commission rate.") .add_args::>() } } @@ -1706,6 +1763,28 @@ pub mod cmds { } } + #[derive(Clone, Debug)] + pub struct EpochSleep(pub args::Query); + + impl SubCmd for EpochSleep { + const CMD: &'static str = "epoch-sleep"; + + fn parse(matches: &ArgMatches) -> Option { + matches + .subcommand_matches(Self::CMD) + .map(|matches| Self(args::Query::parse(matches))) + } + + fn def() -> App { + App::new(Self::CMD) + .about( + "Query for the current epoch, then sleep until the next \ + epoch.", + ) + .add_args::>() + } + } + #[derive(Clone, Debug)] pub enum Utils { JoinNetwork(JoinNetwork), @@ -1714,6 +1793,7 @@ pub mod cmds { InitGenesisValidator(InitGenesisValidator), PkToTmAddress(PkToTmAddress), DefaultBaseDir(DefaultBaseDir), + EpochSleep(EpochSleep), } impl SubCmd for Utils { @@ -1732,12 +1812,14 @@ pub mod cmds { SubCmd::parse(matches).map(Self::PkToTmAddress); let default_base_dir = SubCmd::parse(matches).map(Self::DefaultBaseDir); + let epoch_sleep = SubCmd::parse(matches).map(Self::EpochSleep); join_network .or(fetch_wasms) .or(init_network) .or(init_genesis) .or(pk_to_tm_address) .or(default_base_dir) + .or(epoch_sleep) }) } @@ -1750,6 +1832,7 @@ pub mod cmds { .subcommand(InitGenesisValidator::def()) .subcommand(PkToTmAddress::def()) .subcommand(DefaultBaseDir::def()) + .subcommand(EpochSleep::def()) .subcommand_required(true) .arg_required_else_help(true) } @@ -2415,7 +2498,6 @@ pub mod args { pub const SOURCE: Arg = arg("source"); pub const SOURCE_OPT: ArgOpt = SOURCE.opt(); pub const STORAGE_KEY: Arg = arg("storage-key"); - pub const SUB_PREFIX: ArgOpt = arg_opt("sub-prefix"); pub const SUSPEND_ACTION: ArgFlag = flag("suspend"); pub const TIMEOUT_HEIGHT: ArgOpt = arg_opt("timeout-height"); pub const TIMEOUT_SEC_OFFSET: ArgOpt = arg_opt("timeout-sec-offset"); @@ -3113,7 +3195,6 @@ pub mod args { source: ctx.get_cached(&self.source), target: ctx.get(&self.target), token: ctx.get(&self.token), - sub_prefix: self.sub_prefix, amount: self.amount, native_token: ctx.native_token.clone(), tx_code_path: self.tx_code_path.to_path_buf(), @@ -3127,7 +3208,6 @@ pub mod args { let source = TRANSFER_SOURCE.parse(matches); let target = TRANSFER_TARGET.parse(matches); let token = TOKEN.parse(matches); - let sub_prefix = SUB_PREFIX.parse(matches); let amount = InputAmount::Unvalidated(AMOUNT.parse(matches)); let tx_code_path = PathBuf::from(TX_TRANSFER_WASM); Self { @@ -3135,7 +3215,6 @@ pub mod args { source, target, token, - sub_prefix, amount, native_token: (), tx_code_path, @@ -3153,7 +3232,6 @@ pub mod args { to produce the signature.", )) .arg(TOKEN.def().help("The transfer token.")) - .arg(SUB_PREFIX.def().help("The token's sub prefix.")) .arg(AMOUNT.def().help("The amount to transfer in decimal.")) } } @@ -3165,7 +3243,6 @@ pub mod args { source: ctx.get(&self.source), receiver: self.receiver, token: ctx.get(&self.token), - sub_prefix: self.sub_prefix, amount: self.amount, port_id: self.port_id, channel_id: self.channel_id, @@ -3182,7 +3259,6 @@ pub mod args { let source = SOURCE.parse(matches); let receiver = RECEIVER.parse(matches); let token = TOKEN.parse(matches); - let sub_prefix = SUB_PREFIX.parse(matches); let amount = AMOUNT.parse(matches); let port_id = PORT_ID.parse(matches); let channel_id = CHANNEL_ID.parse(matches); @@ -3194,7 +3270,6 @@ pub mod args { source, receiver, token, - sub_prefix, amount: amount.amount, port_id, channel_id, @@ -3214,7 +3289,6 @@ pub mod args { "The receiver address on the destination chain as string.", )) .arg(TOKEN.def().help("The transfer token.")) - .arg(SUB_PREFIX.def().help("The token's sub prefix.")) .arg(AMOUNT.def().help("The amount to transfer in decimal.")) .arg(PORT_ID.def().help("The port ID.")) .arg(CHANNEL_ID.def().help("The channel ID.")) @@ -3970,7 +4044,6 @@ pub mod args { owner: self.owner.map(|x| ctx.get_cached(&x)), token: self.token.map(|x| ctx.get(&x)), no_conversions: self.no_conversions, - sub_prefix: self.sub_prefix, } } } @@ -3981,13 +4054,11 @@ pub mod args { let owner = BALANCE_OWNER.parse(matches); let token = TOKEN_OPT.parse(matches); let no_conversions = NO_CONVERSIONS.parse(matches); - let sub_prefix = SUB_PREFIX.parse(matches); Self { query, owner, token, no_conversions, - sub_prefix, } } @@ -4008,11 +4079,6 @@ pub mod args { "Whether not to automatically perform conversions.", ), ) - .arg( - SUB_PREFIX - .def() - .help("The token's sub prefix whose balance to query."), - ) } } @@ -4022,7 +4088,6 @@ pub mod args { query: self.query.to_sdk(ctx), owner: self.owner.map(|x| ctx.get_cached(&x)), token: self.token.map(|x| ctx.get(&x)), - sub_prefix: self.sub_prefix, } } } @@ -4032,12 +4097,10 @@ pub mod args { let query = Query::parse(matches); let owner = BALANCE_OWNER.parse(matches); let token = TOKEN_OPT.parse(matches); - let sub_prefix = SUB_PREFIX.parse(matches); Self { query, owner, token, - sub_prefix, } } @@ -4049,11 +4112,6 @@ pub mod args { .arg(TOKEN_OPT.def().help( "The token address that queried transfers must involve.", )) - .arg( - SUB_PREFIX - .def() - .help("The token's sub prefix whose balance to query."), - ) } } @@ -4122,8 +4180,44 @@ pub mod args { "The validator's address whose bonded stake to query.", )) .arg(EPOCH.def().help( - "The epoch at which to query (last committed, if not \ - specified).", + "The epoch at which to query (corresponding to the last \ + committed block, if not specified).", + )) + } + } + + impl CliToSdk> for QueryValidatorState { + fn to_sdk(self, ctx: &mut Context) -> QueryValidatorState { + QueryValidatorState:: { + query: self.query.to_sdk(ctx), + validator: ctx.get(&self.validator), + epoch: self.epoch, + } + } + } + + impl Args for QueryValidatorState { + fn parse(matches: &ArgMatches) -> Self { + let query = Query::parse(matches); + let validator = VALIDATOR.parse(matches); + let epoch = EPOCH.parse(matches); + Self { + query, + validator, + epoch, + } + } + + fn def(app: App) -> App { + app.add_args::>() + .arg( + VALIDATOR.def().help( + "The validator's address whose state is queried.", + ), + ) + .arg(EPOCH.def().help( + "The epoch at which to query (corresponding to the last \ + committed block, if not specified).", )) } } @@ -4170,16 +4264,10 @@ pub mod args { impl CliToSdk> for TxUnjailValidator { fn to_sdk(self, ctx: &mut Context) -> TxUnjailValidator { - TxUnjailValidator { + TxUnjailValidator:: { tx: self.tx.to_sdk(ctx), validator: ctx.get(&self.validator), - tx_code_path: self - .tx_code_path - .as_path() - .to_str() - .unwrap() - .to_string() - .into_bytes(), + tx_code_path: self.tx_code_path.to_path_buf(), } } } @@ -4233,8 +4321,8 @@ pub mod args { "The validator's address whose commission rate to query.", )) .arg(EPOCH.def().help( - "The epoch at which to query (last committed, if not \ - specified).", + "The epoch at which to query (corresponding to the last \ + committed block, if not specified).", )) } } diff --git a/apps/src/lib/cli/api.rs b/apps/src/lib/cli/api.rs new file mode 100644 index 0000000000..c22fe39fd3 --- /dev/null +++ b/apps/src/lib/cli/api.rs @@ -0,0 +1,29 @@ +use std::marker::PhantomData; + +use namada::ledger::queries::Client; +use namada::ledger::rpc::wait_until_node_is_synched; +use namada::tendermint_rpc::HttpClient; +use namada::types::control_flow::Halt; +use tendermint_config::net::Address as TendermintAddress; + +use crate::client::utils; + +/// Trait for clients that can be used with the CLI. +#[async_trait::async_trait(?Send)] +pub trait CliClient: Client + Sync { + fn from_tendermint_address(address: &mut TendermintAddress) -> Self; + async fn wait_until_node_is_synced(&self) -> Halt<()>; +} + +#[async_trait::async_trait(?Send)] +impl CliClient for HttpClient { + fn from_tendermint_address(address: &mut TendermintAddress) -> Self { + HttpClient::new(utils::take_config_address(address)).unwrap() + } + + async fn wait_until_node_is_synced(&self) -> Halt<()> { + wait_until_node_is_synched(self).await + } +} + +pub struct CliApi(PhantomData); diff --git a/apps/src/lib/cli/client.rs b/apps/src/lib/cli/client.rs new file mode 100644 index 0000000000..4c01a03ec7 --- /dev/null +++ b/apps/src/lib/cli/client.rs @@ -0,0 +1,599 @@ +use color_eyre::eyre::{eyre, Report, Result}; +use namada::ledger::eth_bridge::bridge_pool; +use namada::ledger::tx::dump_tx; +use namada::ledger::{signing, tx as sdk_tx}; +use namada::types::control_flow::ProceedOrElse; + +use crate::cli; +use crate::cli::api::{CliApi, CliClient}; +use crate::cli::args::CliToSdk; +use crate::cli::cmds::*; +use crate::client::{rpc, tx, utils}; + +fn error() -> Report { + eyre!("Fatal error") +} + +impl CliApi { + pub async fn handle_client_command( + client: Option, + cmd: cli::NamadaClient, + ) -> Result<()> + where + C: CliClient, + { + match cmd { + cli::NamadaClient::WithContext(cmd_box) => { + let (cmd, mut ctx) = *cmd_box; + use NamadaClientWithContext as Sub; + match cmd { + // Ledger cmds + Sub::TxCustom(TxCustom(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + let dry_run = args.tx.dry_run; + tx::submit_custom(&client, &mut ctx, args).await?; + if !dry_run { + crate::wallet::save(&ctx.wallet) + .unwrap_or_else(|err| eprintln!("{}", err)); + } else { + println!( + "Transaction dry run. No addresses have been \ + saved." + ) + } + } + Sub::TxTransfer(TxTransfer(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_transfer(&client, ctx, args).await?; + } + Sub::TxIbcTransfer(TxIbcTransfer(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_ibc_transfer(&client, ctx, args).await?; + } + Sub::TxUpdateAccount(TxUpdateAccount(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_update_account(&client, &mut ctx, args) + .await?; + } + Sub::TxInitAccount(TxInitAccount(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + let dry_run = args.tx.dry_run; + tx::submit_init_account(&client, &mut ctx, args) + .await?; + if !dry_run { + crate::wallet::save(&ctx.wallet) + .unwrap_or_else(|err| eprintln!("{}", err)); + } else { + println!( + "Transaction dry run. No addresses have been \ + saved." + ) + } + } + Sub::TxInitValidator(TxInitValidator(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_init_validator(&client, ctx, args).await?; + } + Sub::TxInitProposal(TxInitProposal(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_init_proposal(&client, ctx, args).await?; + } + Sub::TxVoteProposal(TxVoteProposal(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_vote_proposal(&client, ctx, args).await?; + } + Sub::TxRevealPk(TxRevealPk(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_reveal_pk(&client, &mut ctx, args).await?; + } + Sub::Bond(Bond(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_bond(&client, &mut ctx, args).await?; + } + Sub::Unbond(Unbond(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_unbond(&client, &mut ctx, args).await?; + } + Sub::Withdraw(Withdraw(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_withdraw(&client, ctx, args).await?; + } + Sub::TxCommissionRateChange(TxCommissionRateChange( + mut args, + )) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_validator_commission_change( + &client, ctx, args, + ) + .await?; + } + // Eth bridge + Sub::AddToEthBridgePool(args) => { + let mut args = args.0; + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + let tx_args = args.tx.clone(); + + let default_signer = signing::signer_from_address( + Some(args.sender.clone()), + ); + let signing_data = signing::aux_signing_data( + &client, + &mut ctx.wallet, + &args.tx, + &args.sender, + default_signer, + ) + .await?; + + let tx_builder = bridge_pool::build_bridge_pool_tx( + &client, + args.clone(), + signing_data.fee_payer.clone(), + ) + .await?; + + if args.tx.dump_tx { + dump_tx(&args.tx, tx_builder); + } else { + tx::submit_reveal_aux( + &client, + &mut ctx, + tx_args.clone(), + &args.sender, + ) + .await?; + + let tx_builder = signing::sign_tx( + &mut ctx.wallet, + &tx_args, + tx_builder, + signing_data, + )?; + + sdk_tx::process_tx( + &client, + &mut ctx.wallet, + &tx_args, + tx_builder, + ) + .await?; + } + } + Sub::TxUnjailValidator(TxUnjailValidator(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_unjail_validator(&client, ctx, args).await?; + } + // Ledger queries + Sub::QueryEpoch(QueryEpoch(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address(&mut args.ledger_address) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + rpc::query_and_print_epoch(&client).await; + } + Sub::QueryValidatorState(QueryValidatorState(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_and_print_validator_state( + &client, + &mut ctx.wallet, + args, + ) + .await; + } + Sub::QueryTransfers(QueryTransfers(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_transfers( + &client, + &mut ctx.wallet, + &mut ctx.shielded, + args, + ) + .await; + } + Sub::QueryConversions(QueryConversions(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_conversions(&client, &mut ctx.wallet, args) + .await; + } + Sub::QueryBlock(QueryBlock(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address(&mut args.ledger_address) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + rpc::query_block(&client).await; + } + Sub::QueryBalance(QueryBalance(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_balance( + &client, + &mut ctx.wallet, + &mut ctx.shielded, + args, + ) + .await; + } + Sub::QueryBonds(QueryBonds(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_bonds(&client, &mut ctx.wallet, args) + .await + .expect("expected successful query of bonds"); + } + Sub::QueryBondedStake(QueryBondedStake(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_bonded_stake(&client, args).await; + } + Sub::QueryCommissionRate(QueryCommissionRate(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_and_print_commission_rate( + &client, + &mut ctx.wallet, + args, + ) + .await; + } + Sub::QuerySlashes(QuerySlashes(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_slashes(&client, &mut ctx.wallet, args) + .await; + } + Sub::QueryDelegations(QueryDelegations(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_delegations(&client, &mut ctx.wallet, args) + .await; + } + Sub::QueryFindValidator(QueryFindValidator(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_find_validator(&client, args).await; + } + Sub::QueryResult(QueryResult(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_result(&client, args).await; + } + Sub::QueryRawBytes(QueryRawBytes(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_raw_bytes(&client, args).await; + } + + Sub::QueryProposal(QueryProposal(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_proposal(&client, args).await; + } + Sub::QueryProposalResult(QueryProposalResult(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_proposal_result(&client, args).await; + } + Sub::QueryProtocolParameters(QueryProtocolParameters( + mut args, + )) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_protocol_parameters(&client, args).await; + } + Sub::QueryAccount(QueryAccount(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_account(&client, args).await; + } + } + } + cli::NamadaClient::WithoutContext(cmd, global_args) => match cmd { + // Utils cmds + Utils::JoinNetwork(JoinNetwork(args)) => { + utils::join_network(global_args, args).await + } + Utils::FetchWasms(FetchWasms(args)) => { + utils::fetch_wasms(global_args, args).await + } + Utils::InitNetwork(InitNetwork(args)) => { + utils::init_network(global_args, args) + } + Utils::InitGenesisValidator(InitGenesisValidator(args)) => { + utils::init_genesis_validator(global_args, args) + } + Utils::PkToTmAddress(PkToTmAddress(args)) => { + utils::pk_to_tm_address(global_args, args) + } + Utils::DefaultBaseDir(DefaultBaseDir(args)) => { + utils::default_base_dir(global_args, args) + } + Utils::EpochSleep(EpochSleep(args)) => { + let mut ctx = cli::Context::new(global_args) + .expect("expected to construct a context"); + let mut ledger_address = args.ledger_address.clone(); + let client = + C::from_tendermint_address(&mut ledger_address); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::epoch_sleep(&client, args).await; + } + }, + } + Ok(()) + } +} diff --git a/apps/src/lib/cli/relayer.rs b/apps/src/lib/cli/relayer.rs new file mode 100644 index 0000000000..531051d27a --- /dev/null +++ b/apps/src/lib/cli/relayer.rs @@ -0,0 +1,166 @@ +use std::sync::Arc; + +use color_eyre::eyre::{eyre, Report, Result}; +use namada::eth_bridge::ethers::providers::{Http, Provider}; +use namada::ledger::eth_bridge::{bridge_pool, validator_set}; +use namada::types::control_flow::ProceedOrElse; + +use crate::cli::api::{CliApi, CliClient}; +use crate::cli::args::CliToSdkCtxless; +use crate::cli::cmds; + +fn error() -> Report { + eyre!("Fatal error") +} + +impl CliApi { + pub async fn handle_relayer_command( + client: Option, + cmd: cmds::NamadaRelayer, + ) -> Result<()> + where + C: CliClient, + { + match cmd { + cmds::NamadaRelayer::EthBridgePool(sub) => match sub { + cmds::EthBridgePool::RecommendBatch(mut args) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk_ctxless(); + bridge_pool::recommend_batch(&client, args) + .await + .proceed_or_else(error)?; + } + cmds::EthBridgePool::ConstructProof(mut args) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk_ctxless(); + bridge_pool::construct_proof(&client, args) + .await + .proceed_or_else(error)?; + } + cmds::EthBridgePool::RelayProof(mut args) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let eth_client = Arc::new( + Provider::::try_from(&args.eth_rpc_endpoint) + .unwrap(), + ); + let args = args.to_sdk_ctxless(); + bridge_pool::relay_bridge_pool_proof( + eth_client, &client, args, + ) + .await + .proceed_or_else(error)?; + } + cmds::EthBridgePool::QueryPool(mut query) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address(&mut query.ledger_address) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + bridge_pool::query_bridge_pool(&client).await; + } + cmds::EthBridgePool::QuerySigned(mut query) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address(&mut query.ledger_address) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + bridge_pool::query_signed_bridge_pool(&client) + .await + .proceed_or_else(error)?; + } + cmds::EthBridgePool::QueryRelays(mut query) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address(&mut query.ledger_address) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + bridge_pool::query_relay_progress(&client).await; + } + }, + cmds::NamadaRelayer::ValidatorSet(sub) => match sub { + cmds::ValidatorSet::ConsensusValidatorSet(mut args) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk_ctxless(); + validator_set::query_validator_set_args(&client, args) + .await; + } + cmds::ValidatorSet::ValidatorSetProof(mut args) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk_ctxless(); + validator_set::query_validator_set_update_proof( + &client, args, + ) + .await; + } + cmds::ValidatorSet::ValidatorSetUpdateRelay(mut args) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let eth_client = Arc::new( + Provider::::try_from(&args.eth_rpc_endpoint) + .unwrap(), + ); + let args = args.to_sdk_ctxless(); + validator_set::relay_validator_set_update( + eth_client, &client, args, + ) + .await + .proceed_or_else(error)?; + } + }, + } + Ok(()) + } +} diff --git a/apps/src/lib/cli/utils.rs b/apps/src/lib/cli/utils.rs index a50f58f5de..aed45507d9 100644 --- a/apps/src/lib/cli/utils.rs +++ b/apps/src/lib/cli/utils.rs @@ -1,10 +1,12 @@ //! Command line interface utilities use std::fmt::Debug; +use std::io::Write; use std::marker::PhantomData; use std::str::FromStr; use clap::{ArgAction, ArgMatches}; use color_eyre::eyre::Result; +use lazy_static::lazy_static; use super::args; use super::context::{Context, FromContext}; @@ -343,12 +345,70 @@ where }) } +#[cfg(not(feature = "testing"))] /// A helper to exit after flushing output, borrowed from `clap::util` module. pub fn safe_exit(code: i32) -> ! { - use std::io::Write; - let _ = std::io::stdout().lock().flush(); let _ = std::io::stderr().lock().flush(); std::process::exit(code) } + +#[cfg(feature = "testing")] +/// A helper to exit after flushing output, borrowed from `clap::util` module. +pub fn safe_exit(_: i32) -> ! { + let _ = std::io::stdout().lock().flush(); + let _ = std::io::stderr().lock().flush(); + + panic!("Test failed because the client exited unexpectedly.") +} + +lazy_static! { + /// A replacement for stdin in testing. + pub static ref TESTIN: std::sync::Arc>> = + std::sync::Arc::new(std::sync::Mutex::new(vec![])); +} + +/// A generic function for displaying a prompt to users and reading +/// in their response. +fn prompt_aux(mut reader: R, mut writer: W, question: &str) -> String +where + R: std::io::Read, + W: Write, +{ + write!(&mut writer, "{}", question).expect("Unable to write"); + writer.flush().unwrap(); + let mut s = String::new(); + reader.read_to_string(&mut s).expect("Unable to read"); + s +} + +/// A function that chooses how to dispatch prompts +/// to users. There is a hierarchy of feature flags +/// that determines this. If no flags are set, +/// the question is printed to stdout and response +/// read from stdin. +pub fn dispatch_prompt(question: impl AsRef) -> String { + if cfg!(feature = "testing") { + prompt_aux( + TESTIN.lock().unwrap().as_slice(), + std::io::stdout(), + question.as_ref(), + ) + } else { + prompt_aux( + std::io::stdin().lock(), + std::io::stdout(), + question.as_ref(), + ) + } +} + +#[macro_export] +/// A convenience macro for formatting the user prompt before +/// forwarding it to the `[dispatch_prompt]` method. +macro_rules! prompt { + ($($arg:tt)*) => {{ + $crate::cli::dispatch_prompt(format!("{}", format_args!($($arg)*))) + }} +} diff --git a/apps/src/bin/namada-wallet/cli.rs b/apps/src/lib/cli/wallet.rs similarity index 83% rename from apps/src/bin/namada-wallet/cli.rs rename to apps/src/lib/cli/wallet.rs index 685ed7f116..7505c59efe 100644 --- a/apps/src/bin/namada-wallet/cli.rs +++ b/apps/src/lib/cli/wallet.rs @@ -11,68 +11,78 @@ use namada::ledger::masp::find_valid_diversifier; use namada::ledger::wallet::{DecryptionError, FindKeyError}; use namada::types::key::*; use namada::types::masp::{MaspValue, PaymentAddress}; -use namada_apps::cli; -use namada_apps::cli::args::CliToSdk; -use namada_apps::cli::{args, cmds, Context}; -use namada_apps::wallet::{ - read_and_confirm_encryption_password, CliWalletUtils, -}; use rand_core::OsRng; -pub fn main() -> Result<()> { - let (cmd, mut ctx) = cli::namada_wallet_cli()?; - match cmd { - cmds::NamadaWallet::Key(sub) => match sub { - cmds::WalletKey::Restore(cmds::KeyRestore(args)) => { - key_and_address_restore(ctx, args) - } - cmds::WalletKey::Gen(cmds::KeyGen(args)) => { - key_and_address_gen(ctx, args) - } - cmds::WalletKey::Find(cmds::KeyFind(args)) => key_find(ctx, args), - cmds::WalletKey::List(cmds::KeyList(args)) => key_list(ctx, args), - cmds::WalletKey::Export(cmds::Export(args)) => { - key_export(ctx, args) - } - }, - cmds::NamadaWallet::Address(sub) => match sub { - cmds::WalletAddress::Gen(cmds::AddressGen(args)) => { - key_and_address_gen(ctx, args) - } - cmds::WalletAddress::Restore(cmds::AddressRestore(args)) => { - key_and_address_restore(ctx, args) - } - cmds::WalletAddress::Find(cmds::AddressOrAliasFind(args)) => { - address_or_alias_find(ctx, args) - } - cmds::WalletAddress::List(cmds::AddressList) => address_list(ctx), - cmds::WalletAddress::Add(cmds::AddressAdd(args)) => { - address_add(ctx, args) - } - }, - cmds::NamadaWallet::Masp(sub) => match sub { - cmds::WalletMasp::GenSpendKey(cmds::MaspGenSpendKey(args)) => { - spending_key_gen(ctx, args) - } - cmds::WalletMasp::GenPayAddr(cmds::MaspGenPayAddr(args)) => { - let args = args.to_sdk(&mut ctx); - payment_address_gen(ctx, args) - } - cmds::WalletMasp::AddAddrKey(cmds::MaspAddAddrKey(args)) => { - address_key_add(ctx, args) - } - cmds::WalletMasp::ListPayAddrs(cmds::MaspListPayAddrs) => { - payment_addresses_list(ctx) - } - cmds::WalletMasp::ListKeys(cmds::MaspListKeys(args)) => { - spending_keys_list(ctx, args) - } - cmds::WalletMasp::FindAddrKey(cmds::MaspFindAddrKey(args)) => { - address_key_find(ctx, args) - } - }, +use crate::cli; +use crate::cli::api::CliApi; +use crate::cli::args::CliToSdk; +use crate::cli::{args, cmds, Context}; +use crate::wallet::{read_and_confirm_encryption_password, CliWalletUtils}; + +impl CliApi { + pub fn handle_wallet_command( + cmd: cmds::NamadaWallet, + mut ctx: Context, + ) -> Result<()> { + match cmd { + cmds::NamadaWallet::Key(sub) => match sub { + cmds::WalletKey::Restore(cmds::KeyRestore(args)) => { + key_and_address_restore(ctx, args) + } + cmds::WalletKey::Gen(cmds::KeyGen(args)) => { + key_and_address_gen(ctx, args) + } + cmds::WalletKey::Find(cmds::KeyFind(args)) => { + key_find(ctx, args) + } + cmds::WalletKey::List(cmds::KeyList(args)) => { + key_list(ctx, args) + } + cmds::WalletKey::Export(cmds::Export(args)) => { + key_export(ctx, args) + } + }, + cmds::NamadaWallet::Address(sub) => match sub { + cmds::WalletAddress::Gen(cmds::AddressGen(args)) => { + key_and_address_gen(ctx, args) + } + cmds::WalletAddress::Restore(cmds::AddressRestore(args)) => { + key_and_address_restore(ctx, args) + } + cmds::WalletAddress::Find(cmds::AddressOrAliasFind(args)) => { + address_or_alias_find(ctx, args) + } + cmds::WalletAddress::List(cmds::AddressList) => { + address_list(ctx) + } + cmds::WalletAddress::Add(cmds::AddressAdd(args)) => { + address_add(ctx, args) + } + }, + cmds::NamadaWallet::Masp(sub) => match sub { + cmds::WalletMasp::GenSpendKey(cmds::MaspGenSpendKey(args)) => { + spending_key_gen(ctx, args) + } + cmds::WalletMasp::GenPayAddr(cmds::MaspGenPayAddr(args)) => { + let args = args.to_sdk(&mut ctx); + payment_address_gen(ctx, args) + } + cmds::WalletMasp::AddAddrKey(cmds::MaspAddAddrKey(args)) => { + address_key_add(ctx, args) + } + cmds::WalletMasp::ListPayAddrs(cmds::MaspListPayAddrs) => { + payment_addresses_list(ctx) + } + cmds::WalletMasp::ListKeys(cmds::MaspListKeys(args)) => { + spending_keys_list(ctx, args) + } + cmds::WalletMasp::FindAddrKey(cmds::MaspFindAddrKey(args)) => { + address_key_find(ctx, args) + } + }, + } + Ok(()) } - Ok(()) } /// Find shielded address or key @@ -213,8 +223,7 @@ fn spending_key_gen( let alias = alias.to_lowercase(); let password = read_and_confirm_encryption_password(unsafe_dont_encrypt); let (alias, _key) = wallet.gen_spending_key(alias, password, alias_force); - namada_apps::wallet::save(&wallet) - .unwrap_or_else(|err| eprintln!("{}", err)); + crate::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully added a spending key with alias: \"{}\"", alias @@ -248,8 +257,7 @@ fn payment_address_gen( eprintln!("Payment address not added"); cli::safe_exit(1); }); - namada_apps::wallet::save(&wallet) - .unwrap_or_else(|err| eprintln!("{}", err)); + crate::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully generated a payment address with the following alias: {}", alias, @@ -306,8 +314,7 @@ fn address_key_add( (alias, "payment address") } }; - namada_apps::wallet::save(&ctx.wallet) - .unwrap_or_else(|err| eprintln!("{}", err)); + crate::wallet::save(&ctx.wallet).unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully added a {} with the following alias to wallet: {}", typ, alias, @@ -345,8 +352,7 @@ fn key_and_address_restore( println!("No changes are persisted. Exiting."); cli::safe_exit(0); }); - namada_apps::wallet::save(&wallet) - .unwrap_or_else(|err| eprintln!("{}", err)); + crate::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully added a key and an address with alias: \"{}\"", alias @@ -387,8 +393,7 @@ fn key_and_address_gen( println!("No changes are persisted. Exiting."); cli::safe_exit(0); }); - namada_apps::wallet::save(&wallet) - .unwrap_or_else(|err| eprintln!("{}", err)); + crate::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully added a key and an address with alias: \"{}\"", alias @@ -573,8 +578,7 @@ fn address_add(ctx: Context, args: args::AddressAdd) { eprintln!("Address not added"); cli::safe_exit(1); } - namada_apps::wallet::save(&wallet) - .unwrap_or_else(|err| eprintln!("{}", err)); + crate::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully added a key and an address with alias: \"{}\"", args.alias.to_lowercase() diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 94d2a66063..b759067287 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -1,7 +1,7 @@ //! Client RPC queries use std::cmp::Ordering; -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::fs::File; use std::io::{self, Write}; use std::iter::Iterator; @@ -16,7 +16,6 @@ use masp_primitives::merkle_tree::MerklePath; use masp_primitives::sapling::{Node, ViewingKey}; use masp_primitives::zip32::ExtendedFullViewingKey; use namada::core::types::transaction::governance::ProposalType; -use namada::ledger::args::InputAmount; use namada::ledger::events::Event; use namada::ledger::governance::parameters::GovParams; use namada::ledger::governance::storage as gov_storage; @@ -36,7 +35,7 @@ use namada::ledger::rpc::{ }; use namada::ledger::storage::ConversionState; use namada::ledger::wallet::{AddressVpType, Wallet}; -use namada::proof_of_stake::types::WeightedValidator; +use namada::proof_of_stake::types::{ValidatorState, WeightedValidator}; use namada::types::address::{masp, Address}; use namada::types::control_flow::ProceedOrElse; use namada::types::governance::{ @@ -46,13 +45,14 @@ use namada::types::hash::Hash; use namada::types::key::*; use namada::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; use namada::types::storage::{BlockHeight, BlockResults, Epoch, Key, KeySeg}; -use namada::types::token::{Change, Denomination, MaspDenom, TokenAddress}; +use namada::types::token::{Change, MaspDenom}; use namada::types::{storage, token}; use tokio::time::Instant; use crate::cli::{self, args}; use crate::facade::tendermint::merkle::proof::Proof; use crate::facade::tendermint_rpc::error::Error as TError; +use crate::prompt; use crate::wallet::CliWalletUtils; /// Query the status of a given transaction. @@ -111,7 +111,7 @@ pub async fn query_results( /// Query the specified accepted transfers from the ledger pub async fn query_transfers< C: namada::ledger::queries::Client + Sync, - U: ShieldedUtils, + U: ShieldedUtils, >( client: &C, wallet: &mut Wallet, @@ -119,7 +119,6 @@ pub async fn query_transfers< args: args::QueryTransfers, ) { let query_token = args.token; - let sub_prefix = args.sub_prefix.map(|s| Key::parse(s).unwrap()); let query_owner = args.owner.map_or_else( || Either::Right(wallet.get_addresses().into_values().collect()), Either::Left, @@ -173,10 +172,8 @@ pub async fn query_transfers< // Check if this transfer pertains to the supplied token relevant &= match &query_token { Some(token) => { - let check = |(tok, chg): (&TokenAddress, &Change)| { - tok.sub_prefix == sub_prefix - && &tok.address == token - && !chg.is_zero() + let check = |(tok, chg): (&Address, &Change)| { + tok == token && !chg.is_zero() }; tfer_delta.values().cloned().any( |MaspChange { ref asset, change }| check((asset, &change)), @@ -196,7 +193,7 @@ pub async fn query_transfers< for (account, MaspChange { ref asset, change }) in tfer_delta { if account != masp() { print!(" {}:", account); - let token_alias = lookup_alias(wallet, &asset.address); + let token_alias = lookup_alias(wallet, asset); let sign = match change.cmp(&Change::zero()) { Ordering::Greater => "+", Ordering::Less => "-", @@ -207,7 +204,7 @@ pub async fn query_transfers< sign, format_denominated_amount(client, asset, change.into(),) .await, - asset.format_with_alias(&token_alias) + token_alias ); } println!(); @@ -219,7 +216,7 @@ pub async fn query_transfers< if fvk_map.contains_key(&account) { print!(" {}:", fvk_map[&account]); for (token_addr, val) in masp_change { - let token_alias = lookup_alias(wallet, &token_addr.address); + let token_alias = lookup_alias(wallet, &token_addr); let sign = match val.cmp(&Change::zero()) { Ordering::Greater => "+", Ordering::Less => "-", @@ -234,7 +231,7 @@ pub async fn query_transfers< val.into(), ) .await, - token_addr.format_with_alias(&token_alias), + token_alias, ); } println!(); @@ -263,7 +260,7 @@ pub async fn query_raw_bytes( /// Query token balance(s) pub async fn query_balance< C: namada::ledger::queries::Client + Sync, - U: ShieldedUtils, + U: ShieldedUtils, >( client: &C, wallet: &mut Wallet, @@ -302,50 +299,23 @@ pub async fn query_transparent_balance< wallet: &mut Wallet, args: args::QueryBalance, ) { - let tokens = wallet.get_addresses_with_vp_type(AddressVpType::Token); + let prefix = Key::from( + Address::Internal(namada::types::address::InternalAddress::Multitoken) + .to_db_key(), + ); match (args.token, args.owner) { (Some(token), Some(owner)) => { - let (balance_key, sub_prefix) = match &args.sub_prefix { - Some(sub_prefix) => { - let sub_prefix = Key::parse(sub_prefix).unwrap(); - let prefix = - token::multitoken_balance_prefix(&token, &sub_prefix); - ( - token::multitoken_balance_key( - &prefix, - &owner.address().unwrap(), - ), - Some(sub_prefix), - ) - } - None => ( - token::balance_key(&token, &owner.address().unwrap()), - None, - ), - }; + let balance_key = + token::balance_key(&token, &owner.address().unwrap()); let token_alias = lookup_alias(wallet, &token); match query_storage_value::(client, &balance_key) .await { Some(balance) => { - let balance = format_denominated_amount( - client, - &TokenAddress { - address: token, - sub_prefix, - }, - balance, - ) - .await; - match &args.sub_prefix { - Some(sub_prefix) => { - println!( - "{} with {}: {}", - token_alias, sub_prefix, balance - ); - } - None => println!("{}: {}", token_alias, balance), - } + let balance = + format_denominated_amount(client, &token, balance) + .await; + println!("{}: {}", token_alias, balance); } None => { println!("No {} balance found for {}", token_alias, owner) @@ -353,22 +323,17 @@ pub async fn query_transparent_balance< } } (None, Some(owner)) => { - for token in tokens { - let prefix = - token::balance_key(&token, &owner.address().unwrap()); - let balances = - query_storage_prefix::(client, &prefix) - .await; - if let Some(balances) = balances { - print_balances( - client, - wallet, - balances, - &token, - owner.address().as_ref(), - ) - .await; - } + let balances = + query_storage_prefix::(client, &prefix).await; + if let Some(balances) = balances { + print_balances( + client, + wallet, + balances, + None, + owner.address().as_ref(), + ) + .await; } } (Some(token), None) => { @@ -376,19 +341,15 @@ pub async fn query_transparent_balance< let balances = query_storage_prefix::(client, &prefix).await; if let Some(balances) = balances { - print_balances(client, wallet, balances, &token, None).await; + print_balances(client, wallet, balances, Some(&token), None) + .await; } } (None, None) => { - for token in tokens { - let key = token::balance_prefix(&token); - let balances = - query_storage_prefix::(client, &key) - .await; - if let Some(balances) = balances { - print_balances(client, wallet, balances, &token, None) - .await; - } + let balances = + query_storage_prefix::(client, &prefix).await; + if let Some(balances) = balances { + print_balances(client, wallet, balances, None, None).await; } } } @@ -397,7 +358,7 @@ pub async fn query_transparent_balance< /// Query the token pinned balance(s) pub async fn query_pinned_balance< C: namada::ledger::queries::Client + Sync, - U: ShieldedUtils, + U: ShieldedUtils, >( client: &C, wallet: &mut Wallet, @@ -439,10 +400,7 @@ pub async fn query_pinned_balance< } // If a suitable viewing key was not found, then demand it from the user if balance == pinned_error { - print!("Enter the viewing key for {}: ", owner); - io::stdout().flush().unwrap(); - let mut vk_str = String::new(); - io::stdin().read_line(&mut vk_str).unwrap(); + let vk_str = prompt!("Enter the viewing key for {}: ", owner); let fvk = match ExtendedViewingKey::from_str(vk_str.trim()) { Ok(fvk) => fvk, _ => { @@ -458,24 +416,19 @@ pub async fn query_pinned_balance< } // Now print out the received quantities according to CLI arguments - match (balance, args.token.as_ref(), args.sub_prefix.as_ref()) { - (Err(PinnedBalanceError::InvalidViewingKey), _, _) => println!( + match (balance, args.token.as_ref()) { + (Err(PinnedBalanceError::InvalidViewingKey), _) => println!( "Supplied viewing key cannot decode transactions to given \ payment address." ), - (Err(PinnedBalanceError::NoTransactionPinned), _, _) => { + (Err(PinnedBalanceError::NoTransactionPinned), _) => { println!("Payment address {} has not yet been consumed.", owner) } - (Ok((balance, epoch)), Some(token), sub_prefix) => { + (Ok((balance, epoch)), Some(token)) => { let token_alias = lookup_alias(wallet, token); - let token_address = TokenAddress { - address: token.clone(), - sub_prefix: sub_prefix - .map(|string| Key::parse(string).unwrap()), - }; let total_balance = balance - .get(&(epoch, token_address.clone())) + .get(&(epoch, token.clone())) .cloned() .unwrap_or_default(); @@ -483,28 +436,23 @@ pub async fn query_pinned_balance< println!( "Payment address {} was consumed during epoch {}. \ Received no shielded {}", - owner, - epoch, - token_address.format_with_alias(&token_alias) + owner, epoch, token_alias ); } else { let formatted = format_denominated_amount( client, - &token_address, + token, total_balance.into(), ) .await; println!( "Payment address {} was consumed during epoch {}. \ Received {} {}", - owner, - epoch, - formatted, - token_address.format_with_alias(&token_alias), + owner, epoch, formatted, token_alias, ); } } - (Ok((balance, epoch)), None, _) => { + (Ok((balance, epoch)), None) => { let mut found_any = false; for ((_, token_addr), value) in balance @@ -526,14 +474,10 @@ pub async fn query_pinned_balance< ) .await; let token_alias = tokens - .get(&token_addr.address) + .get(token_addr) .map(|a| a.to_string()) - .unwrap_or_else(|| token_addr.address.to_string()); - println!( - " {}: {}", - token_addr.format_with_alias(&token_alias), - formatted, - ); + .unwrap_or_else(|| token_addr.to_string()); + println!(" {}: {}", token_alias, formatted,); } if !found_any { println!( @@ -551,77 +495,74 @@ async fn print_balances( client: &C, wallet: &Wallet, balances: impl Iterator, - token: &Address, + token: Option<&Address>, target: Option<&Address>, ) { let stdout = io::stdout(); let mut w = stdout.lock(); - let token_alias = lookup_alias(wallet, token); - writeln!(w, "Token {}", token_alias).unwrap(); let mut print_num = 0; + let mut print_token = None; for (key, balance) in balances { - let (o, s) = match token::is_any_multitoken_balance_key(&key) { - Some((sub_prefix, [tok, owner])) => ( + // Get the token, the owner, and the balance with the token and the + // owner + let (t, o, s) = match token::is_any_token_balance_key(&key) { + Some([tok, owner]) => ( + tok.clone(), owner.clone(), format!( - "with {}: {}, owned by {}", - sub_prefix.clone(), - format_denominated_amount( - client, - &TokenAddress { - address: tok.clone(), - sub_prefix: Some(sub_prefix) - }, - balance - ) - .await, + ": {}, owned by {}", + format_denominated_amount(client, tok, balance).await, lookup_alias(wallet, owner) ), ), - None => { - if let Some([tok, owner]) = - token::is_any_token_balance_key(&key) - { - ( - owner.clone(), - format!( - ": {}, owned by {}", - format_denominated_amount( - client, - &TokenAddress { - address: tok.clone(), - sub_prefix: None - }, - balance - ) - .await, - lookup_alias(wallet, owner) - ), - ) - } else { - continue; - } - } + None => continue, }; - let s = match target { - Some(t) if o == *t => s, - Some(_) => continue, - None => s, + // Get the token and the balance + let (t, s) = match (token, target) { + // the given token and the given target are the same as the + // retrieved ones + (Some(token), Some(target)) if t == *token && o == *target => { + (t, s) + } + // the given token is the same as the retrieved one + (Some(token), None) if t == *token => (t, s), + // the given target is the same as the retrieved one + (None, Some(target)) if o == *target => (t, s), + // no specified token or target + (None, None) => (t, s), + // otherwise, this balance will not be printed + _ => continue, }; + // Print the token if it isn't printed yet + match &print_token { + Some(token) if *token == t => { + // the token has been already printed + } + _ => { + let token_alias = lookup_alias(wallet, &t); + writeln!(w, "Token {}", token_alias).unwrap(); + print_token = Some(t); + } + } + // Print the balance writeln!(w, "{}", s).unwrap(); print_num += 1; } if print_num == 0 { - match target { - Some(t) => { - writeln!(w, "No balances owned by {}", lookup_alias(wallet, t)) - .unwrap() - } - None => { + match (token, target) { + (Some(_), Some(target)) | (None, Some(target)) => writeln!( + w, + "No balances owned by {}", + lookup_alias(wallet, target) + ) + .unwrap(), + (Some(token), None) => { + let token_alias = lookup_alias(wallet, token); writeln!(w, "No balances for token {}", token_alias).unwrap() } + (None, None) => writeln!(w, "No balances").unwrap(), } } } @@ -761,7 +702,7 @@ pub async fn query_proposal( /// Query token shielded balance(s) pub async fn query_shielded_balance< C: namada::ledger::queries::Client + Sync, - U: ShieldedUtils, + U: ShieldedUtils, >( client: &C, wallet: &mut Wallet, @@ -811,27 +752,22 @@ pub async fn query_shielded_balance< let token_alias = lookup_alias(wallet, &token); - let token_address = TokenAddress { - address: token, - sub_prefix: args.sub_prefix.map(|k| Key::parse(k).unwrap()), - }; - let total_balance = balance - .get(&(epoch, token_address.clone())) + .get(&(epoch, token.clone())) .cloned() .unwrap_or_default(); if total_balance.is_zero() { println!( "No shielded {} balance found for given key", - token_address.format_with_alias(&token_alias) + token_alias ); } else { println!( "{}: {}", - token_address.format_with_alias(&token_alias), + token_alias, format_denominated_amount( client, - &token_address, + &token, token::Amount::from(total_balance) ) .await @@ -864,9 +800,6 @@ pub async fn query_shielded_balance< } } - // These are the asset types for which we have human-readable names - let mut read_tokens: HashMap>> = - HashMap::new(); // Print non-zero balances whose asset types can be decoded // TODO Implement a function for this @@ -886,72 +819,21 @@ pub async fn query_shielded_balance< } } } - for ( - ( - fvk, - TokenAddress { - address: addr, - sub_prefix, - }, - ), - token_balance, - ) in balance_map - { - read_tokens - .entry(addr.clone()) - .and_modify(|addr_vec| addr_vec.push(sub_prefix.clone())) - .or_insert_with(|| vec![sub_prefix.clone()]); - let token_address = TokenAddress { - address: addr, - sub_prefix, - }; + for ((fvk, token), token_balance) in balance_map { // Only assets with the current timestamp count let alias = tokens - .get(&token_address.address) + .get(&token) .map(|a| a.to_string()) - .unwrap_or_else(|| token_address.address.to_string()); - println!( - "Shielded Token {}:", - token_address.format_with_alias(&alias), - ); + .unwrap_or_else(|| token.to_string()); + println!("Shielded Token {}:", alias); let formatted = format_denominated_amount( client, - &token_address, + &token, token_balance.into(), ) .await; println!(" {}, owned by {}", formatted, fvk); } - // Print zero balances for remaining assets - for token in tokens { - if let Some(sub_addrs) = read_tokens.get(&token) { - let token_alias = lookup_alias(wallet, &token); - for sub_addr in sub_addrs { - match sub_addr { - // abstract out these prints - Some(sub_addr) => { - println!( - "Shielded Token {}/{}:", - token_alias, sub_addr - ); - println!( - "No shielded {}/{} balance found for any \ - wallet key", - token_alias, sub_addr - ); - } - None => { - println!("Shielded Token {}:", token_alias,); - println!( - "No shielded {} balance found for any \ - wallet key", - token_alias - ); - } - } - } - } - } } // Here the user wants to know the balance for a specific token across // users @@ -969,17 +851,7 @@ pub async fn query_shielded_balance< println!("Shielded Token {}:", token_alias); let mut found_any = false; let token_alias = lookup_alias(wallet, &token); - let token_address = TokenAddress { - address: token.clone(), - sub_prefix: args - .sub_prefix - .as_ref() - .map(|k| Key::parse(k).unwrap()), - }; - println!( - "Shielded Token {}:", - token_address.format_with_alias(&token_alias), - ); + println!("Shielded Token {}:", token_alias,); for fvk in viewing_keys { // Query the multi-asset balance at the given spending key let viewing_key = ExtendedFullViewingKey::from(fvk).fvk.vk; @@ -1011,7 +883,7 @@ pub async fn query_shielded_balance< if !found_any { println!( "No shielded {} balance found for any wallet key", - token_address.format_with_alias(&token_alias), + token_alias, ); } } @@ -1056,10 +928,7 @@ pub async fn print_decoded_balance< { println!( "{} : {}", - token_addr.format_with_alias(&lookup_alias( - wallet, - &token_addr.address - )), + lookup_alias(wallet, token_addr), format_denominated_amount(client, token_addr, (*amount).into()) .await, ); @@ -1081,12 +950,12 @@ pub async fn print_decoded_balance_with_epoch< for ((epoch, token_addr), value) in decoded_balance.iter() { let asset_value = (*value).into(); let alias = tokens - .get(&token_addr.address) + .get(token_addr) .map(|a| a.to_string()) .unwrap_or_else(|| token_addr.to_string()); println!( "{} | {} : {}", - token_addr.format_with_alias(&alias), + alias, epoch, format_denominated_amount(client, token_addr, asset_value).await, ); @@ -1098,7 +967,7 @@ pub async fn get_token_balance( client: &C, token: &Address, owner: &Address, -) -> Option { +) -> token::Amount { namada::ledger::rpc::get_token_balance(client, token, owner).await } @@ -1587,14 +1456,14 @@ pub async fn query_bonded_stake( } None => { let consensus = - unwrap_client_response::>( + unwrap_client_response::>( RPC.vp() .pos() .consensus_validator_set(client, &Some(epoch)) .await, ); let below_capacity = - unwrap_client_response::>( + unwrap_client_response::>( RPC.vp() .pos() .below_capacity_validator_set(client, &Some(epoch)) @@ -1606,7 +1475,7 @@ pub async fn query_bonded_stake( let mut w = stdout.lock(); writeln!(w, "Consensus validators:").unwrap(); - for val in consensus { + for val in consensus.into_iter().rev() { writeln!( w, " {}: {}", @@ -1617,7 +1486,7 @@ pub async fn query_bonded_stake( } if !below_capacity.is_empty() { writeln!(w, "Below capacity validators:").unwrap(); - for val in &below_capacity { + for val in below_capacity.into_iter().rev() { writeln!( w, " {}: {}", @@ -1654,6 +1523,60 @@ pub async fn query_commission_rate< ) } +/// Query and return validator's state +pub async fn query_validator_state< + C: namada::ledger::queries::Client + Sync, +>( + client: &C, + validator: &Address, + epoch: Option, +) -> Option { + unwrap_client_response::>( + RPC.vp() + .pos() + .validator_state(client, validator, &epoch) + .await, + ) +} + +/// Query a validator's state information +pub async fn query_and_print_validator_state< + C: namada::ledger::queries::Client + Sync, +>( + client: &C, + _wallet: &mut Wallet, + args: args::QueryValidatorState, +) { + let validator = args.validator; + let state: Option = + query_validator_state(client, &validator, args.epoch).await; + + match state { + Some(state) => match state { + ValidatorState::Consensus => { + println!("Validator {validator} is in the consensus set") + } + ValidatorState::BelowCapacity => { + println!("Validator {validator} is in the below-capacity set") + } + ValidatorState::BelowThreshold => { + println!("Validator {validator} is in the below-threshold set") + } + ValidatorState::Inactive => { + println!("Validator {validator} is inactive") + } + ValidatorState::Jailed => { + println!("Validator {validator} is jailed") + } + }, + None => println!( + "Validator {validator} is either not a validator, or an epoch \ + before the current epoch has been queried (and the validator \ + state information is no longer stored)" + ), + } +} + /// Query PoS validator's commission rate information pub async fn query_and_print_commission_rate< C: namada::ledger::queries::Client + Sync, @@ -1694,11 +1617,6 @@ pub async fn query_slashes( _wallet: &mut Wallet, args: args::QuerySlashes, ) { - let params_key = pos::params_key(); - let params = query_storage_value::(client, ¶ms_key) - .await - .expect("Parameter should be defined."); - match args.validator { Some(validator) => { let validator = validator; @@ -1707,18 +1625,54 @@ pub async fn query_slashes( RPC.vp().pos().validator_slashes(client, &validator).await, ); if !slashes.is_empty() { + println!("Processed slashes:"); let stdout = io::stdout(); let mut w = stdout.lock(); for slash in slashes { writeln!( w, - "Slash epoch {}, type {}, rate {}", - slash.epoch, slash.r#type, slash.rate + "Infraction epoch {}, block height {}, type {}, rate \ + {}", + slash.epoch, + slash.block_height, + slash.r#type, + slash.rate ) .unwrap(); } } else { - println!("No slashes found for {}", validator.encode()) + println!( + "No processed slashes found for {}", + validator.encode() + ) + } + // Find enqueued slashes to be processed in the future for the given + // validator + let enqueued_slashes: HashMap< + Address, + BTreeMap>, + > = unwrap_client_response::< + C, + HashMap>>, + >(RPC.vp().pos().enqueued_slashes(client).await); + let enqueued_slashes = enqueued_slashes.get(&validator).cloned(); + if let Some(enqueued) = enqueued_slashes { + println!("\nEnqueued slashes for future processing"); + for (epoch, slashes) in enqueued { + println!("To be processed in epoch {}", epoch); + for slash in slashes { + let stdout = io::stdout(); + let mut w = stdout.lock(); + writeln!( + w, + "Infraction epoch {}, block height {}, type {}", + slash.epoch, slash.block_height, slash.r#type, + ) + .unwrap(); + } + } + } else { + println!("No enqueued slashes found for {}", validator.encode()) } } None => { @@ -1730,15 +1684,16 @@ pub async fn query_slashes( if !all_slashes.is_empty() { let stdout = io::stdout(); let mut w = stdout.lock(); + println!("Processed slashes:"); for (validator, slashes) in all_slashes.into_iter() { for slash in slashes { writeln!( w, - "Slash epoch {}, block height {}, rate {}, type \ - {}, validator {}", + "Infraction epoch {}, block height {}, rate {}, \ + type {}, validator {}", slash.epoch, slash.block_height, - slash.r#type.get_slash_rate(¶ms), + slash.rate, slash.r#type, validator, ) @@ -1746,7 +1701,41 @@ pub async fn query_slashes( } } } else { - println!("No slashes found") + println!("No processed slashes found") + } + + // Find enqueued slashes to be processed in the future for the given + // validator + let enqueued_slashes: HashMap< + Address, + BTreeMap>, + > = unwrap_client_response::< + C, + HashMap>>, + >(RPC.vp().pos().enqueued_slashes(client).await); + if !enqueued_slashes.is_empty() { + println!("\nEnqueued slashes for future processing"); + for (validator, slashes_by_epoch) in enqueued_slashes { + for (epoch, slashes) in slashes_by_epoch { + println!("\nTo be processed in epoch {}", epoch); + for slash in slashes { + let stdout = io::stdout(); + let mut w = stdout.lock(); + writeln!( + w, + "Infraction epoch {}, block height {}, type \ + {}, validator {}", + slash.epoch, + slash.block_height, + slash.r#type, + validator + ) + .unwrap(); + } + } + } + } else { + println!("\nNo enqueued slashes found for future processing") } } } @@ -1871,7 +1860,7 @@ pub async fn query_conversions( .expect("Conversions should be defined"); // Track whether any non-sentinel conversions are found let mut conversions_found = false; - for ((addr, sub, _), epoch, conv, _) in conv_state.assets.values() { + for ((addr, _), epoch, conv, _) in conv_state.assets.values() { let amt: masp_primitives::transaction::components::Amount = conv.clone().into(); // If the user has specified any targets, then meet them @@ -1885,9 +1874,8 @@ pub async fn query_conversions( conversions_found = true; // Print the asset to which the conversion applies print!( - "{}{}[{}]: ", + "{}[{}]: ", tokens.get(addr).cloned().unwrap_or_else(|| addr.clone()), - sub.as_ref().map(|k| format!("/{}", k)).unwrap_or_default(), epoch, ); // Now print out the components of the allowed conversion @@ -1895,14 +1883,13 @@ pub async fn query_conversions( for (asset_type, val) in amt.components() { // Look up the address and epoch of asset to facilitate pretty // printing - let ((addr, sub, _), epoch, _, _) = &conv_state.assets[asset_type]; + let ((addr, _), epoch, _, _) = &conv_state.assets[asset_type]; // Now print out this component of the conversion print!( - "{}{} {}{}[{}]", + "{}{} {}[{}]", prefix, val, tokens.get(addr).cloned().unwrap_or_else(|| addr.clone()), - sub.as_ref().map(|k| format!("/{}", k)).unwrap_or_default(), epoch ); // Future iterations need to be prefixed with + @@ -1922,7 +1909,6 @@ pub async fn query_conversion( asset_type: AssetType, ) -> Option<( Address, - Option, MaspDenom, Epoch, masp_primitives::transaction::components::Amount, @@ -2051,6 +2037,21 @@ pub async fn query_result( } } +pub async fn epoch_sleep( + client: &C, + _args: args::Query, +) { + let start_epoch = query_and_print_epoch(client).await; + loop { + tokio::time::sleep(core::time::Duration::from_secs(1)).await; + let current_epoch = query_epoch(client).await; + if current_epoch > start_epoch { + println!("Reached epoch {}", current_epoch); + break; + } + } +} + pub async fn get_proposal_votes( client: &C, epoch: Epoch, @@ -2328,52 +2329,3 @@ fn unwrap_client_response( cli::safe_exit(1) }) } - -/// Get the correct representation of the amount given the token type. -pub async fn validate_amount( - client: &C, - amount: InputAmount, - token: &Address, - sub_prefix: &Option, - force: bool, -) -> token::DenominatedAmount { - let input_amount = match amount { - InputAmount::Unvalidated(amt) => amt.canonical(), - InputAmount::Validated(amt) => return amt, - }; - let denom = unwrap_client_response::>( - RPC.vp() - .token() - .denomination(client, token, sub_prefix) - .await, - ) - .unwrap_or_else(|| { - if force { - println!( - "No denomination found for token: {token}, but --force was \ - passed. Defaulting to the provided denomination." - ); - input_amount.denom - } else { - println!( - "No denomination found for token: {token}, the input \ - arguments could not be parsed." - ); - cli::safe_exit(1); - } - }); - if denom < input_amount.denom && !force { - println!( - "The input amount contained a higher precision than allowed by \ - {token}." - ); - cli::safe_exit(1); - } else { - input_amount.increase_precision(denom).unwrap_or_else(|_| { - println!( - "The amount provided requires more the 256 bits to represent." - ); - cli::safe_exit(1); - }) - } -} diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 18c0a6c3b1..7e91bbefb2 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -10,6 +10,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; use data_encoding::HEXLOWER_PERMISSIVE; use masp_proofs::prover::LocalTxProver; use namada::ledger::governance::storage as gov_storage; +use namada::ledger::queries::Client; use namada::ledger::rpc::{TxBroadcastData, TxResponse}; use namada::ledger::signing::find_pk; use namada::ledger::wallet::{Wallet, WalletUtils}; @@ -34,7 +35,6 @@ use crate::client::rpc::query_wasm_code_hash; use crate::client::tx::tx::ProcessTxResponse; use crate::config::TendermintMode; use crate::facade::tendermint_rpc::endpoint::broadcast::tx_sync::Response; -use crate::facade::tendermint_rpc::HttpClient; use crate::node::ledger::tendermint_node; use crate::wallet::{gen_validator_keys, read_and_confirm_encryption_password}; @@ -545,8 +545,6 @@ impl Default for CLIShieldedUtils { #[async_trait(?Send)] impl masp::ShieldedUtils for CLIShieldedUtils { - type C = crate::facade::tendermint_rpc::HttpClient; - fn local_tx_prover(&self) -> LocalTxProver { if let Ok(params_dir) = env::var(masp::ENV_VAR_MASP_PARAMS_DIR) { let params_dir = PathBuf::from(params_dir); @@ -607,8 +605,8 @@ impl masp::ShieldedUtils for CLIShieldedUtils { } } -pub async fn submit_transfer( - client: &HttpClient, +pub async fn submit_transfer( + client: &C, mut ctx: Context, args: args::TxTransfer, ) -> Result<(), tx::Error> { @@ -832,8 +830,7 @@ where let balance = rpc::get_token_balance(client, &ctx.native_token, &proposal.author) - .await - .unwrap_or_default(); + .await; if balance < token::Amount::from_uint( governance_parameters.min_proposal_fund, @@ -1556,7 +1553,6 @@ mod test_tx { use masp_primitives::transaction::components::Amount; use namada::ledger::masp::{make_asset_type, MaspAmount}; use namada::types::address::testing::gen_established_address; - use namada::types::storage::DbKeySeg; use namada::types::token::MaspDenom; use super::*; @@ -1564,25 +1560,14 @@ mod test_tx { #[test] fn test_masp_add_amount() { let address_1 = gen_established_address(); - let prefix_1: Key = - DbKeySeg::StringSeg("eth_seg".parse().unwrap()).into(); - let prefix_2: Key = - DbKeySeg::StringSeg("crypto_kitty".parse().unwrap()).into(); let denom_1 = MaspDenom::One; let denom_2 = MaspDenom::Three; let epoch = Epoch::default(); let _masp_amount = MaspAmount::default(); - let asset_base = make_asset_type( - Some(epoch), - &address_1, - &Some(prefix_1.clone()), - denom_1, - ); - let _asset_denom = - make_asset_type(Some(epoch), &address_1, &Some(prefix_1), denom_2); - let _asset_prefix = - make_asset_type(Some(epoch), &address_1, &Some(prefix_2), denom_1); + let asset_base = make_asset_type(Some(epoch), &address_1, denom_1); + let _asset_denom = make_asset_type(Some(epoch), &address_1, denom_2); + let _asset_prefix = make_asset_type(Some(epoch), &address_1, denom_1); let _amount_base = Amount::from_pair(asset_base, 16).expect("Test failed"); diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index 272d74b6eb..6f20fe165a 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -47,41 +47,6 @@ const DEFAULT_NETWORK_CONFIGS_SERVER: &str = /// We do pre-genesis validator set up in this directory pub const PRE_GENESIS_DIR: &str = "pre-genesis"; -/// Environment variable set to reduce the amount of printing the CLI -/// tools perform. Extra prints, while good for UI, clog up test tooling. -pub const REDUCED_CLI_PRINTING: &str = "REDUCED_CLI_PRINTING"; - -macro_rules! cli_print { - ($($arg:tt)*) => {{ - if std::env::var(REDUCED_CLI_PRINTING) - .map(|v| if v.to_lowercase().trim() == "true" { - false - } else { - true - }).unwrap_or(true) { - let mut stdout = std::io::stdout().lock(); - _ = stdout.write_all(format!("{}", std::format_args!($($arg)*)).as_bytes()); - _ = stdout.flush(); - } - }}; -} - -#[allow(unused)] -macro_rules! cli_println { - ($($arg:tt)*) => {{ - if std::env::var(REDUCED_CLI_PRINTING) - .map(|v| if v.to_lowercase().trim() == "true" { - false - } else { - true - }).unwrap_or(true) { - let mut stdout = std::io::stdout().lock(); - _ = stdout.write_all(format!("{}\n", std::format_args!($($arg)*)).as_bytes()); - _ = stdout.flush(); - } - }}; -} - /// Configure Namada to join an existing network. The chain must be released in /// the repository. pub async fn join_network( @@ -1204,9 +1169,9 @@ where print!("{}", msg); _ = std::io::stdout().flush(); for c in spinny_wheel.chars().cycle() { - cli_print!("{}", c); + print!("{}", c); std::thread::sleep(std::time::Duration::from_secs(1)); - cli_print!("{}", (8u8 as char)); + print!("{}", (8u8 as char)); if task.is_finished() { break; } diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index d1ff8417a5..63a7586d59 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -22,6 +22,7 @@ use namada::types::key::dkg_session_keys::DkgPublicKey; use namada::types::key::*; use namada::types::time::{DateTimeUtc, DurationSecs}; use namada::types::token::Denomination; +use namada::types::uint::Uint; use namada::types::{storage, token}; /// Genesis configuration file format @@ -45,6 +46,7 @@ pub mod genesis_config { use namada::types::key::*; use namada::types::time::Rfc3339String; use namada::types::token::Denomination; + use namada::types::uint::Uint; use namada::types::{storage, token}; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -122,8 +124,9 @@ pub mod genesis_config { /// Testnet faucet PoW difficulty - defaults to `0` when not set pub faucet_pow_difficulty: Option, #[cfg(not(feature = "mainnet"))] - /// Testnet faucet withdrawal limit - defaults to 1000 NAM when not set - pub faucet_withdrawal_limit: Option, + /// Testnet faucet withdrawal limit - defaults to 1000 tokens when not + /// set + pub faucet_withdrawal_limit: Option, // Initial validator set pub validator: HashMap, // Token accounts present at genesis @@ -394,27 +397,13 @@ pub mod genesis_config { fn load_token( config: &TokenAccountConfig, - wasm: &HashMap, validators: &HashMap, established_accounts: &HashMap, implicit_accounts: &HashMap, ) -> TokenAccount { - let token_vp_name = config.vp.as_ref().unwrap(); - let token_vp_config = wasm.get(token_vp_name).unwrap(); - TokenAccount { address: Address::decode(config.address.as_ref().unwrap()).unwrap(), denom: config.denom, - vp_code_path: token_vp_config.filename.to_owned(), - vp_sha256: token_vp_config - .sha256 - .clone() - .unwrap_or_else(|| { - eprintln!("Unknown token VP WASM sha256"); - cli::safe_exit(1); - }) - .to_sha256_bytes() - .unwrap(), balances: config .balances .as_ref() @@ -559,7 +548,6 @@ pub mod genesis_config { .expect("Missing native token address"), ) .expect("Invalid address"); - let validators: HashMap = validator .iter() .map(|(name, cfg)| (name.clone(), load_validator(cfg, &wasm))) @@ -581,7 +569,6 @@ pub mod genesis_config { .map(|(_name, cfg)| { load_token( cfg, - &wasm, &validators, &established_accounts, &implicit_accounts, @@ -737,7 +724,7 @@ pub struct Genesis { #[cfg(not(feature = "mainnet"))] pub faucet_pow_difficulty: Option, #[cfg(not(feature = "mainnet"))] - pub faucet_withdrawal_limit: Option, + pub faucet_withdrawal_limit: Option, pub validators: Vec, pub token_accounts: Vec, pub established_accounts: Vec, @@ -819,10 +806,6 @@ pub struct TokenAccount { pub address: Address, /// The number of decimal places amounts of this token has pub denom: Denomination, - /// Validity predicate code WASM - pub vp_code_path: String, - /// Expected SHA-256 hash of the validity predicate wasm - pub vp_sha256: [u8; 32], /// Accounts' balances of this token #[derivative(PartialOrd = "ignore", Ord = "ignore")] pub balances: HashMap, @@ -910,7 +893,6 @@ pub fn genesis(num_validators: u64) -> Genesis { use crate::wallet; let vp_implicit_path = "vp_implicit.wasm"; - let vp_token_path = "vp_token.wasm"; let vp_user_path = "vp_user.wasm"; // NOTE When the validator's key changes, tendermint must be reset with @@ -1044,9 +1026,9 @@ pub fn genesis(num_validators: u64) -> Genesis { public_key: wallet::defaults::ester_keypair().ref_to(), }, ]; - let default_user_tokens = token::Amount::native_whole(1_000_000); - let default_key_tokens = token::Amount::native_whole(1_000); - let mut balances: HashMap = HashMap::from_iter([ + let default_user_tokens = Uint::from(1_000_000); + let default_key_tokens = Uint::from(1_000_000); + let mut balances: HashMap = HashMap::from_iter([ // established accounts' balances (wallet::defaults::albert_address(), default_user_tokens), (wallet::defaults::bertha_address(), default_user_tokens), @@ -1090,9 +1072,11 @@ pub fn genesis(num_validators: u64) -> Genesis { .map(|(address, (_, denom))| TokenAccount { address, denom, - vp_code_path: vp_token_path.into(), - vp_sha256: Default::default(), - balances: balances.clone(), + balances: balances + .clone() + .into_iter() + .map(|(k, v)| (k, token::Amount::from_uint(v, denom).unwrap())) + .collect(), }) .collect(); Genesis { diff --git a/apps/src/lib/node/ledger/mod.rs b/apps/src/lib/node/ledger/mod.rs index ebc86468c4..37b447b4da 100644 --- a/apps/src/lib/node/ledger/mod.rs +++ b/apps/src/lib/node/ledger/mod.rs @@ -1,8 +1,8 @@ mod abortable; mod broadcaster; pub mod ethereum_oracle; -mod shell; -mod shims; +pub mod shell; +pub mod shims; pub mod storage; pub mod tendermint_node; diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 3f11f008b8..679c528280 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -4,7 +4,6 @@ use std::collections::HashMap; use data_encoding::HEXUPPER; use namada::ledger::parameters::storage as params_storage; -use namada::ledger::pos::types::into_tm_voting_power; use namada::ledger::pos::{namada_proof_of_stake, staking_token_address}; use namada::ledger::storage::EPOCH_SWITCH_BLOCKS_DELAY; use namada::ledger::storage_api::token::credit_tokens; @@ -21,7 +20,7 @@ use namada::types::address::Address; use namada::types::dec::Dec; use namada::types::key::tm_raw_hash_to_string; use namada::types::storage::{BlockHash, BlockResults, Epoch, Header}; -use namada::types::token::{total_supply_key, Amount}; +use namada::types::token::Amount; use namada::types::transaction::protocol::{ ethereum_tx_data_variants, ProtocolTxType, }; @@ -32,7 +31,6 @@ use super::*; use crate::facade::tendermint_proto::abci::{ Misbehavior as Evidence, VoteInfo, }; -use crate::facade::tendermint_proto::crypto::PublicKey as TendermintPublicKey; use crate::node::ledger::shell::stats::InternalStats; impl Shell @@ -105,11 +103,25 @@ where &mut self.wl_storage, current_epoch, current_epoch + pos_params.pipeline_len, - &namada_proof_of_stake::consensus_validator_set_handle(), - &namada_proof_of_stake::below_capacity_validator_set_handle(), + )?; + namada_proof_of_stake::store_total_consensus_stake( + &mut self.wl_storage, + current_epoch, + )?; + namada_proof_of_stake::purge_validator_sets_for_old_epoch( + &mut self.wl_storage, + current_epoch, )?; } + // Invariant: Has to be applied before `record_slashes_from_evidence` + // because it potentially needs to be able to read validator state from + // previous epoch and jailing validator removes the historical state + self.log_block_rewards(&req.votes, height, current_epoch, new_epoch)?; + if new_epoch { + self.apply_inflation(current_epoch)?; + } + // Invariant: This has to be applied after // `copy_validator_sets_and_positions` and before `self.update_epoch`. self.record_slashes_from_evidence(); @@ -201,7 +213,6 @@ where let tx_hash_key = replay_protection::get_tx_hash_key(&tx_hash); self.wl_storage - .storage .delete(&tx_hash_key) .expect("Error while deleting tx hash from storage"); } @@ -221,16 +232,14 @@ where processed_tx.header_hash().0, )); self.wl_storage - .storage - .write(&wrapper_tx_hash_key, vec![]) + .write_bytes(&wrapper_tx_hash_key, vec![]) .expect("Error while writing tx hash to storage"); let inner_tx_hash_key = replay_protection::get_tx_hash_key( &tx.clone().update_header(TxType::Raw).header_hash(), ); self.wl_storage - .storage - .write(&inner_tx_hash_key, vec![]) + .write_bytes(&inner_tx_hash_key, vec![]) .expect("Error while writing tx hash to storage"); #[cfg(not(feature = "mainnet"))] @@ -256,11 +265,7 @@ where match balance.checked_sub(wrapper_fees) { Some(amount) => { self.wl_storage - .storage - .write( - &balance_key, - amount.try_to_vec().unwrap(), - ) + .write(&balance_key, amount) .unwrap(); } None => { @@ -271,12 +276,9 @@ where if reject { // Burn remaining funds self.wl_storage - .storage .write( &balance_key, - Amount::native_whole(0) - .try_to_vec() - .unwrap(), + Amount::native_whole(0), ) .unwrap(); tx_event["info"] = @@ -481,6 +483,7 @@ where ); stats.increment_errored_txs(); + self.wl_storage.drop_tx(); // If transaction type is Decrypted and failed because of // out of gas, remove its hash from storage to allow // rewrapping it @@ -491,15 +494,16 @@ where let tx_hash_key = replay_protection::get_tx_hash_key(&hash); self.wl_storage - .storage .delete(&tx_hash_key) .expect( "Error while deleting tx hash key from storage", ); + // Apply only to remove its hash, + // since all other changes have already been dropped + self.wl_storage.commit_tx(); } } - self.wl_storage.drop_tx(); tx_event["gas_used"] = self .gas_meter .get_current_transaction_gas() @@ -530,42 +534,6 @@ where self.update_eth_oracle(); } - // Read the block proposer of the previously committed block in storage - // (n-1 if we are in the process of finalizing n right now). - match read_last_block_proposer_address(&self.wl_storage)? { - Some(proposer_address) => { - tracing::debug!( - "Found last block proposer: {proposer_address}" - ); - let votes = pos_votes_from_abci(&self.wl_storage, &req.votes); - namada_proof_of_stake::log_block_rewards( - &mut self.wl_storage, - if new_epoch { - current_epoch.prev() - } else { - current_epoch - }, - &proposer_address, - votes, - )?; - } - None => { - if height > BlockHeight::default().next_height() { - tracing::error!( - "Can't find the last block proposer at height {height}" - ); - } else { - tracing::debug!( - "No last block proposer at height {height}" - ); - } - } - } - - if new_epoch { - self.apply_inflation(current_epoch)?; - } - if !req.proposer_address.is_empty() { let tm_raw_hash_string = tm_raw_hash_to_string(req.proposer_address); @@ -634,45 +602,9 @@ where /// changes to the validator sets and consensus parameters fn update_epoch(&mut self, response: &mut shim::response::FinalizeBlock) { // Apply validator set update - let (current_epoch, _gas) = self.wl_storage.storage.get_current_epoch(); - let pos_params = - namada_proof_of_stake::read_pos_params(&self.wl_storage) - .expect("Could not find the PoS parameters"); - // TODO ABCI validator updates on block H affects the validator set - // on block H+2, do we need to update a block earlier? - response.validator_updates = - namada_proof_of_stake::validator_set_update_tendermint( - &self.wl_storage, - &pos_params, - current_epoch, - |update| { - let (consensus_key, power) = match update { - ValidatorSetUpdate::Consensus(ConsensusValidator { - consensus_key, - bonded_stake, - }) => { - let power: i64 = into_tm_voting_power( - pos_params.tm_votes_per_token, - bonded_stake, - ); - (consensus_key, power) - } - ValidatorSetUpdate::Deactivated(consensus_key) => { - // Any validators that have been dropped from the - // consensus set must have voting power set to 0 to - // remove them from the conensus set - let power = 0_i64; - (consensus_key, power) - } - }; - let pub_key = TendermintPublicKey { - sum: Some(key_to_tendermint(&consensus_key).unwrap()), - }; - let pub_key = Some(pub_key); - ValidatorUpdate { pub_key, power } - }, - ) - .expect("Must be able to update validator sets"); + response.validator_updates = self + .get_abci_validator_updates(false) + .expect("Must be able to update validator set"); } /// Calculate the new inflation rate, mint the new tokens to the PoS @@ -707,9 +639,9 @@ where .expect("PoS inflation amount should exist in storage"); // Read from PoS storage let total_tokens = self - .read_storage_key(&total_supply_key(&staking_token_address( - &self.wl_storage, - ))) + .read_storage_key(&token::minted_balance_key( + &staking_token_address(&self.wl_storage), + )) .expect("Total NAM balance should exist in storage"); let pos_locked_supply = read_total_stake(&self.wl_storage, ¶ms, last_epoch)?; @@ -887,6 +819,48 @@ where Ok(()) } + + // Process the proposer and votes in the block to assign their PoS rewards. + fn log_block_rewards( + &mut self, + votes: &[VoteInfo], + height: BlockHeight, + current_epoch: Epoch, + new_epoch: bool, + ) -> Result<()> { + // Read the block proposer of the previously committed block in storage + // (n-1 if we are in the process of finalizing n right now). + match read_last_block_proposer_address(&self.wl_storage)? { + Some(proposer_address) => { + tracing::debug!( + "Found last block proposer: {proposer_address}" + ); + let votes = pos_votes_from_abci(&self.wl_storage, votes); + namada_proof_of_stake::log_block_rewards( + &mut self.wl_storage, + if new_epoch { + current_epoch.prev() + } else { + current_epoch + }, + &proposer_address, + votes, + )?; + } + None => { + if height > BlockHeight::default().next_height() { + tracing::error!( + "Can't find the last block proposer at height {height}" + ); + } else { + tracing::debug!( + "No last block proposer at height {height}" + ); + } + } + } + Ok(()) + } } /// Convert ABCI vote info to PoS vote info. Any info which fails the conversion @@ -1027,6 +1001,85 @@ mod test_finalize_block { FinalizeBlock, ProcessedTx, }; + /// Make a wrapper tx and a processed tx from the wrapped tx that can be + /// added to `FinalizeBlock` request. + fn mk_wrapper_tx( + shell: &TestShell, + keypair: &common::SecretKey, + ) -> (Tx, ProcessedTx) { + let mut wrapper_tx = + Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( + Fee { + amount: MIN_FEE_AMOUNT, + token: shell.wl_storage.storage.native_token.clone(), + }, + keypair.ref_to(), + Epoch(0), + Default::default(), + #[cfg(not(feature = "mainnet"))] + None, + )))); + wrapper_tx.header.chain_id = shell.chain_id.clone(); + wrapper_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper_tx.set_data(Data::new( + "Encrypted transaction data".as_bytes().to_owned(), + )); + wrapper_tx.add_section(Section::Signature(Signature::new( + wrapper_tx.sechashes(), + keypair, + ))); + let tx = wrapper_tx.to_bytes(); + ( + wrapper_tx, + ProcessedTx { + tx, + result: TxResult { + code: ErrorCodes::Ok.into(), + info: "".into(), + }, + }, + ) + } + + /// Make a wrapper tx and a processed tx from the wrapped tx that can be + /// added to `FinalizeBlock` request. + fn mk_decrypted_tx( + shell: &mut TestShell, + keypair: &common::SecretKey, + ) -> ProcessedTx { + let tx_code = TestWasms::TxNoOp.read_bytes(); + let mut outer_tx = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( + Fee { + amount: MIN_FEE_AMOUNT, + token: shell.wl_storage.storage.native_token.clone(), + }, + keypair.ref_to(), + Epoch(0), + Default::default(), + #[cfg(not(feature = "mainnet"))] + None, + )))); + outer_tx.header.chain_id = shell.chain_id.clone(); + outer_tx.set_code(Code::new(tx_code)); + outer_tx.set_data(Data::new( + "Decrypted transaction data".as_bytes().to_owned(), + )); + shell.enqueue_tx(outer_tx.clone()); + outer_tx.update_header(TxType::Decrypted(DecryptedTx::Decrypted { + #[cfg(not(feature = "mainnet"))] + has_valid_pow: false, + })); + outer_tx.decrypt(::G2Affine::prime_subgroup_generator()) + .expect("Test failed"); + ProcessedTx { + tx: outer_tx.to_bytes(), + result: TxResult { + code: ErrorCodes::Ok.into(), + info: "".into(), + }, + } + } + /// Check that if a wrapper tx was rejected by [`process_proposal`], /// check that the correct event is returned. Check that it does /// not appear in the queue of txs to be decrypted @@ -1053,36 +1106,11 @@ mod test_finalize_block { // create some wrapper txs for i in 1u64..5 { - let mut wrapper = - Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( - Fee { - amount: MIN_FEE_AMOUNT, - token: shell.wl_storage.storage.native_token.clone(), - }, - keypair.ref_to(), - Epoch(0), - Default::default(), - #[cfg(not(feature = "mainnet"))] - None, - )))); - wrapper.header.chain_id = shell.chain_id.clone(); - wrapper.set_data(Data::new("wasm_code".as_bytes().to_owned())); - wrapper.set_code(Code::new( - format!("transaction data: {}", i).as_bytes().to_owned(), - )); - wrapper.add_section(Section::Signature(Signature::new( - wrapper.sechashes(), - &keypair, - ))); + let (wrapper, mut processed_tx) = mk_wrapper_tx(&shell, &keypair); if i > 1 { - processed_txs.push(ProcessedTx { - tx: wrapper.to_bytes(), - result: TxResult { - code: u32::try_from(i.rem_euclid(2)) - .expect("Test failed"), - info: "".into(), - }, - }); + processed_tx.result.code = + u32::try_from(i.rem_euclid(2)).unwrap(); + processed_txs.push(processed_tx); } else { shell.enqueue_tx(wrapper.clone()); } @@ -1248,75 +1276,14 @@ mod test_finalize_block { .unwrap(); // create two decrypted txs - let tx_code = TestWasms::TxNoOp.read_bytes(); - for i in 0..2 { - let mut outer_tx = - Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( - Fee { - amount: MIN_FEE_AMOUNT, - token: shell.wl_storage.storage.native_token.clone(), - }, - keypair.ref_to(), - Epoch(0), - Default::default(), - #[cfg(not(feature = "mainnet"))] - None, - )))); - outer_tx.header.chain_id = shell.chain_id.clone(); - outer_tx.set_code(Code::new(tx_code.clone())); - outer_tx.set_data(Data::new( - format!("Decrypted transaction data: {}", i) - .as_bytes() - .to_owned(), - )); - shell.enqueue_tx(outer_tx.clone()); - outer_tx.update_header(TxType::Decrypted(DecryptedTx::Decrypted { - #[cfg(not(feature = "mainnet"))] - has_valid_pow: false, - })); - outer_tx.decrypt(::G2Affine::prime_subgroup_generator()) - .expect("Test failed"); - processed_txs.push(ProcessedTx { - tx: outer_tx.to_bytes(), - result: TxResult { - code: ErrorCodes::Ok.into(), - info: "".into(), - }, - }); + for _ in 0..2 { + processed_txs.push(mk_decrypted_tx(&mut shell, &keypair)); } // create two wrapper txs - for i in 0..2 { - let mut wrapper_tx = - Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( - Fee { - amount: MIN_FEE_AMOUNT, - token: shell.wl_storage.storage.native_token.clone(), - }, - keypair.ref_to(), - Epoch(0), - Default::default(), - #[cfg(not(feature = "mainnet"))] - None, - )))); - wrapper_tx.header.chain_id = shell.chain_id.clone(); - wrapper_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); - wrapper_tx.set_data(Data::new( - format!("Encrypted transaction data: {}", i) - .as_bytes() - .to_owned(), - )); - wrapper_tx.add_section(Section::Signature(Signature::new( - wrapper_tx.sechashes(), - &keypair, - ))); - valid_txs.push(wrapper_tx.clone()); - processed_txs.push(ProcessedTx { - tx: wrapper_tx.to_bytes(), - result: TxResult { - code: ErrorCodes::Ok.into(), - info: "".into(), - }, - }); + for _ in 0..2 { + let (tx, processed_tx) = mk_wrapper_tx(&shell, &keypair); + valid_txs.push(tx.clone()); + processed_txs.push(processed_tx); } // Put the wrapper txs in front of the decrypted txs processed_txs.rotate_left(2); @@ -1629,10 +1596,12 @@ mod test_finalize_block { let bertha = crate::wallet::defaults::bertha_address(); // add bertha's escrowed `asset` to the pool { - let asset_key = wrapped_erc20s::Keys::from(&asset); - let owner_key = - asset_key.balance(&bridge_pool::BRIDGE_POOL_ADDRESS); - let supply_key = asset_key.supply(); + let token = wrapped_erc20s::token(&asset); + let owner_key = token::balance_key( + &token, + &bridge_pool::BRIDGE_POOL_ADDRESS, + ); + let supply_key = token::minted_balance_key(&token); let amt: Amount = 999_999_u64.into(); shell .wl_storage @@ -1735,6 +1704,21 @@ mod test_finalize_block { shell.wl_storage.storage.next_epoch_min_start_height = BlockHeight(5); shell.wl_storage.storage.next_epoch_min_start_time = DateTimeUtc::now(); + let txs_key = gen_keypair(); + // Add unshielded balance for fee payment + let balance_key = token::balance_key( + &shell.wl_storage.storage.native_token, + &Address::from(&txs_key.ref_to()), + ); + shell + .wl_storage + .storage + .write( + &balance_key, + Amount::native_whole(1000).try_to_vec().unwrap(), + ) + .unwrap(); + // Add a proposal to be executed on next epoch change. let mut add_proposal = |proposal_id, vote| { let validator = shell.mode.get_validator_address().unwrap().clone(); @@ -1830,12 +1814,32 @@ mod test_finalize_block { // Need to supply a proposer address and votes to flow through the // inflation code for _ in 0..20 { + // Add some txs + let mut txs = vec![]; + // create two decrypted txs + for _ in 0..2 { + txs.push(mk_decrypted_tx(&mut shell, &txs_key)); + } + // create two wrapper txs + for _ in 0..2 { + let (_tx, processed_tx) = mk_wrapper_tx(&shell, &txs_key); + txs.push(processed_tx); + } + let req = FinalizeBlock { + txs, proposer_address: proposer_address.clone(), votes: votes.clone(), ..Default::default() }; + // merkle tree root before finalize_block + let root_pre = shell.shell.wl_storage.storage.block.tree.root(); + let _events = shell.finalize_block(req).unwrap(); + + // the merkle tree root should not change after finalize_block + let root_post = shell.shell.wl_storage.storage.block.tree.root(); + assert_eq!(root_pre.0, root_post.0); let new_state = store_block_state(&shell); // The new state must be unchanged itertools::assert_equal( @@ -2227,6 +2231,8 @@ mod test_finalize_block { }, }; shell.enqueue_tx(wrapper_tx); + // merkle tree root before finalize_block + let root_pre = shell.shell.wl_storage.storage.block.tree.root(); let _event = &shell .finalize_block(FinalizeBlock { @@ -2235,6 +2241,10 @@ mod test_finalize_block { }) .expect("Test failed")[0]; + // the merkle tree root should not change after finalize_block + let root_post = shell.shell.wl_storage.storage.block.tree.root(); + assert_eq!(root_pre.0, root_post.0); + // FIXME: uncomment when proper gas metering is in place // // Check inner tx hash has been removed from storage // assert_eq!(event.event_type.to_string(), String::from("applied")); @@ -2407,8 +2417,11 @@ mod test_finalize_block { ); // Advance to the processing epoch - let votes = get_default_true_votes(&shell.wl_storage, Epoch::default()); loop { + let votes = get_default_true_votes( + &shell.wl_storage, + shell.wl_storage.storage.block.epoch, + ); next_block_for_inflation( &mut shell, pkh1.clone(), @@ -2947,10 +2960,14 @@ mod test_finalize_block { total_voting_power: Default::default(), }, ]; + let votes = get_default_true_votes( + &shell.wl_storage, + shell.wl_storage.storage.block.epoch, + ); next_block_for_inflation( &mut shell, pkh1.clone(), - votes.clone(), + votes, Some(misbehaviors), ); assert_eq!(current_epoch.0, 7_u64); diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index 9e249ab705..41bc88c03b 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -6,9 +6,7 @@ use std::hash::Hash; use namada::core::ledger::testnet_pow; use namada::ledger::eth_bridge::EthBridgeStatus; use namada::ledger::parameters::{self, Parameters}; -use namada::ledger::pos::{ - into_tm_voting_power, staking_token_address, PosParams, -}; +use namada::ledger::pos::{staking_token_address, PosParams}; use namada::ledger::storage::traits::StorageHasher; use namada::ledger::storage::{DBIter, DB}; use namada::ledger::storage_api::token::{ @@ -23,8 +21,6 @@ use namada::types::time::{DateTimeUtc, TimeZone, Utc}; use namada::types::token; use super::*; -use crate::facade::tendermint_proto::abci; -use crate::facade::tendermint_proto::crypto::PublicKey as TendermintPublicKey; use crate::facade::tendermint_proto::google::protobuf; use crate::facade::tower_abci::{request, response}; use crate::wasm_loader; @@ -238,10 +234,7 @@ where self.initialize_implicit_accounts(genesis.implicit_accounts); // Initialize genesis token accounts - self.initialize_token_accounts( - genesis.token_accounts, - &implicit_vp_code_path, - ); + self.initialize_token_accounts(genesis.token_accounts); // Initialize genesis validator accounts let staking_token = staking_token_address(&self.wl_storage); @@ -251,18 +244,18 @@ where &implicit_vp_code_path, ); // set the initial validators set - Ok(self.set_initial_validators( + self.set_initial_validators( &staking_token, genesis.validators, &genesis.pos_params, - )) + ) } /// Initialize genesis established accounts fn initialize_established_accounts( &mut self, faucet_pow_difficulty: Option, - faucet_withdrawal_limit: Option, + faucet_withdrawal_limit: Option, accounts: Vec, implicit_vp_code_path: &str, ) -> Result<()> { @@ -315,8 +308,11 @@ where if vp_code_path == "vp_testnet_faucet.wasm" { let difficulty = faucet_pow_difficulty.unwrap_or_default(); // withdrawal limit defaults to 1000 NAM when not set - let withdrawal_limit = faucet_withdrawal_limit - .unwrap_or_else(|| token::Amount::native_whole(1_000)); + let withdrawal_limit = + faucet_withdrawal_limit.unwrap_or_else(|| { + token::Amount::native_whole(1_000).into() + }); + testnet_pow::init_faucet_storage( &mut self.wl_storage, &address, @@ -351,52 +347,16 @@ where fn initialize_token_accounts( &mut self, accounts: Vec, - implicit_vp_code_path: &str, ) { // Initialize genesis token accounts for genesis::TokenAccount { address, denom, - vp_code_path, - vp_sha256, balances, } in accounts { // associate a token with its denomination. - write_denom( - &mut self.wl_storage, - &address, - // TODO: Should we support multi-tokens at genesis? - None, - denom, - ) - .unwrap(); - let vp_code_hash = - read_wasm_hash(&self.wl_storage, vp_code_path.clone()) - .unwrap() - .ok_or(Error::LoadingWasm(format!( - "Unknown vp code path: {}", - implicit_vp_code_path - ))) - .expect("Reading wasms should succeed"); - - // In dev, we don't check the hash - #[cfg(feature = "dev")] - let _ = vp_sha256; - #[cfg(not(feature = "dev"))] - { - assert_eq!( - vp_code_hash.0.as_slice(), - &vp_sha256, - "Invalid token account's VP sha256 hash for {}", - vp_code_path - ); - } - - self.wl_storage - .write_bytes(&Key::validity_predicate(&address), vp_code_hash) - .unwrap(); - + write_denom(&mut self.wl_storage, &address, denom).unwrap(); for (owner, amount) in balances { credit_tokens(&mut self.wl_storage, &address, &owner, amount) .unwrap(); @@ -476,7 +436,7 @@ where staking_token: &Address, validators: Vec, pos_params: &PosParams, - ) -> response::InitChain { + ) -> Result { let mut response = response::InitChain::default(); // PoS system depends on epoch being initialized. Write the total // genesis staking token balance to storage after @@ -485,20 +445,15 @@ where pos::init_genesis_storage( &mut self.wl_storage, pos_params, - validators - .clone() - .into_iter() - .map(|validator| validator.pos_data), + validators.into_iter().map(|validator| validator.pos_data), current_epoch, ); - let total_nam = - read_total_supply(&self.wl_storage, staking_token).unwrap(); + let total_nam = read_total_supply(&self.wl_storage, staking_token)?; // At this stage in the chain genesis, the PoS address balance is the // same as the number of staked tokens let total_staked_nam = - read_balance(&self.wl_storage, staking_token, &address::POS) - .unwrap(); + read_balance(&self.wl_storage, staking_token, &address::POS)?; tracing::info!( "Genesis total native tokens: {}.", @@ -519,21 +474,12 @@ where ibc::init_genesis_storage(&mut self.wl_storage); // Set the initial validator set - for validator in validators { - let mut abci_validator = abci::ValidatorUpdate::default(); - let consensus_key: common::PublicKey = - validator.pos_data.consensus_key.clone(); - let pub_key = TendermintPublicKey { - sum: Some(key_to_tendermint(&consensus_key).unwrap()), - }; - abci_validator.pub_key = Some(pub_key); - abci_validator.power = into_tm_voting_power( - pos_params.tm_votes_per_token, - validator.pos_data.tokens, - ); - response.validators.push(abci_validator); - } - response + response.validators = self + .get_abci_validator_updates(true) + .expect("Must be able to set genesis validator set"); + debug_assert!(!response.validators.is_empty()); + + Ok(response) } } diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index 5fd505e344..7c3fc3d8bf 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -13,6 +13,9 @@ mod prepare_proposal; mod process_proposal; pub(super) mod queries; mod stats; +#[cfg(any(test, feature = "testing"))] +#[allow(dead_code)] +pub mod testing; mod vote_extensions; use std::collections::{BTreeSet, HashSet}; @@ -28,6 +31,7 @@ use namada::ledger::eth_bridge::{EthBridgeQueries, EthereumBridgeConfig}; use namada::ledger::events::log::EventLog; use namada::ledger::events::Event; use namada::ledger::gas::BlockGasMeter; +use namada::ledger::pos::into_tm_voting_power; use namada::ledger::pos::namada_proof_of_stake::types::{ ConsensusValidator, ValidatorSetUpdate, }; @@ -137,7 +141,7 @@ impl From for TxResult { /// The different error codes that the ledger may /// send back to a client indicating the status /// of their submitted tx -#[derive(Debug, Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)] +#[derive(Debug, Copy, Clone, FromPrimitive, ToPrimitive, PartialEq, Eq)] pub enum ErrorCodes { Ok = 0, InvalidDecryptedChainId = 1, @@ -1342,6 +1346,59 @@ where } false } + + fn get_abci_validator_updates( + &self, + is_genesis: bool, + ) -> storage_api::Result> + { + use namada::ledger::pos::namada_proof_of_stake; + + use crate::facade::tendermint_proto::crypto::PublicKey as TendermintPublicKey; + + let (current_epoch, _gas) = self.wl_storage.storage.get_current_epoch(); + let pos_params = + namada_proof_of_stake::read_pos_params(&self.wl_storage) + .expect("Could not find the PoS parameters"); + + let validator_set_update_fn = if is_genesis { + namada_proof_of_stake::genesis_validator_set_tendermint + } else { + namada_proof_of_stake::validator_set_update_tendermint + }; + + validator_set_update_fn( + &self.wl_storage, + &pos_params, + current_epoch, + |update| { + let (consensus_key, power) = match update { + ValidatorSetUpdate::Consensus(ConsensusValidator { + consensus_key, + bonded_stake, + }) => { + let power: i64 = into_tm_voting_power( + pos_params.tm_votes_per_token, + bonded_stake, + ); + (consensus_key, power) + } + ValidatorSetUpdate::Deactivated(consensus_key) => { + // Any validators that have been dropped from the + // consensus set must have voting power set to 0 to + // remove them from the conensus set + let power = 0_i64; + (consensus_key, power) + } + }; + let pub_key = TendermintPublicKey { + sum: Some(key_to_tendermint(&consensus_key).unwrap()), + }; + let pub_key = Some(pub_key); + ValidatorUpdate { pub_key, power } + }, + ) + } } impl<'a, D, H> From<&'a mut Shell> diff --git a/apps/src/lib/node/ledger/shell/prepare_proposal.rs b/apps/src/lib/node/ledger/shell/prepare_proposal.rs index 3fc5cd8983..e1e94be61e 100644 --- a/apps/src/lib/node/ledger/shell/prepare_proposal.rs +++ b/apps/src/lib/node/ledger/shell/prepare_proposal.rs @@ -16,8 +16,6 @@ use namada::types::transaction::{ use namada::types::vote_extensions::VoteExtensionDigest; use super::super::*; -#[allow(unused_imports)] -use super::block_space_alloc; use super::block_space_alloc::states::{ BuildingDecryptedTxBatch, BuildingProtocolTxBatch, EncryptedTxBatchAllocator, NextState, TryAlloc, @@ -41,8 +39,8 @@ where { /// Begin a new block. /// - /// Block construction is documented in [`block_space_alloc`] - /// and [`block_space_alloc::states`]. + /// Block construction is documented in `block_space_alloc` + /// and `block_space_alloc::states` (private modules). /// /// INVARIANT: Any changes applied in this method must be reverted if /// the proposal is rejected (unless we can simply overwrite diff --git a/apps/src/lib/node/ledger/shell/process_proposal.rs b/apps/src/lib/node/ledger/shell/process_proposal.rs index 9b9488eacb..c1f38769f5 100644 --- a/apps/src/lib/node/ledger/shell/process_proposal.rs +++ b/apps/src/lib/node/ledger/shell/process_proposal.rs @@ -934,7 +934,7 @@ where /// Checks if it is not possible to include encrypted txs at the current /// block height. - fn encrypted_txs_not_allowed(&self) -> bool { + pub(super) fn encrypted_txs_not_allowed(&self) -> bool { let pos_queries = self.wl_storage.pos_queries(); let is_2nd_height_off = pos_queries.is_deciding_offset_within_epoch(1); let is_3rd_height_off = pos_queries.is_deciding_offset_within_epoch(2); diff --git a/apps/src/lib/node/ledger/shell/testing/client.rs b/apps/src/lib/node/ledger/shell/testing/client.rs new file mode 100644 index 0000000000..164e4a03ea --- /dev/null +++ b/apps/src/lib/node/ledger/shell/testing/client.rs @@ -0,0 +1,82 @@ +use std::ops::ControlFlow; + +use clap::Command as App; +use eyre::Report; +use namada::types::control_flow::Halt; +use tendermint_config::net::Address as TendermintAddress; + +use super::node::MockNode; +use crate::cli::api::{CliApi, CliClient}; +use crate::cli::args::Global; +use crate::cli::{args, cmds, Cmd, Context, NamadaClient}; +use crate::node::ledger::shell::testing::utils::Bin; + +pub fn run( + node: &MockNode, + who: Bin, + mut args: Vec<&str>, +) -> Result<(), Report> { + let global = { + let locked = node.shell.lock().unwrap(); + Global { + chain_id: Some(locked.chain_id.clone()), + base_dir: locked.base_dir.clone(), + wasm_dir: Some(locked.wasm_dir.clone()), + } + }; + let ctx = Context::new(global.clone())?; + + let rt = tokio::runtime::Runtime::new().unwrap(); + match who { + Bin::Node => { + unreachable!("Node commands aren't supported by integration tests") + } + Bin::Client => { + args.insert(0, "client"); + let app = App::new("test"); + let app = cmds::NamadaClient::add_sub(args::Global::def(app)); + let matches = app.get_matches_from(args.clone()); + let cmd = match cmds::NamadaClient::parse(&matches) + .expect("Could not parse client command") + { + cmds::NamadaClient::WithContext(sub_cmd) => { + NamadaClient::WithContext(Box::new((sub_cmd, ctx))) + } + cmds::NamadaClient::WithoutContext(sub_cmd) => { + NamadaClient::WithoutContext(sub_cmd, global) + } + }; + rt.block_on(CliApi::<()>::handle_client_command(Some(node), cmd)) + } + Bin::Wallet => { + args.insert(0, "wallet"); + let app = App::new("test"); + let app = cmds::NamadaWallet::add_sub(args::Global::def(app)); + let matches = app.get_matches_from(args.clone()); + + let cmd = cmds::NamadaWallet::parse(&matches) + .expect("Could not parse wallet command"); + CliApi::<()>::handle_wallet_command(cmd, ctx) + } + Bin::Relayer => { + args.insert(0, "relayer"); + let app = App::new("test"); + let app = cmds::NamadaRelayer::add_sub(args::Global::def(app)); + let matches = app.get_matches_from(args.clone()); + let cmd = cmds::NamadaRelayer::parse(&matches) + .expect("Could not parse wallet command"); + rt.block_on(CliApi::<()>::handle_relayer_command(Some(node), cmd)) + } + } +} + +#[async_trait::async_trait(?Send)] +impl<'a> CliClient for &'a MockNode { + fn from_tendermint_address(_: &mut TendermintAddress) -> Self { + unreachable!("MockNode should always be instantiated at test start.") + } + + async fn wait_until_node_is_synced(&self) -> Halt<()> { + ControlFlow::Continue(()) + } +} diff --git a/apps/src/lib/node/ledger/shell/testing/mod.rs b/apps/src/lib/node/ledger/shell/testing/mod.rs new file mode 100644 index 0000000000..fff1df00ba --- /dev/null +++ b/apps/src/lib/node/ledger/shell/testing/mod.rs @@ -0,0 +1,3 @@ +pub mod client; +pub mod node; +pub mod utils; diff --git a/apps/src/lib/node/ledger/shell/testing/node.rs b/apps/src/lib/node/ledger/shell/testing/node.rs new file mode 100644 index 0000000000..a66d33cb78 --- /dev/null +++ b/apps/src/lib/node/ledger/shell/testing/node.rs @@ -0,0 +1,543 @@ +use std::mem::ManuallyDrop; +use std::path::PathBuf; +use std::str::FromStr; +use std::sync::{Arc, Mutex}; + +use color_eyre::eyre::{Report, Result}; +use lazy_static::lazy_static; +use namada::ledger::events::log::dumb_queries; +use namada::ledger::queries::{ + Client, EncodedResponseQuery, RequestCtx, RequestQuery, Router, RPC, +}; +use namada::ledger::storage::{ + LastBlock, Sha256Hasher, EPOCH_SWITCH_BLOCKS_DELAY, +}; +use namada::tendermint_rpc::endpoint::abci_info; +use namada::tendermint_rpc::SimpleRequest; +use namada::types::hash::Hash; +use namada::types::storage::{BlockHash, BlockHeight, Epoch, Header}; +use namada::types::time::DateTimeUtc; +use num_traits::cast::FromPrimitive; +use regex::Regex; +use tokio::sync::mpsc::UnboundedReceiver; + +use crate::facade::tendermint_proto::abci::response_process_proposal::ProposalStatus; +use crate::facade::tendermint_proto::abci::RequestProcessProposal; +use crate::facade::tendermint_rpc::endpoint::abci_info::AbciInfo; +use crate::facade::tendermint_rpc::error::Error as RpcError; +use crate::facade::{tendermint, tendermint_rpc}; +use crate::node::ledger::shell::testing::utils::TestDir; +use crate::node::ledger::shell::{ErrorCodes, Shell}; +use crate::node::ledger::shims::abcipp_shim_types::shim::request::{ + FinalizeBlock, ProcessedTx, +}; +use crate::node::ledger::shims::abcipp_shim_types::shim::response::TxResult; +use crate::node::ledger::storage; + +/// Status of tx +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum NodeResults { + /// Success + Ok, + /// Rejected by Process Proposal + Rejected(TxResult), + /// Failure in application in Finalize Block + Failed(ErrorCodes), +} + +pub struct MockNode { + pub shell: Arc>>, + pub test_dir: ManuallyDrop, + pub keep_temp: bool, + pub _broadcast_recv: UnboundedReceiver>, + pub results: Arc>>, +} + +impl Drop for MockNode { + fn drop(&mut self) { + unsafe { + if !self.keep_temp { + ManuallyDrop::take(&mut self.test_dir).clean_up() + } else { + println!( + "Keeping tempfile at {}", + self.test_dir.path().to_string_lossy() + ); + ManuallyDrop::drop(&mut self.test_dir) + } + } + } +} + +impl MockNode { + pub fn genesis_dir(&self) -> PathBuf { + self.test_dir + .path() + .join(self.shell.lock().unwrap().chain_id.to_string()) + } + + pub fn genesis_path(&self) -> PathBuf { + self.test_dir + .path() + .join(format!("{}.toml", self.shell.lock().unwrap().chain_id)) + } + + pub fn wasm_dir(&self) -> PathBuf { + self.genesis_path().join("wasm") + } + + pub fn wallet_path(&self) -> PathBuf { + self.genesis_dir().join("wallet.toml") + } + + pub fn current_epoch(&self) -> Epoch { + self.shell.lock().unwrap().wl_storage.storage.last_epoch + } + + pub fn next_epoch(&mut self) -> Epoch { + { + let mut locked = self.shell.lock().unwrap(); + + let next_epoch_height = + locked.wl_storage.storage.get_last_block_height() + 1; + locked.wl_storage.storage.next_epoch_min_start_height = + next_epoch_height; + locked.wl_storage.storage.next_epoch_min_start_time = + DateTimeUtc::now(); + let next_epoch_min_start_height = + locked.wl_storage.storage.next_epoch_min_start_height; + if let Some(LastBlock { height, .. }) = + locked.wl_storage.storage.last_block.as_mut() + { + *height = next_epoch_min_start_height; + } + } + self.finalize_and_commit(); + + for _ in 0..EPOCH_SWITCH_BLOCKS_DELAY { + self.finalize_and_commit(); + } + self.shell + .lock() + .unwrap() + .wl_storage + .storage + .get_current_epoch() + .0 + } + + /// Simultaneously call the `FinalizeBlock` and + /// `Commit` handlers. + pub fn finalize_and_commit(&self) { + let mut req = FinalizeBlock { + hash: BlockHash([0u8; 32]), + header: Header { + hash: Hash([0; 32]), + time: DateTimeUtc::now(), + next_validators_hash: Hash([0; 32]), + }, + byzantine_validators: vec![], + txs: vec![], + proposer_address: vec![], + votes: vec![], + }; + req.header.time = DateTimeUtc::now(); + let mut locked = self.shell.lock().unwrap(); + locked.finalize_block(req).expect("Test failed"); + locked.commit(); + } + + /// Advance to a block height that allows + /// txs + fn advance_to_allowed_block(&self) { + loop { + let not_allowed = + { self.shell.lock().unwrap().encrypted_txs_not_allowed() }; + if not_allowed { + self.finalize_and_commit(); + } else { + break; + } + } + } + + /// Send a tx through Process Proposal and Finalize Block + /// and register the results. + fn submit_tx(&self, tx_bytes: Vec) { + let req = RequestProcessProposal { + txs: vec![tx_bytes.clone()], + ..Default::default() + }; + // The block space allocator disallows txs in certain blocks. + // Advance to block height that allows txs. + self.advance_to_allowed_block(); + let mut locked = self.shell.lock().unwrap(); + let mut result = locked.process_proposal(req); + let mut errors: Vec<_> = result + .tx_results + .iter() + .map(|e| { + if e.code == 0 { + NodeResults::Ok + } else { + NodeResults::Rejected(e.clone()) + } + }) + .collect(); + if result.status != i32::from(ProposalStatus::Accept) { + self.results.lock().unwrap().append(&mut errors); + return; + } + + // process proposal succeeded, now run finalize block + let req = FinalizeBlock { + hash: BlockHash([0u8; 32]), + header: Header { + hash: Hash([0; 32]), + time: DateTimeUtc::now(), + next_validators_hash: Hash([0; 32]), + }, + byzantine_validators: vec![], + txs: vec![ProcessedTx { + tx: tx_bytes, + result: result.tx_results.remove(0), + }], + proposer_address: vec![], + votes: vec![], + }; + + // process the results + let resp = locked.finalize_block(req).unwrap(); + let mut error_codes = resp + .events + .into_iter() + .map(|e| { + let code = ErrorCodes::from_u32( + e.attributes + .get("code") + .map(|e| u32::from_str(e).unwrap()) + .unwrap_or_default(), + ) + .unwrap(); + if code == ErrorCodes::Ok { + NodeResults::Ok + } else { + NodeResults::Failed(code) + } + }) + .collect::>(); + self.results.lock().unwrap().append(&mut error_codes); + locked.commit(); + } + + /// Check that applying a tx succeeded. + pub fn success(&self) -> bool { + self.results + .lock() + .unwrap() + .iter() + .all(|r| *r == NodeResults::Ok) + } + + pub fn clear_results(&self) { + self.results.lock().unwrap().clear(); + } + + pub fn assert_success(&self) { + if !self.success() { + panic!( + "Assert failed: The node did not execute \ + successfully:\nErrors:\n {:?}", + self.results.lock().unwrap() + ); + } else { + self.clear_results(); + } + } +} + +#[async_trait::async_trait(?Send)] +impl<'a> Client for &'a MockNode { + type Error = Report; + + async fn request( + &self, + path: String, + data: Option>, + height: Option, + prove: bool, + ) -> std::result::Result { + let rpc = RPC; + let data = data.unwrap_or_default(); + let latest_height = { + self.shell + .lock() + .unwrap() + .wl_storage + .storage + .last_block + .as_ref() + .map(|b| b.height) + .unwrap_or_default() + }; + let height = height.unwrap_or(latest_height); + // Handle a path by invoking the `RPC.handle` directly with the + // borrowed storage + let request = RequestQuery { + data, + path, + height, + prove, + }; + let borrowed = self.shell.lock().unwrap(); + let ctx = RequestCtx { + wl_storage: &borrowed.wl_storage, + event_log: borrowed.event_log(), + vp_wasm_cache: borrowed.vp_wasm_cache.read_only(), + tx_wasm_cache: borrowed.tx_wasm_cache.read_only(), + storage_read_past_height_limit: None, + }; + rpc.handle(ctx, &request).map_err(Report::new) + } + + async fn perform( + &self, + _request: R, + ) -> std::result::Result + where + R: SimpleRequest, + { + unreachable!() + } + + /// `/abci_info`: get information about the ABCI application. + async fn abci_info(&self) -> Result { + let locked = self.shell.lock().unwrap(); + Ok(AbciInfo { + data: "Namada".to_string(), + version: "test".to_string(), + app_version: 0, + last_block_height: locked + .wl_storage + .storage + .last_block + .as_ref() + .map(|b| b.height.0 as u32) + .unwrap_or_default() + .into(), + last_block_app_hash: locked + .wl_storage + .storage + .last_block + .as_ref() + .map(|b| b.hash.0) + .unwrap_or_default() + .to_vec(), + }) + } + + /// `/broadcast_tx_sync`: broadcast a transaction, returning the response + /// from `CheckTx`. + async fn broadcast_tx_sync( + &self, + tx: namada::tendermint::abci::Transaction, + ) -> Result + { + let resp = tendermint_rpc::endpoint::broadcast::tx_sync::Response { + code: Default::default(), + data: Default::default(), + log: Default::default(), + hash: tendermint::abci::transaction::Hash::new([0; 32]), + }; + let tx_bytes: Vec = tx.into(); + self.submit_tx(tx_bytes); + if !self.success() { + return Ok(resp); + } else { + self.clear_results(); + } + let tx_bytes = { + let locked = self.shell.lock().unwrap(); + locked.prepare_proposal(Default::default()).txs.remove(0) + }; + self.submit_tx(tx_bytes); + Ok(resp) + } + + /// `/block_search`: search for blocks by BeginBlock and EndBlock events. + async fn block_search( + &self, + query: namada::tendermint_rpc::query::Query, + _page: u32, + _per_page: u8, + _order: namada::tendermint_rpc::Order, + ) -> Result + { + let matcher = parse_tm_query(query); + let borrowed = self.shell.lock().unwrap(); + // we store an index into the event log as a block + // height in the response of the query... VERY NAISSSE + let matching_events = borrowed.event_log().iter().enumerate().flat_map( + |(index, event)| { + if matcher.matches(event) { + Some(EncodedEvent(index as u64)) + } else { + None + } + }, + ); + let blocks = matching_events + .map(|encoded_event| namada::tendermint_rpc::endpoint::block::Response { + block_id: Default::default(), + block: namada::tendermint_proto::types::Block { + header: Some(namada::tendermint_proto::types::Header { + version: Some(namada::tendermint_proto::version::Consensus { + block: 0, + app: 0, + }), + chain_id: "Namada".try_into().unwrap(), + height: encoded_event.0 as i64, + time: None, + last_block_id: None, + last_commit_hash: vec![], + data_hash: vec![], + validators_hash: vec![], + next_validators_hash: vec![], + consensus_hash: vec![], + app_hash: vec![], + last_results_hash: vec![], + evidence_hash: vec![], + proposer_address: vec![] + + }), + data: Default::default(), + evidence: Default::default(), + last_commit: Some(namada::tendermint_proto::types::Commit { + height: encoded_event.0 as i64, + round: 0, + block_id: Some(namada::tendermint_proto::types::BlockId { + hash: vec![0u8; 32], + part_set_header: Some(namada::tendermint_proto::types::PartSetHeader { + total: 1, + hash: vec![1; 32], + }), + }), + signatures: vec![], + }), + }.try_into().unwrap(), + }) + .collect::>(); + + Ok(namada::tendermint_rpc::endpoint::block_search::Response { + total_count: blocks.len() as u32, + blocks, + }) + } + + /// `/block_results`: get ABCI results for a block at a particular height. + async fn block_results( + &self, + height: H, + ) -> Result + where + H: Into + Send, + { + let height = height.into(); + let encoded_event = EncodedEvent(height.value()); + let locked = self.shell.lock().unwrap(); + let events: Vec<_> = locked + .event_log() + .iter() + .enumerate() + .flat_map(|(index, event)| { + if index == encoded_event.log_index() { + Some(event) + } else { + None + } + }) + .map(|event| namada::tendermint::abci::responses::Event { + type_str: event.event_type.to_string(), + attributes: event + .attributes + .iter() + .map(|(k, v)| namada::tendermint::abci::tag::Tag { + key: k.parse().unwrap(), + value: v.parse().unwrap(), + }) + .collect(), + }) + .collect(); + let has_events = !events.is_empty(); + + Ok(tendermint_rpc::endpoint::block_results::Response { + height, + txs_results: None, + begin_block_events: None, + end_block_events: has_events.then_some(events), + validator_updates: vec![], + consensus_param_updates: None, + }) + } + + /// `/tx_search`: search for transactions with their results. + async fn tx_search( + &self, + _query: namada::tendermint_rpc::query::Query, + _prove: bool, + _page: u32, + _per_page: u8, + _order: namada::tendermint_rpc::Order, + ) -> Result { + // In the past, some cli commands for masp called this. However, these + // commands are not currently supported, so we do not need to fill + // in this function for now. + unreachable!() + } + + /// `/health`: get node health. + /// + /// Returns empty result (200 OK) on success, no response in case of an + /// error. + async fn health(&self) -> Result<(), RpcError> { + Ok(()) + } +} + +/// Parse a Tendermint query. +fn parse_tm_query( + query: namada::tendermint_rpc::query::Query, +) -> dumb_queries::QueryMatcher { + const QUERY_PARSING_REGEX_STR: &str = + r"^tm\.event='NewBlock' AND (accepted|applied)\.hash='([^']+)'$"; + + lazy_static! { + /// Compiled regular expression used to parse Tendermint queries. + static ref QUERY_PARSING_REGEX: Regex = Regex::new(QUERY_PARSING_REGEX_STR).unwrap(); + } + + let query = query.to_string(); + let captures = QUERY_PARSING_REGEX.captures(&query).unwrap(); + + match captures.get(0).unwrap().as_str() { + "accepted" => dumb_queries::QueryMatcher::accepted( + captures.get(1).unwrap().as_str().try_into().unwrap(), + ), + "applied" => dumb_queries::QueryMatcher::applied( + captures.get(1).unwrap().as_str().try_into().unwrap(), + ), + _ => unreachable!("We only query accepted or applied txs"), + } +} + +/// A Namada event log index and event type encoded as +/// a Tendermint block height. +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +struct EncodedEvent(u64); + +impl EncodedEvent { + /// Get the encoded event log index. + const fn log_index(self) -> usize { + self.0 as usize + } +} diff --git a/apps/src/lib/node/ledger/shell/testing/utils.rs b/apps/src/lib/node/ledger/shell/testing/utils.rs new file mode 100644 index 0000000000..e66ead21e7 --- /dev/null +++ b/apps/src/lib/node/ledger/shell/testing/utils.rs @@ -0,0 +1,48 @@ +use std::path::{Path, PathBuf}; + +use tempfile::tempdir; + +/// Namada binaries +#[derive(Debug)] +#[allow(dead_code)] +pub enum Bin { + Node, + Client, + Wallet, + Relayer, +} + +/// A temporary directory for testing +#[derive(Debug)] +pub struct TestDir(PathBuf); + +impl TestDir { + /// Creat a new temp directory. This will have to be manually + /// cleaned up. + pub fn new() -> Self { + let temp = tempdir().unwrap(); + Self(temp.into_path()) + } + + /// Get the path of the directory + pub fn path(&self) -> &Path { + &self.0 + } + + /// Manually remove the test directory from the + /// file system. + pub fn clean_up(self) { + if let Err(e) = std::fs::remove_dir_all(&self.0) { + println!( + "Failed to clean up test dir at {}: {e:?}", + self.0.to_string_lossy() + ); + } + } +} + +impl Default for TestDir { + fn default() -> Self { + Self::new() + } +} diff --git a/apps/src/lib/node/ledger/shell/vote_extensions/bridge_pool_vext.rs b/apps/src/lib/node/ledger/shell/vote_extensions/bridge_pool_vext.rs index 3de6e03c0c..edd83cdb3a 100644 --- a/apps/src/lib/node/ledger/shell/vote_extensions/bridge_pool_vext.rs +++ b/apps/src/lib/node/ledger/shell/vote_extensions/bridge_pool_vext.rs @@ -176,7 +176,7 @@ where /// Takes an iterator over Bridge pool root vote extension instances, /// and returns another iterator. The latter yields /// valid Brige pool root vote extensions, or the reason why these - /// are invalid, in the form of a [`VoteExtensionError`]. + /// are invalid, in the form of a `VoteExtensionError`. #[inline] pub fn validate_bp_roots_vext_list<'iter>( &'iter self, diff --git a/apps/src/lib/node/ledger/shell/vote_extensions/eth_events.rs b/apps/src/lib/node/ledger/shell/vote_extensions/eth_events.rs index c836ecbbc5..891a403f90 100644 --- a/apps/src/lib/node/ledger/shell/vote_extensions/eth_events.rs +++ b/apps/src/lib/node/ledger/shell/vote_extensions/eth_events.rs @@ -308,7 +308,7 @@ where /// Takes an iterator over Ethereum events vote extension instances, /// and returns another iterator. The latter yields /// valid Ethereum events vote extensions, or the reason why these - /// are invalid, in the form of a [`VoteExtensionError`]. + /// are invalid, in the form of a `VoteExtensionError`. #[inline] pub fn validate_eth_events_vext_list<'iter>( &'iter self, diff --git a/apps/src/lib/node/ledger/shell/vote_extensions/val_set_update.rs b/apps/src/lib/node/ledger/shell/vote_extensions/val_set_update.rs index c491c5d6d2..aa8183d9aa 100644 --- a/apps/src/lib/node/ledger/shell/vote_extensions/val_set_update.rs +++ b/apps/src/lib/node/ledger/shell/vote_extensions/val_set_update.rs @@ -161,7 +161,7 @@ where /// Takes an iterator over validator set update vote extension instances, /// and returns another iterator. The latter yields /// valid validator set update vote extensions, or the reason why these - /// are invalid, in the form of a [`VoteExtensionError`]. + /// are invalid, in the form of a `VoteExtensionError`. #[inline] pub fn validate_valset_upd_vext_list( &self, diff --git a/apps/src/lib/node/ledger/shims/abcipp_shim_types.rs b/apps/src/lib/node/ledger/shims/abcipp_shim_types.rs index 331aaeeeba..ca7be9a9e5 100644 --- a/apps/src/lib/node/ledger/shims/abcipp_shim_types.rs +++ b/apps/src/lib/node/ledger/shims/abcipp_shim_types.rs @@ -306,7 +306,7 @@ pub mod shim { #[derive(Debug, Default)] pub struct VerifyHeader; - #[derive(Debug, Default, Clone)] + #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct TxResult { pub code: u32, pub info: String, diff --git a/apps/src/lib/node/ledger/storage/rocksdb.rs b/apps/src/lib/node/ledger/storage/rocksdb.rs index 9b6077f50a..f679ca9232 100644 --- a/apps/src/lib/node/ledger/storage/rocksdb.rs +++ b/apps/src/lib/node/ledger/storage/rocksdb.rs @@ -176,14 +176,6 @@ impl RocksDB { .ok_or(Error::DBError("No {cf_name} column family".to_string())) } - fn flush(&self, wait: bool) -> Result<()> { - let mut flush_opts = FlushOptions::default(); - flush_opts.set_wait(wait); - self.0 - .flush_opt(&flush_opts) - .map_err(|e| Error::DBError(e.into_string())) - } - /// Persist the diff of an account subspace key-val under the height where /// it was changed. fn write_subspace_diff( @@ -512,7 +504,7 @@ impl DB for RocksDB { .map_err(|e| Error::DBError(e.into_string())) } - fn read_last_block(&mut self) -> Result> { + fn read_last_block(&self) -> Result> { // Block height let state_cf = self.get_column_family(STATE_CF)?; let height: BlockHeight = match self @@ -730,8 +722,8 @@ impl DB for RocksDB { } } - fn write_block( - &mut self, + fn add_block_to_batch( + &self, state: BlockStateWrite, batch: &mut Self::WriteBatch, is_full_commit: bool, @@ -1543,7 +1535,7 @@ mod test { ) .unwrap(); - write_block(&mut db, &mut batch, BlockHeight::default()).unwrap(); + add_block_to_batch(&db, &mut batch, BlockHeight::default()).unwrap(); db.exec_batch(batch.0).unwrap(); let _state = db @@ -1734,7 +1726,7 @@ mod test { ) .unwrap(); - write_block(&mut db, &mut batch, height_0).unwrap(); + add_block_to_batch(&db, &mut batch, height_0).unwrap(); db.exec_batch(batch.0).unwrap(); // Write second block @@ -1754,7 +1746,7 @@ mod test { db.batch_delete_subspace_val(&mut batch, height_1, &delete_key) .unwrap(); - write_block(&mut db, &mut batch, height_1).unwrap(); + add_block_to_batch(&db, &mut batch, height_1).unwrap(); db.exec_batch(batch.0).unwrap(); // Check that the values are as expected from second block @@ -1778,8 +1770,8 @@ mod test { } /// A test helper to write a block - fn write_block( - db: &mut RocksDB, + fn add_block_to_batch( + db: &RocksDB, batch: &mut RocksDBWriteBatch, height: BlockHeight, ) -> Result<()> { @@ -1814,6 +1806,6 @@ mod test { eth_events_queue: ð_events_queue, }; - db.write_block(block, batch, true) + db.add_block_to_batch(block, batch, true) } } diff --git a/apps/src/lib/wallet/cli_utils.rs b/apps/src/lib/wallet/cli_utils.rs new file mode 100644 index 0000000000..ffaf080841 --- /dev/null +++ b/apps/src/lib/wallet/cli_utils.rs @@ -0,0 +1,515 @@ +use std::fs::File; +use std::io::{self, Write}; + +use borsh::BorshSerialize; +use itertools::sorted; +use masp_primitives::zip32::ExtendedFullViewingKey; +use namada::ledger::masp::find_valid_diversifier; +use namada::ledger::wallet::{DecryptionError, FindKeyError}; +use namada::types::key::{PublicKeyHash, RefTo}; +use namada::types::masp::{MaspValue, PaymentAddress}; +use rand_core::OsRng; + +use crate::cli; +use crate::cli::{args, Context}; +use crate::wallet::{read_and_confirm_encryption_password, CliWalletUtils}; + +/// Find shielded address or key +pub fn address_key_find( + ctx: Context, + args::AddrKeyFind { + alias, + unsafe_show_secret, + }: args::AddrKeyFind, +) { + let mut wallet = ctx.wallet; + let alias = alias.to_lowercase(); + if let Ok(viewing_key) = wallet.find_viewing_key(&alias) { + // Check if alias is a viewing key + println!("Viewing key: {}", viewing_key); + if unsafe_show_secret { + // Check if alias is also a spending key + match wallet.find_spending_key(&alias, None) { + Ok(spending_key) => println!("Spending key: {}", spending_key), + Err(FindKeyError::KeyNotFound) => {} + Err(err) => eprintln!("{}", err), + } + } + } else if let Some(payment_addr) = wallet.find_payment_addr(&alias) { + // Failing that, check if alias is a payment address + println!("Payment address: {}", payment_addr); + } else { + // Otherwise alias cannot be referring to any shielded value + println!( + "No shielded address or key with alias {} found. Use the commands \ + `masp list-addrs` and `masp list-keys` to see all the known \ + addresses and keys.", + alias.to_lowercase() + ); + } +} + +/// List spending keys. +pub fn spending_keys_list( + ctx: Context, + args::MaspKeysList { + decrypt, + unsafe_show_secret, + }: args::MaspKeysList, +) { + let wallet = ctx.wallet; + let known_view_keys = wallet.get_viewing_keys(); + let known_spend_keys = wallet.get_spending_keys(); + if known_view_keys.is_empty() { + println!( + "No known keys. Try `masp add --alias my-addr --value ...` to add \ + a new key to the wallet." + ); + } else { + let stdout = std::io::stdout(); + let mut w = stdout.lock(); + writeln!(w, "Known keys:").unwrap(); + for (alias, key) in known_view_keys { + write!(w, " Alias \"{}\"", alias).unwrap(); + let spending_key_opt = known_spend_keys.get(&alias); + // If this alias is associated with a spending key, indicate whether + // or not the spending key is encrypted + // TODO: consider turning if let into match + if let Some(spending_key) = spending_key_opt { + if spending_key.is_encrypted() { + writeln!(w, " (encrypted):") + } else { + writeln!(w, " (not encrypted):") + } + .unwrap(); + } else { + writeln!(w, ":").unwrap(); + } + // Always print the corresponding viewing key + writeln!(w, " Viewing Key: {}", key).unwrap(); + // A subset of viewing keys will have corresponding spending keys. + // Print those too if they are available and requested. + if unsafe_show_secret { + if let Some(spending_key) = spending_key_opt { + match spending_key.get::(decrypt, None) { + // Here the spending key is unencrypted or successfully + // decrypted + Ok(spending_key) => { + writeln!(w, " Spending key: {}", spending_key) + .unwrap(); + } + // Here the key is encrypted but decryption has not been + // requested + Err(DecryptionError::NotDecrypting) if !decrypt => { + continue; + } + // Here the key is encrypted but incorrect password has + // been provided + Err(err) => { + writeln!( + w, + " Couldn't decrypt the spending key: {}", + err + ) + .unwrap(); + } + } + } + } + } + } +} + +/// List payment addresses. +pub fn payment_addresses_list(ctx: Context) { + let wallet = ctx.wallet; + let known_addresses = wallet.get_payment_addrs(); + if known_addresses.is_empty() { + println!( + "No known payment addresses. Try `masp gen-addr --alias my-addr` \ + to generate a new payment address." + ); + } else { + let stdout = std::io::stdout(); + let mut w = stdout.lock(); + writeln!(w, "Known payment addresses:").unwrap(); + for (alias, address) in sorted(known_addresses) { + writeln!(w, " \"{}\": {}", alias, address).unwrap(); + } + } +} + +/// Generate a spending key. +pub fn spending_key_gen( + ctx: Context, + args::MaspSpendKeyGen { + alias, + alias_force, + unsafe_dont_encrypt, + }: args::MaspSpendKeyGen, +) { + let mut wallet = ctx.wallet; + let alias = alias.to_lowercase(); + let password = read_and_confirm_encryption_password(unsafe_dont_encrypt); + let (alias, _key) = wallet.gen_spending_key(alias, password, alias_force); + crate::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); + println!( + "Successfully added a spending key with alias: \"{}\"", + alias + ); +} + +/// Generate a shielded payment address from the given key. +pub fn payment_address_gen( + ctx: Context, + args::MaspPayAddrGen { + alias, + alias_force, + viewing_key, + pin, + }: args::MaspPayAddrGen, +) { + let alias = alias.to_lowercase(); + let viewing_key = ExtendedFullViewingKey::from(viewing_key).fvk.vk; + let (div, _g_d) = find_valid_diversifier(&mut OsRng); + let payment_addr = viewing_key + .to_payment_address(div) + .expect("a PaymentAddress"); + let mut wallet = ctx.wallet; + let alias = wallet + .insert_payment_addr( + alias, + PaymentAddress::from(payment_addr).pinned(pin), + alias_force, + ) + .unwrap_or_else(|| { + eprintln!("Payment address not added"); + cli::safe_exit(1); + }); + crate::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); + println!( + "Successfully generated a payment address with the following alias: {}", + alias, + ); +} + +/// Add a viewing key, spending key, or payment address to wallet. +pub fn address_key_add( + mut ctx: Context, + args::MaspAddrKeyAdd { + alias, + alias_force, + value, + unsafe_dont_encrypt, + }: args::MaspAddrKeyAdd, +) { + let alias = alias.to_lowercase(); + let (alias, typ) = match value { + MaspValue::FullViewingKey(viewing_key) => { + let alias = ctx + .wallet + .insert_viewing_key(alias, viewing_key, alias_force) + .unwrap_or_else(|| { + eprintln!("Viewing key not added"); + cli::safe_exit(1); + }); + (alias, "viewing key") + } + MaspValue::ExtendedSpendingKey(spending_key) => { + let password = + read_and_confirm_encryption_password(unsafe_dont_encrypt); + let alias = ctx + .wallet + .encrypt_insert_spending_key( + alias, + spending_key, + password, + alias_force, + ) + .unwrap_or_else(|| { + eprintln!("Spending key not added"); + cli::safe_exit(1); + }); + (alias, "spending key") + } + MaspValue::PaymentAddress(payment_addr) => { + let alias = ctx + .wallet + .insert_payment_addr(alias, payment_addr, alias_force) + .unwrap_or_else(|| { + eprintln!("Payment address not added"); + cli::safe_exit(1); + }); + (alias, "payment address") + } + }; + crate::wallet::save(&ctx.wallet).unwrap_or_else(|err| eprintln!("{}", err)); + println!( + "Successfully added a {} with the following alias to wallet: {}", + typ, alias, + ); +} + +/// Restore a keypair and an implicit address from the mnemonic code in the +/// wallet. +pub fn key_and_address_restore( + ctx: Context, + args::KeyAndAddressRestore { + scheme, + alias, + alias_force, + unsafe_dont_encrypt, + derivation_path, + }: args::KeyAndAddressRestore, +) { + let mut wallet = ctx.wallet; + let encryption_password = + read_and_confirm_encryption_password(unsafe_dont_encrypt); + let (alias, _key) = wallet + .derive_key_from_user_mnemonic_code( + scheme, + alias, + alias_force, + derivation_path, + encryption_password, + ) + .unwrap_or_else(|err| { + eprintln!("{}", err); + cli::safe_exit(1) + }) + .unwrap_or_else(|| { + println!("No changes are persisted. Exiting."); + cli::safe_exit(0); + }); + crate::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); + println!( + "Successfully added a key and an address with alias: \"{}\"", + alias + ); +} + +/// Generate a new keypair and derive implicit address from it and store them in +/// the wallet. +pub fn key_and_address_gen( + ctx: Context, + args::KeyAndAddressGen { + scheme, + alias, + alias_force, + unsafe_dont_encrypt, + derivation_path, + }: args::KeyAndAddressGen, +) { + let mut wallet = ctx.wallet; + let encryption_password = + read_and_confirm_encryption_password(unsafe_dont_encrypt); + let mut rng = OsRng; + let derivation_path_and_mnemonic_rng = + derivation_path.map(|p| (p, &mut rng)); + let (alias, _key) = wallet + .gen_key( + scheme, + alias, + alias_force, + encryption_password, + derivation_path_and_mnemonic_rng, + ) + .unwrap_or_else(|err| { + eprintln!("{}", err); + cli::safe_exit(1); + }) + .unwrap_or_else(|| { + println!("No changes are persisted. Exiting."); + cli::safe_exit(0); + }); + crate::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); + println!( + "Successfully added a key and an address with alias: \"{}\"", + alias + ); +} + +/// Find a keypair in the wallet store. +pub fn key_find( + ctx: Context, + args::KeyFind { + public_key, + alias, + value, + unsafe_show_secret, + }: args::KeyFind, +) { + let mut wallet = ctx.wallet; + let found_keypair = match public_key { + Some(pk) => wallet.find_key_by_pk(&pk, None), + None => { + let alias = alias.or(value); + match alias { + None => { + eprintln!( + "An alias, public key or public key hash needs to be \ + supplied" + ); + cli::safe_exit(1) + } + Some(alias) => wallet.find_key(alias.to_lowercase(), None), + } + } + }; + match found_keypair { + Ok(keypair) => { + let pkh: PublicKeyHash = (&keypair.ref_to()).into(); + println!("Public key hash: {}", pkh); + println!("Public key: {}", keypair.ref_to()); + if unsafe_show_secret { + println!("Secret key: {}", keypair); + } + } + Err(err) => { + eprintln!("{}", err); + } + } +} + +/// List all known keys. +pub fn key_list( + ctx: Context, + args::KeyList { + decrypt, + unsafe_show_secret, + }: args::KeyList, +) { + let wallet = ctx.wallet; + let known_keys = wallet.get_keys(); + if known_keys.is_empty() { + println!( + "No known keys. Try `key gen --alias my-key` to generate a new \ + key." + ); + } else { + let stdout = io::stdout(); + let mut w = stdout.lock(); + writeln!(w, "Known keys:").unwrap(); + for (alias, (stored_keypair, pkh)) in known_keys { + let encrypted = if stored_keypair.is_encrypted() { + "encrypted" + } else { + "not encrypted" + }; + writeln!(w, " Alias \"{}\" ({}):", alias, encrypted).unwrap(); + if let Some(pkh) = pkh { + writeln!(w, " Public key hash: {}", pkh).unwrap(); + } + match stored_keypair.get::(decrypt, None) { + Ok(keypair) => { + writeln!(w, " Public key: {}", keypair.ref_to()) + .unwrap(); + if unsafe_show_secret { + writeln!(w, " Secret key: {}", keypair).unwrap(); + } + } + Err(DecryptionError::NotDecrypting) if !decrypt => { + continue; + } + Err(err) => { + writeln!(w, " Couldn't decrypt the keypair: {}", err) + .unwrap(); + } + } + } + } +} + +/// Export a keypair to a file. +pub fn key_export(ctx: Context, args::KeyExport { alias }: args::KeyExport) { + let mut wallet = ctx.wallet; + wallet + .find_key(alias.to_lowercase(), None) + .map(|keypair| { + let file_data = keypair + .try_to_vec() + .expect("Encoding keypair shouldn't fail"); + let file_name = format!("key_{}", alias.to_lowercase()); + let mut file = File::create(&file_name).unwrap(); + + file.write_all(file_data.as_ref()).unwrap(); + println!("Exported to file {}", file_name); + }) + .unwrap_or_else(|err| { + eprintln!("{}", err); + cli::safe_exit(1) + }) +} + +/// List all known addresses. +pub fn address_list(ctx: Context) { + let wallet = ctx.wallet; + let known_addresses = wallet.get_addresses(); + if known_addresses.is_empty() { + println!( + "No known addresses. Try `address gen --alias my-addr` to \ + generate a new implicit address." + ); + } else { + let stdout = io::stdout(); + let mut w = stdout.lock(); + writeln!(w, "Known addresses:").unwrap(); + for (alias, address) in sorted(known_addresses) { + writeln!(w, " \"{}\": {}", alias, address.to_pretty_string()) + .unwrap(); + } + } +} + +/// Find address (alias) by its alias (address). +pub fn address_or_alias_find(ctx: Context, args: args::AddressOrAliasFind) { + let wallet = ctx.wallet; + if args.address.is_some() && args.alias.is_some() { + panic!( + "This should not be happening: clap should emit its own error \ + message." + ); + } else if args.alias.is_some() { + if let Some(address) = wallet.find_address(args.alias.as_ref().unwrap()) + { + println!("Found address {}", address.to_pretty_string()); + } else { + println!( + "No address with alias {} found. Use the command `address \ + list` to see all the known addresses.", + args.alias.unwrap().to_lowercase() + ); + } + } else if args.address.is_some() { + if let Some(alias) = wallet.find_alias(args.address.as_ref().unwrap()) { + println!("Found alias {}", alias); + } else { + println!( + "No alias with address {} found. Use the command `address \ + list` to see all the known addresses.", + args.address.unwrap() + ); + } + } +} + +/// Add an address to the wallet. +pub fn address_add(ctx: Context, args: args::AddressAdd) { + let mut wallet = ctx.wallet; + if wallet + .add_address( + args.alias.clone().to_lowercase(), + args.address, + args.alias_force, + ) + .is_none() + { + eprintln!("Address not added"); + cli::safe_exit(1); + } + crate::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); + println!( + "Successfully added a key and an address with alias: \"{}\"", + args.alias.to_lowercase() + ); +} diff --git a/apps/src/lib/wallet/mod.rs b/apps/src/lib/wallet/mod.rs index 32d2ab6eac..464d276ecc 100644 --- a/apps/src/lib/wallet/mod.rs +++ b/apps/src/lib/wallet/mod.rs @@ -1,3 +1,4 @@ +pub mod cli_utils; pub mod defaults; pub mod pre_genesis; mod store; diff --git a/apps/src/lib/wallet/pre_genesis.rs b/apps/src/lib/wallet/pre_genesis.rs index a0ec978887..3b05bb214f 100644 --- a/apps/src/lib/wallet/pre_genesis.rs +++ b/apps/src/lib/wallet/pre_genesis.rs @@ -2,7 +2,7 @@ use std::fs; use std::path::{Path, PathBuf}; use ark_serialize::{Read, Write}; -use file_lock::{FileLock, FileOptions}; +use fd_lock::RwLock; use namada::ledger::wallet::pre_genesis::{ ReadError, ValidatorStore, ValidatorWallet, }; @@ -36,10 +36,11 @@ pub fn gen_and_store( let wallet_dir = wallet_path.parent().unwrap(); fs::create_dir_all(wallet_dir)?; // Write the file - let options = FileOptions::new().create(true).write(true).truncate(true); - let mut filelock = - FileLock::lock(wallet_path.to_str().unwrap(), true, options)?; - filelock.file.write_all(&data)?; + let mut options = fs::OpenOptions::new(); + options.create(true).write(true).truncate(true); + let mut lock = RwLock::new(options.open(wallet_path)?); + let mut guard = lock.write()?; + guard.write_all(&data)?; Ok(validator) } @@ -47,59 +48,60 @@ pub fn gen_and_store( /// from a TOML file. pub fn load(store_dir: &Path) -> Result { let wallet_file = validator_file_name(store_dir); - match FileLock::lock( - wallet_file.to_str().unwrap(), - true, - FileOptions::new().read(true).write(false), - ) { - Ok(mut filelock) => { - let mut store = Vec::::new(); - filelock.file.read_to_end(&mut store).map_err(|err| { - ReadError::ReadWallet( - store_dir.to_str().unwrap().into(), - err.to_string(), - ) - })?; - let store = - ValidatorStore::decode(store).map_err(ReadError::Decode)?; + let mut options = fs::OpenOptions::new(); + options.read(true).write(false); + let lock = RwLock::new(options.open(&wallet_file).map_err(|err| { + ReadError::ReadWallet( + wallet_file.to_string_lossy().into_owned(), + err.to_string(), + ) + })?); + let guard = lock.read().map_err(|err| { + ReadError::ReadWallet( + wallet_file.to_string_lossy().into_owned(), + err.to_string(), + ) + })?; + let mut store = Vec::::new(); + (&*guard).read_to_end(&mut store).map_err(|err| { + ReadError::ReadWallet( + store_dir.to_str().unwrap().into(), + err.to_string(), + ) + })?; + let store = ValidatorStore::decode(store).map_err(ReadError::Decode)?; - let password = if store.account_key.is_encrypted() - || store.consensus_key.is_encrypted() - || store.account_key.is_encrypted() - { - Some(CliWalletUtils::read_decryption_password()) - } else { - None - }; + let password = if store.account_key.is_encrypted() + || store.consensus_key.is_encrypted() + || store.account_key.is_encrypted() + { + Some(CliWalletUtils::read_decryption_password()) + } else { + None + }; - let account_key = store - .account_key - .get::(true, password.clone())?; - let consensus_key = store - .consensus_key - .get::(true, password.clone())?; - let eth_cold_key = store - .eth_cold_key - .get::(true, password.clone())?; - let eth_hot_key = store.validator_keys.eth_bridge_keypair.clone(); - let tendermint_node_key = store - .tendermint_node_key - .get::(true, password)?; + let account_key = store + .account_key + .get::(true, password.clone())?; + let consensus_key = store + .consensus_key + .get::(true, password.clone())?; + let eth_cold_key = store + .eth_cold_key + .get::(true, password.clone())?; + let eth_hot_key = store.validator_keys.eth_bridge_keypair.clone(); + let tendermint_node_key = store + .tendermint_node_key + .get::(true, password)?; - Ok(ValidatorWallet { - store, - account_key, - consensus_key, - eth_cold_key, - eth_hot_key, - tendermint_node_key, - }) - } - Err(err) => Err(ReadError::ReadWallet( - wallet_file.to_string_lossy().into_owned(), - err.to_string(), - )), - } + Ok(ValidatorWallet { + store, + account_key, + consensus_key, + eth_cold_key, + eth_hot_key, + tendermint_node_key, + }) } /// Generate a new [`ValidatorWallet`] with required pre-genesis keys. Will diff --git a/apps/src/lib/wallet/store.rs b/apps/src/lib/wallet/store.rs index 925e9c6bf9..6ae0d023d9 100644 --- a/apps/src/lib/wallet/store.rs +++ b/apps/src/lib/wallet/store.rs @@ -7,7 +7,7 @@ use std::str::FromStr; use ark_std::rand::prelude::*; use ark_std::rand::SeedableRng; -use file_lock::{FileLock, FileOptions}; +use fd_lock::RwLock; #[cfg(not(feature = "dev"))] use namada::ledger::wallet::store::AddressVpType; #[cfg(feature = "dev")] @@ -48,10 +48,11 @@ pub fn save(store: &Store, store_dir: &Path) -> std::io::Result<()> { let wallet_dir = wallet_path.parent().unwrap(); fs::create_dir_all(wallet_dir)?; // Write the file - let options = FileOptions::new().create(true).write(true).truncate(true); - let mut filelock = - FileLock::lock(wallet_path.to_str().unwrap(), true, options)?; - filelock.file.write_all(&data) + let mut options = fs::OpenOptions::new(); + options.create(true).write(true).truncate(true); + let mut lock = RwLock::new(options.open(wallet_path)?); + let mut guard = lock.write()?; + guard.write_all(&data) } /// Load the store file or create a new one without any keys or addresses. @@ -88,26 +89,28 @@ pub fn load_or_new_from_genesis( /// Attempt to load the store file. pub fn load(store_dir: &Path) -> Result { let wallet_file = wallet_file(store_dir); - match FileLock::lock( - wallet_file.to_str().unwrap(), - true, - FileOptions::new().read(true).write(false), - ) { - Ok(mut filelock) => { - let mut store = Vec::::new(); - filelock.file.read_to_end(&mut store).map_err(|err| { - LoadStoreError::ReadWallet( - store_dir.to_str().unwrap().parse().unwrap(), - err.to_string(), - ) - })?; - Store::decode(store).map_err(LoadStoreError::Decode) - } - Err(err) => Err(LoadStoreError::ReadWallet( + let mut options = fs::OpenOptions::new(); + options.read(true).write(false); + let lock = RwLock::new(options.open(&wallet_file).map_err(|err| { + LoadStoreError::ReadWallet( wallet_file.to_string_lossy().into_owned(), err.to_string(), - )), - } + ) + })?); + let guard = lock.read().map_err(|err| { + LoadStoreError::ReadWallet( + wallet_file.to_string_lossy().into_owned(), + err.to_string(), + ) + })?; + let mut store = Vec::::new(); + (&*guard).read_to_end(&mut store).map_err(|err| { + LoadStoreError::ReadWallet( + store_dir.to_str().unwrap().parse().unwrap(), + err.to_string(), + ) + })?; + Store::decode(store).map_err(LoadStoreError::Decode) } /// Add addresses from a genesis configuration. diff --git a/core/src/ledger/eth_bridge/storage/mod.rs b/core/src/ledger/eth_bridge/storage/mod.rs index 958b002af0..0906be3d5d 100644 --- a/core/src/ledger/eth_bridge/storage/mod.rs +++ b/core/src/ledger/eth_bridge/storage/mod.rs @@ -29,6 +29,7 @@ pub fn escrow_key(nam_addr: &Address) -> Key { pub fn is_eth_bridge_key(nam_addr: &Address, key: &Key) -> bool { key == &escrow_key(nam_addr) || matches!(key.segments.get(0), Some(first_segment) if first_segment == &ADDRESS.to_db_key()) + || wrapped_erc20s::has_erc20_segment(key) } /// A key for storing the active / inactive status @@ -62,6 +63,7 @@ mod test { use super::*; use crate::types::address; use crate::types::address::nam; + use crate::types::ethereum_events::testing::arbitrary_eth_address; #[test] fn test_is_eth_bridge_key_returns_true_for_eth_bridge_address() { @@ -77,6 +79,17 @@ mod test { assert!(is_eth_bridge_key(&nam(), &key)); } + #[test] + fn test_is_eth_bridge_key_returns_true_for_eth_bridge_balance_key() { + let eth_addr = arbitrary_eth_address(); + let token = address::Address::Internal( + address::InternalAddress::Erc20(eth_addr), + ); + let key = + balance_key(&token, &address::testing::established_address_1()); + assert!(is_eth_bridge_key(&nam(), &key)); + } + #[test] fn test_is_eth_bridge_key_returns_false_for_different_address() { let key = @@ -92,4 +105,11 @@ mod test { .expect("Could not set up test"); assert!(!is_eth_bridge_key(&nam(), &key)); } + + #[test] + fn test_is_eth_bridge_key_returns_false_for_non_eth_bridge_balance_key() { + let key = + balance_key(&nam(), &address::testing::established_address_1()); + assert!(!is_eth_bridge_key(&nam(), &key)); + } } diff --git a/core/src/ledger/eth_bridge/storage/wrapped_erc20s.rs b/core/src/ledger/eth_bridge/storage/wrapped_erc20s.rs index 6d2f6de4da..0062dd50c9 100644 --- a/core/src/ledger/eth_bridge/storage/wrapped_erc20s.rs +++ b/core/src/ledger/eth_bridge/storage/wrapped_erc20s.rs @@ -1,68 +1,17 @@ //! Functionality for accessing the multitoken subspace -use std::str::FromStr; use eyre::eyre; -use crate::types::address::Address; +use crate::types::address::{Address, InternalAddress}; use crate::types::ethereum_events::EthAddress; -use crate::types::storage::{self, DbKeySeg, KeySeg}; - -#[allow(missing_docs)] -pub const MULTITOKEN_KEY_SEGMENT: &str = "ERC20"; - -/// Get the key prefix corresponding to the storage subspace that holds wrapped -/// ERC20 tokens -pub fn prefix() -> storage::Key { - super::prefix() - .push(&MULTITOKEN_KEY_SEGMENT.to_owned()) - .expect("should always be able to construct this key") -} - -const BALANCE_KEY_SEGMENT: &str = "balance"; -const SUPPLY_KEY_SEGMENT: &str = "supply"; - -/// Generator for the keys under which details of an ERC20 token are stored -pub struct Keys { - /// The prefix of keys under which the details for a specific ERC20 token - /// are stored - prefix: storage::Key, -} - -impl Keys { - /// Get the `balance` key for a specific owner - there should be a - /// [`crate::types::token::Amount`] stored here - pub fn balance(&self, owner: &Address) -> storage::Key { - self.prefix - .push(&BALANCE_KEY_SEGMENT.to_owned()) - .expect("should always be able to construct this key") - .push(&owner.to_db_key()) - .expect("should always be able to construct this key") - } - - /// Get the `supply` key - there should be a - /// [`crate::types::token::Amount`] stored here - pub fn supply(&self) -> storage::Key { - self.prefix - .push(&SUPPLY_KEY_SEGMENT.to_owned()) - .expect("should always be able to construct this key") - } -} - -impl From<&EthAddress> for Keys { - fn from(address: &EthAddress) -> Self { - Keys { - prefix: prefix() - .push(&address.to_canonical()) - .expect("should always be able to construct this key"), - } - } -} - -/// Construct a sub-prefix from an ERC20 address. -pub fn sub_prefix(address: &EthAddress) -> storage::Key { - storage::Key::from(MULTITOKEN_KEY_SEGMENT.to_owned().to_db_key()) - .push(&address.to_db_key()) - .expect("should always be able to construct this key") +use crate::types::storage::{self, DbKeySeg}; +use crate::types::token::{ + balance_key, minted_balance_key, MINTED_STORAGE_KEY, +}; + +/// Construct a token address from an ERC20 address. +pub fn token(address: &EthAddress) -> Address { + Address::Internal(InternalAddress::Erc20(*address)) } /// Represents the type of a key relating to a wrapped ERC20 @@ -88,18 +37,21 @@ pub struct Key { impl From<&Key> for storage::Key { fn from(mt_key: &Key) -> Self { - let keys = Keys::from(&mt_key.asset); + let token = token(&mt_key.asset); match &mt_key.suffix { - KeyType::Balance { owner } => keys.balance(owner), - KeyType::Supply => keys.supply(), + KeyType::Balance { owner } => balance_key(&token, owner), + KeyType::Supply => minted_balance_key(&token), } } } -fn has_erc20_segment(key: &storage::Key) -> bool { +/// Returns true if the given key has an ERC20 token +pub fn has_erc20_segment(key: &storage::Key) -> bool { matches!( key.segments.get(1), - Some(segment) if segment == &DbKeySeg::StringSeg(MULTITOKEN_KEY_SEGMENT.to_owned()), + Some(DbKeySeg::AddressSeg(Address::Internal( + InternalAddress::Erc20(_addr), + ))) ) } @@ -116,52 +68,41 @@ impl TryFrom<(&Address, &storage::Key)> for Key { return Err(eyre!("key does not have ERC20 segment")); } - let asset = - if let Some(DbKeySeg::StringSeg(segment)) = key.segments.get(2) { - EthAddress::from_str(segment)? - } else { - return Err(eyre!( - "key has an incorrect segment at index #2, expected an \ - Ethereum address" - )); - }; - - let segment_3 = - if let Some(DbKeySeg::StringSeg(segment)) = key.segments.get(3) { - segment.to_owned() - } else { - return Err(eyre!( - "key has an incorrect segment at index #3, expected a \ - string segment" - )); - }; - - match segment_3.as_str() { - SUPPLY_KEY_SEGMENT => { - let supply_key = Key { + let asset = if let Some(DbKeySeg::AddressSeg(Address::Internal( + InternalAddress::Erc20(addr), + ))) = key.segments.get(1) + { + *addr + } else { + return Err(eyre!( + "key has an incorrect segment at index #2, expected an \ + Ethereum address" + )); + }; + + match key.segments.get(3) { + Some(DbKeySeg::AddressSeg(owner)) => { + let balance_key = Key { asset, - suffix: KeyType::Supply, + suffix: KeyType::Balance { + owner: owner.clone(), + }, }; - Ok(supply_key) + Ok(balance_key) } - BALANCE_KEY_SEGMENT => { - let owner = if let Some(DbKeySeg::AddressSeg(address)) = - key.segments.get(4) - { - address.to_owned() - } else { - return Err(eyre!( - "key has an incorrect segment at index #4, expected \ - an address segment" - )); - }; - let balance_key = Key { + Some(DbKeySeg::StringSeg(segment)) + if segment == MINTED_STORAGE_KEY => + { + let supply_key = Key { asset, - suffix: KeyType::Balance { owner }, + suffix: KeyType::Supply, }; - Ok(balance_key) + Ok(supply_key) } - _ => Err(eyre!("key has unrecognized string segment at index #3")), + _ => Err(eyre!( + "key has an incorrect segment at index #3, expected a string \ + segment" + )), } } } @@ -174,97 +115,77 @@ mod test { use super::*; use crate::ledger::eth_bridge::ADDRESS; use crate::types::address::{nam, Address}; - use crate::types::ethereum_events::testing::{ - DAI_ERC20_ETH_ADDRESS, DAI_ERC20_ETH_ADDRESS_CHECKSUMMED, - }; + use crate::types::ethereum_events::testing::DAI_ERC20_ETH_ADDRESS; use crate::types::storage::DbKeySeg; + use crate::types::token::BALANCE_STORAGE_KEY; + const MULTITOKEN_ADDRESS: Address = + Address::Internal(InternalAddress::Multitoken); const ARBITRARY_OWNER_ADDRESS: &str = "atest1d9khqw36x9zyxwfhgfpygv2pgc65gse4gy6rjs34gfzr2v69gy6y23zpggurjv2yx5m52sesu6r4y4"; - #[test] - fn test_prefix() { - assert_matches!( - &prefix().segments[..], - [ - DbKeySeg::AddressSeg(multitoken_addr), - DbKeySeg::StringSeg(multitoken_path), - ] if multitoken_addr == &ADDRESS && - multitoken_path == MULTITOKEN_KEY_SEGMENT - ) - } - - #[test] - fn test_keys_from_eth_address() { - let keys: Keys = (&DAI_ERC20_ETH_ADDRESS).into(); - assert_matches!( - &keys.prefix.segments[..], - [ - DbKeySeg::AddressSeg(multitoken_addr), - DbKeySeg::StringSeg(multitoken_path), - DbKeySeg::StringSeg(token_id), - ] if multitoken_addr == &ADDRESS && - multitoken_path == MULTITOKEN_KEY_SEGMENT && - token_id == &DAI_ERC20_ETH_ADDRESS_CHECKSUMMED.to_ascii_lowercase() - ) + fn dai_erc20_token() -> Address { + Address::Internal(InternalAddress::Erc20(DAI_ERC20_ETH_ADDRESS)) } #[test] fn test_keys_balance() { - let keys: Keys = (&DAI_ERC20_ETH_ADDRESS).into(); - let key = - keys.balance(&Address::from_str(ARBITRARY_OWNER_ADDRESS).unwrap()); + let token = token(&DAI_ERC20_ETH_ADDRESS); + let key = balance_key( + &token, + &Address::from_str(ARBITRARY_OWNER_ADDRESS).unwrap(), + ); assert_matches!( &key.segments[..], [ DbKeySeg::AddressSeg(multitoken_addr), - DbKeySeg::StringSeg(multitoken_path), - DbKeySeg::StringSeg(token_id), + DbKeySeg::AddressSeg(token_addr), DbKeySeg::StringSeg(balance_key_seg), DbKeySeg::AddressSeg(owner_addr), - ] if multitoken_addr == &ADDRESS && - multitoken_path == MULTITOKEN_KEY_SEGMENT && - token_id == &DAI_ERC20_ETH_ADDRESS_CHECKSUMMED.to_ascii_lowercase() && - balance_key_seg == BALANCE_KEY_SEGMENT && + ] if multitoken_addr == &MULTITOKEN_ADDRESS && + token_addr == &dai_erc20_token() && + balance_key_seg == BALANCE_STORAGE_KEY && owner_addr == &Address::decode(ARBITRARY_OWNER_ADDRESS).unwrap() ) } #[test] fn test_keys_balance_to_string() { - let keys: Keys = (&DAI_ERC20_ETH_ADDRESS).into(); - let key = - keys.balance(&Address::from_str(ARBITRARY_OWNER_ADDRESS).unwrap()); + let token = token(&DAI_ERC20_ETH_ADDRESS); + let key = balance_key( + &token, + &Address::from_str(ARBITRARY_OWNER_ADDRESS).unwrap(), + ); assert_eq!( - "#atest1v9hx7w36g42ysgzzwf5kgem9ypqkgerjv4ehxgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpq8f99ew/ERC20/0x6b175474e89094c44da98b954eedeac495271d0f/balance/#atest1d9khqw36x9zyxwfhgfpygv2pgc65gse4gy6rjs34gfzr2v69gy6y23zpggurjv2yx5m52sesu6r4y4", + "#atest1v9hx7w36f46kcarfw3hkketwyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpq4w0mck/#atest1v46xsw36xe3rzde4xsmngefc8ycrjdrrxs6xgcfe8p3rjdf5v4jkgetpvv6rjdfjxuckgvrxqhdj5x/balance/#atest1d9khqw36x9zyxwfhgfpygv2pgc65gse4gy6rjs34gfzr2v69gy6y23zpggurjv2yx5m52sesu6r4y4", key.to_string() ) } #[test] fn test_keys_supply() { - let keys: Keys = (&DAI_ERC20_ETH_ADDRESS).into(); - let key = keys.supply(); + let token = token(&DAI_ERC20_ETH_ADDRESS); + let key = minted_balance_key(&token); assert_matches!( &key.segments[..], [ DbKeySeg::AddressSeg(multitoken_addr), - DbKeySeg::StringSeg(multitoken_path), - DbKeySeg::StringSeg(token_id), + DbKeySeg::AddressSeg(token_addr), + DbKeySeg::StringSeg(balance_key_seg), DbKeySeg::StringSeg(supply_key_seg), - ] if multitoken_addr == &ADDRESS && - multitoken_path == MULTITOKEN_KEY_SEGMENT && - token_id == &DAI_ERC20_ETH_ADDRESS_CHECKSUMMED.to_ascii_lowercase() && - supply_key_seg == SUPPLY_KEY_SEGMENT + ] if multitoken_addr == &MULTITOKEN_ADDRESS && + token_addr == &dai_erc20_token() && + balance_key_seg == BALANCE_STORAGE_KEY && + supply_key_seg == MINTED_STORAGE_KEY ) } #[test] fn test_keys_supply_to_string() { - let keys: Keys = (&DAI_ERC20_ETH_ADDRESS).into(); - let key = keys.supply(); + let token = token(&DAI_ERC20_ETH_ADDRESS); + let key = minted_balance_key(&token); assert_eq!( - "#atest1v9hx7w36g42ysgzzwf5kgem9ypqkgerjv4ehxgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpq8f99ew/ERC20/0x6b175474e89094c44da98b954eedeac495271d0f/supply", + "#atest1v9hx7w36f46kcarfw3hkketwyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpq4w0mck/#atest1v46xsw36xe3rzde4xsmngefc8ycrjdrrxs6xgcfe8p3rjdf5v4jkgetpvv6rjdfjxuckgvrxqhdj5x/balance/minted", key.to_string(), ) } @@ -281,13 +202,13 @@ mod test { &key.segments[..], [ DbKeySeg::AddressSeg(multitoken_addr), - DbKeySeg::StringSeg(multitoken_path), - DbKeySeg::StringSeg(token_id), + DbKeySeg::AddressSeg(token_addr), + DbKeySeg::StringSeg(balance_key_seg), DbKeySeg::StringSeg(supply_key_seg), - ] if multitoken_addr == &ADDRESS && - multitoken_path == MULTITOKEN_KEY_SEGMENT && - token_id == &DAI_ERC20_ETH_ADDRESS_CHECKSUMMED.to_ascii_lowercase() && - supply_key_seg == SUPPLY_KEY_SEGMENT + ] if multitoken_addr == &MULTITOKEN_ADDRESS && + token_addr == &dai_erc20_token() && + balance_key_seg == BALANCE_STORAGE_KEY && + supply_key_seg == MINTED_STORAGE_KEY ); // balance key @@ -302,14 +223,12 @@ mod test { &key.segments[..], [ DbKeySeg::AddressSeg(multitoken_addr), - DbKeySeg::StringSeg(multitoken_path), - DbKeySeg::StringSeg(token_id), + DbKeySeg::AddressSeg(token_addr), DbKeySeg::StringSeg(balance_key_seg), DbKeySeg::AddressSeg(owner_addr), - ] if multitoken_addr == &ADDRESS && - multitoken_path == MULTITOKEN_KEY_SEGMENT && - token_id == &DAI_ERC20_ETH_ADDRESS_CHECKSUMMED.to_ascii_lowercase() && - balance_key_seg == BALANCE_KEY_SEGMENT && + ] if multitoken_addr == &MULTITOKEN_ADDRESS && + token_addr == &dai_erc20_token() && + balance_key_seg == BALANCE_STORAGE_KEY && owner_addr == &Address::decode(ARBITRARY_OWNER_ADDRESS).unwrap() ); } @@ -318,9 +237,10 @@ mod test { fn test_try_from_key_for_multitoken_key_supply() { // supply key let key = storage::Key::from_str(&format!( - "#{}/ERC20/{}/supply", - ADDRESS, - DAI_ERC20_ETH_ADDRESS_CHECKSUMMED.to_ascii_lowercase(), + "#{}/#{}/balance/{}", + MULTITOKEN_ADDRESS, + dai_erc20_token(), + MINTED_STORAGE_KEY, )) .expect("Should be able to construct key for test"); @@ -344,9 +264,9 @@ mod test { fn test_try_from_key_for_multitoken_key_balance() { // supply key let key = storage::Key::from_str(&format!( - "#{}/ERC20/{}/balance/#{}", + "#{}/#{}/balance/#{}", ADDRESS, - DAI_ERC20_ETH_ADDRESS_CHECKSUMMED.to_ascii_lowercase(), + dai_erc20_token(), ARBITRARY_OWNER_ADDRESS )) .expect("Should be able to construct key for test"); @@ -375,9 +295,9 @@ mod test { #[test] fn test_has_erc20_segment() { let key = storage::Key::from_str(&format!( - "#{}/ERC20/{}/balance/#{}", + "#{}/#{}/balance/#{}", ADDRESS, - DAI_ERC20_ETH_ADDRESS_CHECKSUMMED.to_ascii_lowercase(), + dai_erc20_token(), ARBITRARY_OWNER_ADDRESS )) .expect("Should be able to construct key for test"); @@ -385,16 +305,21 @@ mod test { assert!(has_erc20_segment(&key)); let key = storage::Key::from_str(&format!( - "#{}/ERC20/{}/supply", + "#{}/#{}/balance/{}", ADDRESS, - DAI_ERC20_ETH_ADDRESS_CHECKSUMMED.to_ascii_lowercase(), + dai_erc20_token(), + MINTED_STORAGE_KEY, )) .expect("Should be able to construct key for test"); assert!(has_erc20_segment(&key)); - let key = storage::Key::from_str(&format!("#{}/ERC20", ADDRESS)) - .expect("Should be able to construct key for test"); + let key = storage::Key::from_str(&format!( + "#{}/#{}", + MULTITOKEN_ADDRESS, + dai_erc20_token() + )) + .expect("Should be able to construct key for test"); assert!(has_erc20_segment(&key)); } diff --git a/core/src/ledger/ibc/actions.rs b/core/src/ledger/ibc/actions.rs deleted file mode 100644 index e925a98d92..0000000000 --- a/core/src/ledger/ibc/actions.rs +++ /dev/null @@ -1,1605 +0,0 @@ -//! Functions to handle IBC modules - -use std::str::FromStr; - -use sha2::Digest; -use thiserror::Error; - -use crate::ibc::applications::ics20_fungible_token_transfer::msgs::transfer::MsgTransfer; -use crate::ibc::clients::ics07_tendermint::consensus_state::ConsensusState as TmConsensusState; -use crate::ibc::core::ics02_client::client_consensus::{ - AnyConsensusState, ConsensusState, -}; -use crate::ibc::core::ics02_client::client_state::{ - AnyClientState, ClientState, -}; -use crate::ibc::core::ics02_client::client_type::ClientType; -use crate::ibc::core::ics02_client::events::{ - Attributes as ClientAttributes, CreateClient, UpdateClient, UpgradeClient, -}; -use crate::ibc::core::ics02_client::header::{AnyHeader, Header}; -use crate::ibc::core::ics02_client::height::Height; -use crate::ibc::core::ics02_client::msgs::create_client::MsgCreateAnyClient; -use crate::ibc::core::ics02_client::msgs::update_client::MsgUpdateAnyClient; -use crate::ibc::core::ics02_client::msgs::upgrade_client::MsgUpgradeAnyClient; -use crate::ibc::core::ics02_client::msgs::ClientMsg; -use crate::ibc::core::ics03_connection::connection::{ - ConnectionEnd, Counterparty as ConnCounterparty, State as ConnState, -}; -use crate::ibc::core::ics03_connection::events::{ - Attributes as ConnectionAttributes, OpenAck as ConnOpenAck, - OpenConfirm as ConnOpenConfirm, OpenInit as ConnOpenInit, - OpenTry as ConnOpenTry, -}; -use crate::ibc::core::ics03_connection::msgs::conn_open_ack::MsgConnectionOpenAck; -use crate::ibc::core::ics03_connection::msgs::conn_open_confirm::MsgConnectionOpenConfirm; -use crate::ibc::core::ics03_connection::msgs::conn_open_init::MsgConnectionOpenInit; -use crate::ibc::core::ics03_connection::msgs::conn_open_try::MsgConnectionOpenTry; -use crate::ibc::core::ics03_connection::msgs::ConnectionMsg; -use crate::ibc::core::ics04_channel::channel::{ - ChannelEnd, Counterparty as ChanCounterparty, Order, State as ChanState, -}; -use crate::ibc::core::ics04_channel::commitment::PacketCommitment; -use crate::ibc::core::ics04_channel::events::{ - AcknowledgePacket, CloseConfirm as ChanCloseConfirm, - CloseInit as ChanCloseInit, OpenAck as ChanOpenAck, - OpenConfirm as ChanOpenConfirm, OpenInit as ChanOpenInit, - OpenTry as ChanOpenTry, SendPacket, TimeoutPacket, WriteAcknowledgement, -}; -use crate::ibc::core::ics04_channel::msgs::acknowledgement::MsgAcknowledgement; -use crate::ibc::core::ics04_channel::msgs::chan_close_confirm::MsgChannelCloseConfirm; -use crate::ibc::core::ics04_channel::msgs::chan_close_init::MsgChannelCloseInit; -use crate::ibc::core::ics04_channel::msgs::chan_open_ack::MsgChannelOpenAck; -use crate::ibc::core::ics04_channel::msgs::chan_open_confirm::MsgChannelOpenConfirm; -use crate::ibc::core::ics04_channel::msgs::chan_open_init::MsgChannelOpenInit; -use crate::ibc::core::ics04_channel::msgs::chan_open_try::MsgChannelOpenTry; -use crate::ibc::core::ics04_channel::msgs::recv_packet::MsgRecvPacket; -use crate::ibc::core::ics04_channel::msgs::timeout::MsgTimeout; -use crate::ibc::core::ics04_channel::msgs::timeout_on_close::MsgTimeoutOnClose; -use crate::ibc::core::ics04_channel::msgs::{ChannelMsg, PacketMsg}; -use crate::ibc::core::ics04_channel::packet::{Packet, Sequence}; -use crate::ibc::core::ics23_commitment::commitment::CommitmentPrefix; -use crate::ibc::core::ics24_host::error::ValidationError as Ics24Error; -use crate::ibc::core::ics24_host::identifier::{ - ChannelId, ClientId, ConnectionId, PortChannelId, PortId, -}; -use crate::ibc::core::ics26_routing::msgs::Ics26Envelope; -use crate::ibc::events::IbcEvent; -#[cfg(any(feature = "ibc-mocks-abcipp", feature = "ibc-mocks"))] -use crate::ibc::mock::client_state::{MockClientState, MockConsensusState}; -use crate::ibc::timestamp::Timestamp; -use crate::ledger::ibc::data::{ - Error as IbcDataError, FungibleTokenPacketData, IbcMessage, PacketAck, - PacketReceipt, -}; -use crate::ledger::ibc::storage; -use crate::ledger::storage_api; -use crate::tendermint::Time; -use crate::tendermint_proto::{Error as ProtoError, Protobuf}; -use crate::types::address::{Address, InternalAddress}; -use crate::types::ibc::IbcEvent as NamadaIbcEvent; -use crate::types::storage::{BlockHeight, Key}; -use crate::types::time::Rfc3339String; -use crate::types::token::{self, Amount}; - -const COMMITMENT_PREFIX: &[u8] = b"ibc"; - -#[allow(missing_docs)] -#[derive(Error, Debug)] -pub enum Error { - #[error("Invalid client error: {0}")] - ClientId(Ics24Error), - #[error("Invalid port error: {0}")] - PortId(Ics24Error), - #[error("Updating a client error: {0}")] - ClientUpdate(String), - #[error("IBC data error: {0}")] - IbcData(IbcDataError), - #[error("Decoding IBC data error: {0}")] - Decoding(ProtoError), - #[error("Client error: {0}")] - Client(String), - #[error("Connection error: {0}")] - Connection(String), - #[error("Channel error: {0}")] - Channel(String), - #[error("Counter error: {0}")] - Counter(String), - #[error("Sequence error: {0}")] - Sequence(String), - #[error("Time error: {0}")] - Time(String), - #[error("Invalid transfer message: {0}")] - TransferMessage(token::TransferError), - #[error("Sending a token error: {0}")] - SendingToken(String), - #[error("Receiving a token error: {0}")] - ReceivingToken(String), - #[error("IBC storage error: {0}")] - IbcStorage(storage::Error), -} - -// This is needed to use `ibc::Handler::Error` with `IbcActions` in -// `tx_prelude/src/ibc.rs` -impl From for storage_api::Error { - fn from(err: Error) -> Self { - storage_api::Error::new(err) - } -} - -/// for handling IBC modules -pub type Result = std::result::Result; - -/// IBC trait to be implemented in integration that can read and write -pub trait IbcActions { - /// IBC action error - type Error: From; - - /// Read IBC-related data - fn read_ibc_data( - &self, - key: &Key, - ) -> std::result::Result>, Self::Error>; - - /// Write IBC-related data - fn write_ibc_data( - &mut self, - key: &Key, - data: impl AsRef<[u8]>, - ) -> std::result::Result<(), Self::Error>; - - /// Delete IBC-related data - fn delete_ibc_data( - &mut self, - key: &Key, - ) -> std::result::Result<(), Self::Error>; - - /// Emit an IBC event - fn emit_ibc_event( - &mut self, - event: NamadaIbcEvent, - ) -> std::result::Result<(), Self::Error>; - - /// Transfer token - fn transfer_token( - &mut self, - src: &Key, - dest: &Key, - amount: Amount, - ) -> std::result::Result<(), Self::Error>; - - /// Get the current height of this chain - fn get_height(&self) -> std::result::Result; - - /// Get the current time of the tendermint header of this chain - fn get_header_time( - &self, - ) -> std::result::Result; - - /// dispatch according to ICS26 routing - fn dispatch_ibc_action( - &mut self, - tx_data: &[u8], - ) -> std::result::Result<(), Self::Error> { - let ibc_msg = IbcMessage::decode(tx_data).map_err(Error::IbcData)?; - match &ibc_msg.0 { - Ics26Envelope::Ics2Msg(ics02_msg) => match ics02_msg { - ClientMsg::CreateClient(msg) => self.create_client(msg), - ClientMsg::UpdateClient(msg) => self.update_client(msg), - ClientMsg::Misbehaviour(_msg) => todo!(), - ClientMsg::UpgradeClient(msg) => self.upgrade_client(msg), - }, - Ics26Envelope::Ics3Msg(ics03_msg) => match ics03_msg { - ConnectionMsg::ConnectionOpenInit(msg) => { - self.init_connection(msg) - } - ConnectionMsg::ConnectionOpenTry(msg) => { - self.try_connection(msg) - } - ConnectionMsg::ConnectionOpenAck(msg) => { - self.ack_connection(msg) - } - ConnectionMsg::ConnectionOpenConfirm(msg) => { - self.confirm_connection(msg) - } - }, - Ics26Envelope::Ics4ChannelMsg(ics04_msg) => match ics04_msg { - ChannelMsg::ChannelOpenInit(msg) => self.init_channel(msg), - ChannelMsg::ChannelOpenTry(msg) => self.try_channel(msg), - ChannelMsg::ChannelOpenAck(msg) => self.ack_channel(msg), - ChannelMsg::ChannelOpenConfirm(msg) => { - self.confirm_channel(msg) - } - ChannelMsg::ChannelCloseInit(msg) => { - self.close_init_channel(msg) - } - ChannelMsg::ChannelCloseConfirm(msg) => { - self.close_confirm_channel(msg) - } - }, - Ics26Envelope::Ics4PacketMsg(ics04_msg) => match ics04_msg { - PacketMsg::AckPacket(msg) => self.acknowledge_packet(msg), - PacketMsg::RecvPacket(msg) => self.receive_packet(msg), - PacketMsg::ToPacket(msg) => self.timeout_packet(msg), - PacketMsg::ToClosePacket(msg) => { - self.timeout_on_close_packet(msg) - } - }, - Ics26Envelope::Ics20Msg(msg) => self.send_token(msg), - } - } - - /// Create a new client - fn create_client( - &mut self, - msg: &MsgCreateAnyClient, - ) -> std::result::Result<(), Self::Error> { - let counter_key = storage::client_counter_key(); - let counter = self.get_and_inc_counter(&counter_key)?; - let client_type = msg.client_state.client_type(); - let client_id = client_id(client_type, counter)?; - // client type - let client_type_key = storage::client_type_key(&client_id); - self.write_ibc_data(&client_type_key, client_type.as_str().as_bytes())?; - // client state - let client_state_key = storage::client_state_key(&client_id); - self.write_ibc_data( - &client_state_key, - msg.client_state - .encode_vec() - .expect("encoding shouldn't fail"), - )?; - // consensus state - let height = msg.client_state.latest_height(); - let consensus_state_key = - storage::consensus_state_key(&client_id, height); - self.write_ibc_data( - &consensus_state_key, - msg.consensus_state - .encode_vec() - .expect("encoding shouldn't fail"), - )?; - - self.set_client_update_time(&client_id)?; - - let event = make_create_client_event(&client_id, msg) - .try_into() - .unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Update a client - fn update_client( - &mut self, - msg: &MsgUpdateAnyClient, - ) -> std::result::Result<(), Self::Error> { - // get and update the client - let client_id = msg.client_id.clone(); - let client_state_key = storage::client_state_key(&client_id); - let value = - self.read_ibc_data(&client_state_key)?.ok_or_else(|| { - Error::Client(format!( - "The client to be updated doesn't exist: ID {}", - client_id - )) - })?; - let client_state = - AnyClientState::decode_vec(&value).map_err(Error::Decoding)?; - let (new_client_state, new_consensus_state) = - update_client(client_state, msg.header.clone())?; - - let height = new_client_state.latest_height(); - self.write_ibc_data( - &client_state_key, - new_client_state - .encode_vec() - .expect("encoding shouldn't fail"), - )?; - let consensus_state_key = - storage::consensus_state_key(&client_id, height); - self.write_ibc_data( - &consensus_state_key, - new_consensus_state - .encode_vec() - .expect("encoding shouldn't fail"), - )?; - - self.set_client_update_time(&client_id)?; - - let event = make_update_client_event(&client_id, msg) - .try_into() - .unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Upgrade a client - fn upgrade_client( - &mut self, - msg: &MsgUpgradeAnyClient, - ) -> std::result::Result<(), Self::Error> { - let client_state_key = storage::client_state_key(&msg.client_id); - let height = msg.client_state.latest_height(); - let consensus_state_key = - storage::consensus_state_key(&msg.client_id, height); - self.write_ibc_data( - &client_state_key, - msg.client_state - .encode_vec() - .expect("encoding shouldn't fail"), - )?; - self.write_ibc_data( - &consensus_state_key, - msg.consensus_state - .encode_vec() - .expect("encoding shouldn't fail"), - )?; - - self.set_client_update_time(&msg.client_id)?; - - let event = make_upgrade_client_event(&msg.client_id, msg) - .try_into() - .unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Initialize a connection for ConnectionOpenInit - fn init_connection( - &mut self, - msg: &MsgConnectionOpenInit, - ) -> std::result::Result<(), Self::Error> { - let counter_key = storage::connection_counter_key(); - let counter = self.get_and_inc_counter(&counter_key)?; - // new connection - let conn_id = connection_id(counter); - let conn_key = storage::connection_key(&conn_id); - let connection = init_connection(msg); - self.write_ibc_data( - &conn_key, - connection.encode_vec().expect("encoding shouldn't fail"), - )?; - - let event = make_open_init_connection_event(&conn_id, msg) - .try_into() - .unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Initialize a connection for ConnectionOpenTry - fn try_connection( - &mut self, - msg: &MsgConnectionOpenTry, - ) -> std::result::Result<(), Self::Error> { - let counter_key = storage::connection_counter_key(); - let counter = self.get_and_inc_counter(&counter_key)?; - // new connection - let conn_id = connection_id(counter); - let conn_key = storage::connection_key(&conn_id); - let connection = try_connection(msg); - self.write_ibc_data( - &conn_key, - connection.encode_vec().expect("encoding shouldn't fail"), - )?; - - let event = make_open_try_connection_event(&conn_id, msg) - .try_into() - .unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Open the connection for ConnectionOpenAck - fn ack_connection( - &mut self, - msg: &MsgConnectionOpenAck, - ) -> std::result::Result<(), Self::Error> { - let conn_key = storage::connection_key(&msg.connection_id); - let value = self.read_ibc_data(&conn_key)?.ok_or_else(|| { - Error::Connection(format!( - "The connection to be opened doesn't exist: ID {}", - msg.connection_id - )) - })?; - let mut connection = - ConnectionEnd::decode_vec(&value).map_err(Error::Decoding)?; - open_connection(&mut connection); - let mut counterparty = connection.counterparty().clone(); - counterparty.connection_id = - Some(msg.counterparty_connection_id.clone()); - connection.set_counterparty(counterparty); - self.write_ibc_data( - &conn_key, - connection.encode_vec().expect("encoding shouldn't fail"), - )?; - - let event = make_open_ack_connection_event(msg).try_into().unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Open the connection for ConnectionOpenConfirm - fn confirm_connection( - &mut self, - msg: &MsgConnectionOpenConfirm, - ) -> std::result::Result<(), Self::Error> { - let conn_key = storage::connection_key(&msg.connection_id); - let value = self.read_ibc_data(&conn_key)?.ok_or_else(|| { - Error::Connection(format!( - "The connection to be opend doesn't exist: ID {}", - msg.connection_id - )) - })?; - let mut connection = - ConnectionEnd::decode_vec(&value).map_err(Error::Decoding)?; - open_connection(&mut connection); - self.write_ibc_data( - &conn_key, - connection.encode_vec().expect("encoding shouldn't fail"), - )?; - - let event = make_open_confirm_connection_event(msg).try_into().unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Initialize a channel for ChannelOpenInit - fn init_channel( - &mut self, - msg: &MsgChannelOpenInit, - ) -> std::result::Result<(), Self::Error> { - self.bind_port(&msg.port_id)?; - let counter_key = storage::channel_counter_key(); - let counter = self.get_and_inc_counter(&counter_key)?; - let channel_id = channel_id(counter); - let port_channel_id = port_channel_id(msg.port_id.clone(), channel_id); - let channel_key = storage::channel_key(&port_channel_id); - self.write_ibc_data( - &channel_key, - msg.channel.encode_vec().expect("encoding shouldn't fail"), - )?; - - let event = make_open_init_channel_event(&channel_id, msg) - .try_into() - .unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Initialize a channel for ChannelOpenTry - fn try_channel( - &mut self, - msg: &MsgChannelOpenTry, - ) -> std::result::Result<(), Self::Error> { - self.bind_port(&msg.port_id)?; - let counter_key = storage::channel_counter_key(); - let counter = self.get_and_inc_counter(&counter_key)?; - let channel_id = channel_id(counter); - let port_channel_id = port_channel_id(msg.port_id.clone(), channel_id); - let channel_key = storage::channel_key(&port_channel_id); - self.write_ibc_data( - &channel_key, - msg.channel.encode_vec().expect("encoding shouldn't fail"), - )?; - - let event = make_open_try_channel_event(&channel_id, msg) - .try_into() - .unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Open the channel for ChannelOpenAck - fn ack_channel( - &mut self, - msg: &MsgChannelOpenAck, - ) -> std::result::Result<(), Self::Error> { - let port_channel_id = - port_channel_id(msg.port_id.clone(), msg.channel_id); - let channel_key = storage::channel_key(&port_channel_id); - let value = self.read_ibc_data(&channel_key)?.ok_or_else(|| { - Error::Channel(format!( - "The channel to be opened doesn't exist: Port/Channel {}", - port_channel_id - )) - })?; - let mut channel = - ChannelEnd::decode_vec(&value).map_err(Error::Decoding)?; - channel.set_counterparty_channel_id(msg.counterparty_channel_id); - open_channel(&mut channel); - self.write_ibc_data( - &channel_key, - channel.encode_vec().expect("encoding shouldn't fail"), - )?; - - let event = make_open_ack_channel_event(msg, &channel)? - .try_into() - .unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Open the channel for ChannelOpenConfirm - fn confirm_channel( - &mut self, - msg: &MsgChannelOpenConfirm, - ) -> std::result::Result<(), Self::Error> { - let port_channel_id = - port_channel_id(msg.port_id.clone(), msg.channel_id); - let channel_key = storage::channel_key(&port_channel_id); - let value = self.read_ibc_data(&channel_key)?.ok_or_else(|| { - Error::Channel(format!( - "The channel to be opened doesn't exist: Port/Channel {}", - port_channel_id - )) - })?; - let mut channel = - ChannelEnd::decode_vec(&value).map_err(Error::Decoding)?; - open_channel(&mut channel); - self.write_ibc_data( - &channel_key, - channel.encode_vec().expect("encoding shouldn't fail"), - )?; - - let event = make_open_confirm_channel_event(msg, &channel)? - .try_into() - .unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Close the channel for ChannelCloseInit - fn close_init_channel( - &mut self, - msg: &MsgChannelCloseInit, - ) -> std::result::Result<(), Self::Error> { - let port_channel_id = - port_channel_id(msg.port_id.clone(), msg.channel_id); - let channel_key = storage::channel_key(&port_channel_id); - let value = self.read_ibc_data(&channel_key)?.ok_or_else(|| { - Error::Channel(format!( - "The channel to be closed doesn't exist: Port/Channel {}", - port_channel_id - )) - })?; - let mut channel = - ChannelEnd::decode_vec(&value).map_err(Error::Decoding)?; - close_channel(&mut channel); - self.write_ibc_data( - &channel_key, - channel.encode_vec().expect("encoding shouldn't fail"), - )?; - - let event = make_close_init_channel_event(msg, &channel)? - .try_into() - .unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Close the channel for ChannelCloseConfirm - fn close_confirm_channel( - &mut self, - msg: &MsgChannelCloseConfirm, - ) -> std::result::Result<(), Self::Error> { - let port_channel_id = - port_channel_id(msg.port_id.clone(), msg.channel_id); - let channel_key = storage::channel_key(&port_channel_id); - let value = self.read_ibc_data(&channel_key)?.ok_or_else(|| { - Error::Channel(format!( - "The channel to be closed doesn't exist: Port/Channel {}", - port_channel_id - )) - })?; - let mut channel = - ChannelEnd::decode_vec(&value).map_err(Error::Decoding)?; - close_channel(&mut channel); - self.write_ibc_data( - &channel_key, - channel.encode_vec().expect("encoding shouldn't fail"), - )?; - - let event = make_close_confirm_channel_event(msg, &channel)? - .try_into() - .unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Send a packet - fn send_packet( - &mut self, - port_channel_id: PortChannelId, - data: Vec, - timeout_height: Height, - timeout_timestamp: Timestamp, - ) -> std::result::Result<(), Self::Error> { - // get and increment the next sequence send - let seq_key = storage::next_sequence_send_key(&port_channel_id); - let sequence = self.get_and_inc_sequence(&seq_key)?; - - // get the channel for the destination info. - let channel_key = storage::channel_key(&port_channel_id); - let channel = self - .read_ibc_data(&channel_key)? - .expect("cannot get the channel to be closed"); - let channel = - ChannelEnd::decode_vec(&channel).expect("cannot get the channel"); - let counterparty = channel.counterparty(); - - // make a packet - let packet = Packet { - sequence, - source_port: port_channel_id.port_id.clone(), - source_channel: port_channel_id.channel_id, - destination_port: counterparty.port_id.clone(), - destination_channel: *counterparty - .channel_id() - .expect("the counterparty channel should exist"), - data, - timeout_height, - timeout_timestamp, - }; - // store the commitment of the packet - let commitment_key = storage::commitment_key( - &port_channel_id.port_id, - &port_channel_id.channel_id, - packet.sequence, - ); - let commitment = commitment(&packet); - self.write_ibc_data(&commitment_key, commitment.into_vec())?; - - let event = make_send_packet_event(packet).try_into().unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Receive a packet - fn receive_packet( - &mut self, - msg: &MsgRecvPacket, - ) -> std::result::Result<(), Self::Error> { - // check the packet data - let packet_ack = - if let Ok(data) = serde_json::from_slice(&msg.packet.data) { - match self.receive_token(&msg.packet, &data) { - Ok(_) => PacketAck::result_success(), - Err(_) => PacketAck::result_error( - "receiving a token failed".to_string(), - ), - } - } else { - PacketAck::result_error("unknown packet data".to_string()) - }; - - // store the receipt - let receipt_key = storage::receipt_key( - &msg.packet.destination_port, - &msg.packet.destination_channel, - msg.packet.sequence, - ); - self.write_ibc_data(&receipt_key, PacketReceipt::default().as_bytes())?; - - // store the ack - let ack_key = storage::ack_key( - &msg.packet.destination_port, - &msg.packet.destination_channel, - msg.packet.sequence, - ); - let ack = packet_ack.encode_to_vec(); - let ack_commitment = sha2::Sha256::digest(&ack).to_vec(); - self.write_ibc_data(&ack_key, ack_commitment)?; - - // increment the next sequence receive - let port_channel_id = port_channel_id( - msg.packet.destination_port.clone(), - msg.packet.destination_channel, - ); - let seq_key = storage::next_sequence_recv_key(&port_channel_id); - self.get_and_inc_sequence(&seq_key)?; - - let event = make_write_ack_event(msg.packet.clone(), ack) - .try_into() - .unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Receive a acknowledgement - fn acknowledge_packet( - &mut self, - msg: &MsgAcknowledgement, - ) -> std::result::Result<(), Self::Error> { - let ack = PacketAck::try_from(msg.acknowledgement.clone()) - .map_err(Error::IbcData)?; - if !ack.is_success() { - if let Ok(data) = serde_json::from_slice(&msg.packet.data) { - self.refund_token(&msg.packet, &data)?; - } - } - - let commitment_key = storage::commitment_key( - &msg.packet.source_port, - &msg.packet.source_channel, - msg.packet.sequence, - ); - self.delete_ibc_data(&commitment_key)?; - - // get and increment the next sequence ack - let port_channel_id = port_channel_id( - msg.packet.source_port.clone(), - msg.packet.source_channel, - ); - let seq_key = storage::next_sequence_ack_key(&port_channel_id); - self.get_and_inc_sequence(&seq_key)?; - - let event = make_ack_event(msg.packet.clone()).try_into().unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Receive a timeout - fn timeout_packet( - &mut self, - msg: &MsgTimeout, - ) -> std::result::Result<(), Self::Error> { - // check the packet data - if let Ok(data) = serde_json::from_slice(&msg.packet.data) { - self.refund_token(&msg.packet, &data)?; - } - - // delete the commitment of the packet - let commitment_key = storage::commitment_key( - &msg.packet.source_port, - &msg.packet.source_channel, - msg.packet.sequence, - ); - self.delete_ibc_data(&commitment_key)?; - - // close the channel - let port_channel_id = port_channel_id( - msg.packet.source_port.clone(), - msg.packet.source_channel, - ); - let channel_key = storage::channel_key(&port_channel_id); - let value = self.read_ibc_data(&channel_key)?.ok_or_else(|| { - Error::Channel(format!( - "The channel to be closed doesn't exist: Port/Channel {}", - port_channel_id - )) - })?; - let mut channel = - ChannelEnd::decode_vec(&value).map_err(Error::Decoding)?; - if channel.order_matches(&Order::Ordered) { - close_channel(&mut channel); - self.write_ibc_data( - &channel_key, - channel.encode_vec().expect("encoding shouldn't fail"), - )?; - } - - let event = make_timeout_event(msg.packet.clone()).try_into().unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Receive a timeout for TimeoutOnClose - fn timeout_on_close_packet( - &mut self, - msg: &MsgTimeoutOnClose, - ) -> std::result::Result<(), Self::Error> { - // check the packet data - if let Ok(data) = serde_json::from_slice(&msg.packet.data) { - self.refund_token(&msg.packet, &data)?; - } - - // delete the commitment of the packet - let commitment_key = storage::commitment_key( - &msg.packet.source_port, - &msg.packet.source_channel, - msg.packet.sequence, - ); - self.delete_ibc_data(&commitment_key)?; - - // close the channel - let port_channel_id = port_channel_id( - msg.packet.source_port.clone(), - msg.packet.source_channel, - ); - let channel_key = storage::channel_key(&port_channel_id); - let value = self.read_ibc_data(&channel_key)?.ok_or_else(|| { - Error::Channel(format!( - "The channel to be closed doesn't exist: Port/Channel {}", - port_channel_id - )) - })?; - let mut channel = - ChannelEnd::decode_vec(&value).map_err(Error::Decoding)?; - if channel.order_matches(&Order::Ordered) { - close_channel(&mut channel); - self.write_ibc_data( - &channel_key, - channel.encode_vec().expect("encoding shouldn't fail"), - )?; - } - - Ok(()) - } - - /// Set the timestamp and the height for the client update - fn set_client_update_time( - &mut self, - client_id: &ClientId, - ) -> std::result::Result<(), Self::Error> { - let time = Time::parse_from_rfc3339(&self.get_header_time()?.0) - .map_err(|e| { - Error::Time(format!("The time of the header is invalid: {}", e)) - })?; - let key = storage::client_update_timestamp_key(client_id); - self.write_ibc_data( - &key, - time.encode_vec().expect("encoding shouldn't fail"), - )?; - - // the revision number is always 0 - let height = Height::new(0, self.get_height()?.0); - let height_key = storage::client_update_height_key(client_id); - // write the current height as u64 - self.write_ibc_data( - &height_key, - height.encode_vec().expect("Encoding shouldn't fail"), - )?; - - Ok(()) - } - - /// Get and increment the counter - fn get_and_inc_counter( - &mut self, - key: &Key, - ) -> std::result::Result { - let value = self.read_ibc_data(key)?.ok_or_else(|| { - Error::Counter(format!("The counter doesn't exist: {}", key)) - })?; - let value: [u8; 8] = value.try_into().map_err(|_| { - Error::Counter(format!("The counter value wasn't u64: Key {}", key)) - })?; - let counter = u64::from_be_bytes(value); - self.write_ibc_data(key, (counter + 1).to_be_bytes())?; - Ok(counter) - } - - /// Get and increment the sequence - fn get_and_inc_sequence( - &mut self, - key: &Key, - ) -> std::result::Result { - let index = match self.read_ibc_data(key)? { - Some(v) => { - let index: [u8; 8] = v.try_into().map_err(|_| { - Error::Sequence(format!( - "The sequence index wasn't u64: Key {}", - key - )) - })?; - u64::from_be_bytes(index) - } - // when the sequence has never been used, returns the initial value - None => 1, - }; - self.write_ibc_data(key, (index + 1).to_be_bytes())?; - Ok(index.into()) - } - - /// Bind a new port - fn bind_port( - &mut self, - port_id: &PortId, - ) -> std::result::Result<(), Self::Error> { - let port_key = storage::port_key(port_id); - match self.read_ibc_data(&port_key)? { - Some(_) => {} - None => { - // create a new capability and claim it - let index_key = storage::capability_index_key(); - let cap_index = self.get_and_inc_counter(&index_key)?; - self.write_ibc_data(&port_key, cap_index.to_be_bytes())?; - let cap_key = storage::capability_key(cap_index); - self.write_ibc_data(&cap_key, port_id.as_bytes())?; - } - } - Ok(()) - } - - /// Send the specified token by escrowing or burning - fn send_token( - &mut self, - msg: &MsgTransfer, - ) -> std::result::Result<(), Self::Error> { - let mut data = FungibleTokenPacketData::from(msg.clone()); - if let Some(hash) = storage::token_hash_from_denom(&data.denom) - .map_err(Error::IbcStorage)? - { - let denom_key = storage::ibc_denom_key(hash); - let denom_bytes = - self.read_ibc_data(&denom_key)?.ok_or_else(|| { - Error::SendingToken(format!( - "No original denom: denom_key {}", - denom_key - )) - })?; - let denom = std::str::from_utf8(&denom_bytes).map_err(|e| { - Error::SendingToken(format!( - "Decoding the denom failed: denom_key {}, error {}", - denom_key, e - )) - })?; - data.denom = denom.to_string(); - } - let token = storage::token(&data.denom).map_err(Error::IbcStorage)?; - let amount = Amount::from_str(&data.amount, 0).map_err(|e| { - Error::SendingToken(format!( - "Invalid amount: amount {}, error {}", - data.amount, e - )) - })?; - - let source_addr = Address::decode(&data.sender).map_err(|e| { - Error::SendingToken(format!( - "Invalid sender address: sender {}, error {}", - data.sender, e - )) - })?; - - // check the denomination field - let prefix = format!( - "{}/{}/", - msg.source_port.clone(), - msg.source_channel.clone() - ); - let (source, target) = if data.denom.starts_with(&prefix) { - // the receiver's chain was the source - // transfer from the origin-specific account of the token - let key_prefix = storage::ibc_token_prefix(&data.denom) - .map_err(Error::IbcStorage)?; - let src = token::multitoken_balance_key(&key_prefix, &source_addr); - - let key_prefix = storage::ibc_account_prefix( - &msg.source_port, - &msg.source_channel, - &token, - ); - let burn = token::multitoken_balance_key( - &key_prefix, - &Address::Internal(InternalAddress::IbcBurn), - ); - (src, burn) - } else { - // this chain is the source - // escrow the amount of the token - let src = if data.denom == token.to_string() { - token::balance_key(&token, &source_addr) - } else { - let key_prefix = storage::ibc_token_prefix(&data.denom) - .map_err(Error::IbcStorage)?; - token::multitoken_balance_key(&key_prefix, &source_addr) - }; - - let key_prefix = storage::ibc_account_prefix( - &msg.source_port, - &msg.source_channel, - &token, - ); - let escrow = token::multitoken_balance_key( - &key_prefix, - &Address::Internal(InternalAddress::IbcEscrow), - ); - (src, escrow) - }; - self.transfer_token(&source, &target, amount)?; - - // send a packet - let port_channel_id = - port_channel_id(msg.source_port.clone(), msg.source_channel); - let packet_data = serde_json::to_vec(&data) - .expect("encoding the packet data shouldn't fail"); - self.send_packet( - port_channel_id, - packet_data, - msg.timeout_height, - msg.timeout_timestamp, - ) - } - - /// Receive the specified token by unescrowing or minting - fn receive_token( - &mut self, - packet: &Packet, - data: &FungibleTokenPacketData, - ) -> std::result::Result<(), Self::Error> { - let token = storage::token(&data.denom).map_err(Error::IbcStorage)?; - let amount = Amount::from_str(&data.amount, 0).map_err(|e| { - Error::ReceivingToken(format!( - "Invalid amount: amount {}, error {}", - data.amount, e - )) - })?; - // The receiver should be an address because the origin-specific account - // key should be assigned internally - let dest_addr = Address::decode(&data.receiver).map_err(|e| { - Error::ReceivingToken(format!( - "Invalid receiver address: receiver {}, error {}", - data.receiver, e - )) - })?; - - let prefix = format!( - "{}/{}/", - packet.source_port.clone(), - packet.source_channel.clone() - ); - let (source, target) = match data.denom.strip_prefix(&prefix) { - Some(denom) => { - // unescrow the token because this chain was the source - let escrow_prefix = storage::ibc_account_prefix( - &packet.destination_port, - &packet.destination_channel, - &token, - ); - let escrow = token::multitoken_balance_key( - &escrow_prefix, - &Address::Internal(InternalAddress::IbcEscrow), - ); - let dest = if denom == token.to_string() { - token::balance_key(&token, &dest_addr) - } else { - let key_prefix = storage::ibc_token_prefix(denom) - .map_err(Error::IbcStorage)?; - token::multitoken_balance_key(&key_prefix, &dest_addr) - }; - (escrow, dest) - } - None => { - // mint the token because the sender chain is the source - let key_prefix = storage::ibc_account_prefix( - &packet.destination_port, - &packet.destination_channel, - &token, - ); - let mint = token::multitoken_balance_key( - &key_prefix, - &Address::Internal(InternalAddress::IbcMint), - ); - - // prefix the denom with the this chain port and channel - let denom = format!( - "{}/{}/{}", - &packet.destination_port, - &packet.destination_channel, - &data.denom - ); - let key_prefix = storage::ibc_token_prefix(&denom) - .map_err(Error::IbcStorage)?; - let dest = - token::multitoken_balance_key(&key_prefix, &dest_addr); - - // store the prefixed denom - let token_hash = storage::calc_hash(&denom); - let denom_key = storage::ibc_denom_key(token_hash); - self.write_ibc_data(&denom_key, denom.as_bytes())?; - - (mint, dest) - } - }; - self.transfer_token(&source, &target, amount)?; - - Ok(()) - } - - /// Refund the specified token by unescrowing or minting - fn refund_token( - &mut self, - packet: &Packet, - data: &FungibleTokenPacketData, - ) -> std::result::Result<(), Self::Error> { - let token = storage::token(&data.denom).map_err(Error::IbcStorage)?; - let amount = Amount::from_str(&data.amount, 0).map_err(|e| { - Error::ReceivingToken(format!( - "Invalid amount: amount {}, error {}", - data.amount, e - )) - })?; - - let dest_addr = Address::decode(&data.sender).map_err(|e| { - Error::SendingToken(format!( - "Invalid sender address: sender {}, error {}", - data.sender, e - )) - })?; - - let prefix = format!( - "{}/{}/", - packet.source_port.clone(), - packet.source_channel.clone() - ); - let (source, target) = if data.denom.starts_with(&prefix) { - // mint the token because the amount was burned - let key_prefix = storage::ibc_account_prefix( - &packet.source_port, - &packet.source_channel, - &token, - ); - let mint = token::multitoken_balance_key( - &key_prefix, - &Address::Internal(InternalAddress::IbcMint), - ); - let key_prefix = storage::ibc_token_prefix(&data.denom) - .map_err(Error::IbcStorage)?; - let dest = token::multitoken_balance_key(&key_prefix, &dest_addr); - (mint, dest) - } else { - // unescrow the token because the acount was escrowed - let dest = if data.denom == token.to_string() { - token::balance_key(&token, &dest_addr) - } else { - let key_prefix = storage::ibc_token_prefix(&data.denom) - .map_err(Error::IbcStorage)?; - token::multitoken_balance_key(&key_prefix, &dest_addr) - }; - - let key_prefix = storage::ibc_account_prefix( - &packet.source_port, - &packet.source_channel, - &token, - ); - let escrow = token::multitoken_balance_key( - &key_prefix, - &Address::Internal(InternalAddress::IbcEscrow), - ); - (escrow, dest) - }; - self.transfer_token(&source, &target, amount)?; - - Ok(()) - } -} - -/// Update a client with the given state and headers -pub fn update_client( - client_state: AnyClientState, - header: AnyHeader, -) -> Result<(AnyClientState, AnyConsensusState)> { - match client_state { - AnyClientState::Tendermint(cs) => match header { - AnyHeader::Tendermint(h) => { - let new_client_state = cs.with_header(h.clone()).wrap_any(); - let new_consensus_state = TmConsensusState::from(h).wrap_any(); - Ok((new_client_state, new_consensus_state)) - } - #[cfg(any(feature = "ibc-mocks-abcipp", feature = "ibc-mocks"))] - _ => Err(Error::ClientUpdate( - "The header type is mismatched".to_owned(), - )), - }, - #[cfg(any(feature = "ibc-mocks-abcipp", feature = "ibc-mocks"))] - AnyClientState::Mock(_) => match header { - AnyHeader::Mock(h) => Ok(( - MockClientState::new(h).wrap_any(), - MockConsensusState::new(h).wrap_any(), - )), - _ => Err(Error::ClientUpdate( - "The header type is mismatched".to_owned(), - )), - }, - } -} - -/// Returns a new client ID -pub fn client_id(client_type: ClientType, counter: u64) -> Result { - ClientId::new(client_type, counter).map_err(Error::ClientId) -} - -/// Returns a new connection ID -pub fn connection_id(counter: u64) -> ConnectionId { - ConnectionId::new(counter) -} - -/// Make a connection end from the init message -pub fn init_connection(msg: &MsgConnectionOpenInit) -> ConnectionEnd { - ConnectionEnd::new( - ConnState::Init, - msg.client_id.clone(), - msg.counterparty.clone(), - vec![msg.version.clone().unwrap_or_default()], - msg.delay_period, - ) -} - -/// Make a connection end from the try message -pub fn try_connection(msg: &MsgConnectionOpenTry) -> ConnectionEnd { - ConnectionEnd::new( - ConnState::TryOpen, - msg.client_id.clone(), - msg.counterparty.clone(), - msg.counterparty_versions.clone(), - msg.delay_period, - ) -} - -/// Open the connection -pub fn open_connection(conn: &mut ConnectionEnd) { - conn.set_state(ConnState::Open); -} - -/// Returns a new channel ID -pub fn channel_id(counter: u64) -> ChannelId { - ChannelId::new(counter) -} - -/// Open the channel -pub fn open_channel(channel: &mut ChannelEnd) { - channel.set_state(ChanState::Open); -} - -/// Close the channel -pub fn close_channel(channel: &mut ChannelEnd) { - channel.set_state(ChanState::Closed); -} - -/// Returns a port ID -pub fn port_id(id: &str) -> Result { - PortId::from_str(id).map_err(Error::PortId) -} - -/// Returns a pair of port ID and channel ID -pub fn port_channel_id( - port_id: PortId, - channel_id: ChannelId, -) -> PortChannelId { - PortChannelId { - port_id, - channel_id, - } -} - -/// Returns a sequence -pub fn sequence(index: u64) -> Sequence { - Sequence::from(index) -} - -/// Make a packet from MsgTransfer -pub fn packet_from_message( - msg: &MsgTransfer, - sequence: Sequence, - counterparty: &ChanCounterparty, -) -> Packet { - Packet { - sequence, - source_port: msg.source_port.clone(), - source_channel: msg.source_channel, - destination_port: counterparty.port_id.clone(), - destination_channel: *counterparty - .channel_id() - .expect("the counterparty channel should exist"), - data: serde_json::to_vec(&FungibleTokenPacketData::from(msg.clone())) - .expect("encoding the packet data shouldn't fail"), - timeout_height: msg.timeout_height, - timeout_timestamp: msg.timeout_timestamp, - } -} - -/// Returns a commitment from the given packet -pub fn commitment(packet: &Packet) -> PacketCommitment { - let timeout = packet.timeout_timestamp.nanoseconds().to_be_bytes(); - let revision_number = packet.timeout_height.revision_number.to_be_bytes(); - let revision_height = packet.timeout_height.revision_height.to_be_bytes(); - let data = sha2::Sha256::digest(&packet.data); - let input = [ - &timeout, - &revision_number, - &revision_height, - data.as_slice(), - ] - .concat(); - sha2::Sha256::digest(&input).to_vec().into() -} - -/// Returns a counterparty of a connection -pub fn connection_counterparty( - client_id: ClientId, - conn_id: ConnectionId, -) -> ConnCounterparty { - ConnCounterparty::new(client_id, Some(conn_id), commitment_prefix()) -} - -/// Returns a counterparty of a channel -pub fn channel_counterparty( - port_id: PortId, - channel_id: ChannelId, -) -> ChanCounterparty { - ChanCounterparty::new(port_id, Some(channel_id)) -} - -/// Returns Namada commitment prefix -pub fn commitment_prefix() -> CommitmentPrefix { - CommitmentPrefix::try_from(COMMITMENT_PREFIX.to_vec()) - .expect("the conversion shouldn't fail") -} - -/// Makes CreateClient event -pub fn make_create_client_event( - client_id: &ClientId, - msg: &MsgCreateAnyClient, -) -> IbcEvent { - let attributes = ClientAttributes { - client_id: client_id.clone(), - client_type: msg.client_state.client_type(), - consensus_height: msg.client_state.latest_height(), - ..Default::default() - }; - IbcEvent::CreateClient(CreateClient::from(attributes)) -} - -/// Makes UpdateClient event -pub fn make_update_client_event( - client_id: &ClientId, - msg: &MsgUpdateAnyClient, -) -> IbcEvent { - let attributes = ClientAttributes { - client_id: client_id.clone(), - client_type: msg.header.client_type(), - consensus_height: msg.header.height(), - ..Default::default() - }; - IbcEvent::UpdateClient(UpdateClient::from(attributes)) -} - -/// Makes UpgradeClient event -pub fn make_upgrade_client_event( - client_id: &ClientId, - msg: &MsgUpgradeAnyClient, -) -> IbcEvent { - let attributes = ClientAttributes { - client_id: client_id.clone(), - client_type: msg.client_state.client_type(), - consensus_height: msg.client_state.latest_height(), - ..Default::default() - }; - IbcEvent::UpgradeClient(UpgradeClient::from(attributes)) -} - -/// Makes OpenInitConnection event -pub fn make_open_init_connection_event( - conn_id: &ConnectionId, - msg: &MsgConnectionOpenInit, -) -> IbcEvent { - let attributes = ConnectionAttributes { - connection_id: Some(conn_id.clone()), - client_id: msg.client_id.clone(), - counterparty_connection_id: msg.counterparty.connection_id().cloned(), - counterparty_client_id: msg.counterparty.client_id().clone(), - ..Default::default() - }; - ConnOpenInit::from(attributes).into() -} - -/// Makes OpenTryConnection event -pub fn make_open_try_connection_event( - conn_id: &ConnectionId, - msg: &MsgConnectionOpenTry, -) -> IbcEvent { - let attributes = ConnectionAttributes { - connection_id: Some(conn_id.clone()), - client_id: msg.client_id.clone(), - counterparty_connection_id: msg.counterparty.connection_id().cloned(), - counterparty_client_id: msg.counterparty.client_id().clone(), - ..Default::default() - }; - ConnOpenTry::from(attributes).into() -} - -/// Makes OpenAckConnection event -pub fn make_open_ack_connection_event(msg: &MsgConnectionOpenAck) -> IbcEvent { - let attributes = ConnectionAttributes { - connection_id: Some(msg.connection_id.clone()), - counterparty_connection_id: Some( - msg.counterparty_connection_id.clone(), - ), - ..Default::default() - }; - ConnOpenAck::from(attributes).into() -} - -/// Makes OpenConfirmConnection event -pub fn make_open_confirm_connection_event( - msg: &MsgConnectionOpenConfirm, -) -> IbcEvent { - let attributes = ConnectionAttributes { - connection_id: Some(msg.connection_id.clone()), - ..Default::default() - }; - ConnOpenConfirm::from(attributes).into() -} - -/// Makes OpenInitChannel event -pub fn make_open_init_channel_event( - channel_id: &ChannelId, - msg: &MsgChannelOpenInit, -) -> IbcEvent { - let connection_id = match msg.channel.connection_hops().get(0) { - Some(c) => c.clone(), - None => ConnectionId::default(), - }; - let attributes = ChanOpenInit { - height: Height::default(), - port_id: msg.port_id.clone(), - channel_id: Some(*channel_id), - connection_id, - counterparty_port_id: msg.channel.counterparty().port_id().clone(), - counterparty_channel_id: msg - .channel - .counterparty() - .channel_id() - .cloned(), - }; - attributes.into() -} - -/// Makes OpenTryChannel event -pub fn make_open_try_channel_event( - channel_id: &ChannelId, - msg: &MsgChannelOpenTry, -) -> IbcEvent { - let connection_id = match msg.channel.connection_hops().get(0) { - Some(c) => c.clone(), - None => ConnectionId::default(), - }; - let attributes = ChanOpenTry { - height: Height::default(), - port_id: msg.port_id.clone(), - channel_id: Some(*channel_id), - connection_id, - counterparty_port_id: msg.channel.counterparty().port_id().clone(), - counterparty_channel_id: msg - .channel - .counterparty() - .channel_id() - .cloned(), - }; - attributes.into() -} - -/// Makes OpenAckChannel event -pub fn make_open_ack_channel_event( - msg: &MsgChannelOpenAck, - channel: &ChannelEnd, -) -> Result { - let conn_id = get_connection_id_from_channel(channel)?; - let counterparty = channel.counterparty(); - let attributes = ChanOpenAck { - height: Height::default(), - port_id: msg.port_id.clone(), - channel_id: Some(msg.channel_id), - counterparty_channel_id: Some(msg.counterparty_channel_id), - connection_id: conn_id.clone(), - counterparty_port_id: counterparty.port_id().clone(), - }; - Ok(attributes.into()) -} - -/// Makes OpenConfirmChannel event -pub fn make_open_confirm_channel_event( - msg: &MsgChannelOpenConfirm, - channel: &ChannelEnd, -) -> Result { - let conn_id = get_connection_id_from_channel(channel)?; - let counterparty = channel.counterparty(); - let attributes = ChanOpenConfirm { - height: Height::default(), - port_id: msg.port_id.clone(), - channel_id: Some(msg.channel_id), - connection_id: conn_id.clone(), - counterparty_port_id: counterparty.port_id().clone(), - counterparty_channel_id: counterparty.channel_id().cloned(), - }; - Ok(attributes.into()) -} - -/// Makes CloseInitChannel event -pub fn make_close_init_channel_event( - msg: &MsgChannelCloseInit, - channel: &ChannelEnd, -) -> Result { - let conn_id = get_connection_id_from_channel(channel)?; - let counterparty = channel.counterparty(); - let attributes = ChanCloseInit { - height: Height::default(), - port_id: msg.port_id.clone(), - channel_id: msg.channel_id, - connection_id: conn_id.clone(), - counterparty_port_id: counterparty.port_id().clone(), - counterparty_channel_id: counterparty.channel_id().cloned(), - }; - Ok(attributes.into()) -} - -/// Makes CloseConfirmChannel event -pub fn make_close_confirm_channel_event( - msg: &MsgChannelCloseConfirm, - channel: &ChannelEnd, -) -> Result { - let conn_id = get_connection_id_from_channel(channel)?; - let counterparty = channel.counterparty(); - let attributes = ChanCloseConfirm { - height: Height::default(), - port_id: msg.port_id.clone(), - channel_id: Some(msg.channel_id), - connection_id: conn_id.clone(), - counterparty_port_id: counterparty.port_id.clone(), - counterparty_channel_id: counterparty.channel_id().cloned(), - }; - Ok(attributes.into()) -} - -fn get_connection_id_from_channel( - channel: &ChannelEnd, -) -> Result<&ConnectionId> { - channel.connection_hops().get(0).ok_or_else(|| { - Error::Channel("No connection for the channel".to_owned()) - }) -} - -/// Makes SendPacket event -pub fn make_send_packet_event(packet: Packet) -> IbcEvent { - IbcEvent::SendPacket(SendPacket { - height: packet.timeout_height, - packet, - }) -} - -/// Makes WriteAcknowledgement event -pub fn make_write_ack_event(packet: Packet, ack: Vec) -> IbcEvent { - IbcEvent::WriteAcknowledgement(WriteAcknowledgement { - // this height is not used - height: Height::default(), - packet, - ack, - }) -} - -/// Makes AcknowledgePacket event -pub fn make_ack_event(packet: Packet) -> IbcEvent { - IbcEvent::AcknowledgePacket(AcknowledgePacket { - // this height is not used - height: Height::default(), - packet, - }) -} - -/// Makes TimeoutPacket event -pub fn make_timeout_event(packet: Packet) -> IbcEvent { - IbcEvent::TimeoutPacket(TimeoutPacket { - // this height is not used - height: Height::default(), - packet, - }) -} diff --git a/core/src/ledger/ibc/context/common.rs b/core/src/ledger/ibc/context/common.rs index 1d03ffbeec..4c96c034b5 100644 --- a/core/src/ledger/ibc/context/common.rs +++ b/core/src/ledger/ibc/context/common.rs @@ -1,5 +1,6 @@ //! IbcCommonContext implementation for IBC +use borsh::BorshSerialize; use prost::Message; use sha2::Digest; @@ -363,7 +364,14 @@ pub trait IbcCommonContext: IbcStorageContext { denom: PrefixedDenom, ) -> Result<(), ContextError> { let key = storage::ibc_denom_key(trace_hash); - let bytes = denom.to_string().as_bytes().to_vec(); + let bytes = denom.to_string().try_to_vec().map_err(|e| { + ContextError::ChannelError(ChannelError::Other { + description: format!( + "Encoding the denom failed: Denom {}, error {}", + denom, e + ), + }) + })?; self.write(&key, bytes).map_err(|_| { ContextError::ChannelError(ChannelError::Other { description: format!("Writing the denom failed: Key {}", key), diff --git a/core/src/ledger/ibc/context/execution.rs b/core/src/ledger/ibc/context/execution.rs index ddf289ce08..ae84afa614 100644 --- a/core/src/ledger/ibc/context/execution.rs +++ b/core/src/ledger/ibc/context/execution.rs @@ -1,5 +1,7 @@ //! ExecutionContext implementation for IBC +use borsh::{BorshDeserialize, BorshSerialize}; + use super::super::{IbcActions, IbcCommonContext}; use crate::ibc::core::ics02_client::client_state::ClientState; use crate::ibc::core::ics02_client::client_type::ClientType; @@ -180,7 +182,7 @@ where .expect("Creating a key for the client state shouldn't fail"); let list = match self.ctx.borrow().read(&key) { Ok(Some(value)) => { - let list = String::from_utf8(value).map_err(|e| { + let list = String::try_from_slice(&value).map_err(|e| { ContextError::ConnectionError(ConnectionError::Other { description: format!( "Decoding the connection list failed: Key {}, \ @@ -201,7 +203,7 @@ where }))? } }; - let bytes = list.as_bytes().to_vec(); + let bytes = list.try_to_vec().expect("encoding shouldn't fail"); self.ctx.borrow_mut().write(&key, bytes).map_err(|_| { ContextError::ConnectionError(ConnectionError::Other { description: format!( diff --git a/core/src/ledger/ibc/context/storage.rs b/core/src/ledger/ibc/context/storage.rs index a99a659187..87555990a4 100644 --- a/core/src/ledger/ibc/context/storage.rs +++ b/core/src/ledger/ibc/context/storage.rs @@ -6,6 +6,7 @@ pub use ics23::ProofSpec; use super::super::Error; use crate::ledger::storage_api; +use crate::types::address::Address; use crate::types::ibc::IbcEvent; use crate::types::storage::{BlockHeight, Header, Key}; use crate::types::token::Amount; @@ -54,8 +55,25 @@ pub trait IbcStorageContext { /// Transfer token fn transfer_token( &mut self, - src: &Key, - dest: &Key, + src: &Address, + dest: &Address, + token: &Address, + amount: Amount, + ) -> Result<(), Self::Error>; + + /// Mint token + fn mint_token( + &mut self, + target: &Address, + token: &Address, + amount: Amount, + ) -> Result<(), Self::Error>; + + /// Burn token + fn burn_token( + &mut self, + target: &Address, + token: &Address, amount: Amount, ) -> Result<(), Self::Error>; diff --git a/core/src/ledger/ibc/context/transfer_mod.rs b/core/src/ledger/ibc/context/transfer_mod.rs index ad0aa75800..8280f7c36b 100644 --- a/core/src/ledger/ibc/context/transfer_mod.rs +++ b/core/src/ledger/ibc/context/transfer_mod.rs @@ -388,7 +388,7 @@ where _port_id: &PortId, _channel_id: &ChannelId, ) -> Result { - Ok(Address::Internal(InternalAddress::IbcEscrow)) + Ok(Address::Internal(InternalAddress::Ibc)) } fn can_send_coins(&self) -> Result<(), TokenTransferError> { @@ -444,44 +444,18 @@ where ) -> Result<(), TokenTransferError> { // Assumes that the coin denom is prefixed with "port-id/channel-id" or // has no prefix - let (token, amount) = get_token_amount(coin)?; - - let src = if coin.denom.trace_path.is_empty() - || *from == Address::Internal(InternalAddress::IbcEscrow) - || *from == Address::Internal(InternalAddress::IbcMint) - { - token::balance_key(&token, from) - } else { - let prefix = storage::ibc_token_prefix(coin.denom.to_string()) - .map_err(|_| TokenTransferError::InvalidCoin { - coin: coin.to_string(), - })?; - token::multitoken_balance_key(&prefix, from) - }; - - let dest = if coin.denom.trace_path.is_empty() - || *to == Address::Internal(InternalAddress::IbcEscrow) - || *to == Address::Internal(InternalAddress::IbcBurn) - { - token::balance_key(&token, to) - } else { - let prefix = storage::ibc_token_prefix(coin.denom.to_string()) - .map_err(|_| TokenTransferError::InvalidCoin { - coin: coin.to_string(), - })?; - token::multitoken_balance_key(&prefix, to) - }; + let (ibc_token, amount) = get_token_amount(coin)?; self.ctx .borrow_mut() - .transfer_token(&src, &dest, amount) + .transfer_token(from, to, &ibc_token, amount) .map_err(|_| { TokenTransferError::ContextError(ContextError::ChannelError( ChannelError::Other { description: format!( "Sending a coin failed: from {}, to {}, amount {}", - src, - dest, + from, + to, amount.to_string_native() ), }, @@ -494,33 +468,18 @@ where account: &Self::AccountId, coin: &PrefixedCoin, ) -> Result<(), TokenTransferError> { - let (token, amount) = get_token_amount(coin)?; - - let src = token::balance_key( - &token, - &Address::Internal(InternalAddress::IbcMint), - ); - - let dest = if coin.denom.trace_path.is_empty() { - token::balance_key(&token, account) - } else { - let prefix = storage::ibc_token_prefix(coin.denom.to_string()) - .map_err(|_| TokenTransferError::InvalidCoin { - coin: coin.to_string(), - })?; - token::multitoken_balance_key(&prefix, account) - }; + // The trace path of the denom is already updated if receiving the token + let (ibc_token, amount) = get_token_amount(coin)?; self.ctx .borrow_mut() - .transfer_token(&src, &dest, amount) + .mint_token(account, &ibc_token, amount) .map_err(|_| { TokenTransferError::ContextError(ContextError::ChannelError( ChannelError::Other { description: format!( - "Sending a coin failed: from {}, to {}, amount {}", - src, - dest, + "Minting a coin failed: account {}, amount {}", + account, amount.to_string_native() ), }, @@ -533,33 +492,18 @@ where account: &Self::AccountId, coin: &PrefixedCoin, ) -> Result<(), TokenTransferError> { - let (token, amount) = get_token_amount(coin)?; - - let src = if coin.denom.trace_path.is_empty() { - token::balance_key(&token, account) - } else { - let prefix = storage::ibc_token_prefix(coin.denom.to_string()) - .map_err(|_| TokenTransferError::InvalidCoin { - coin: coin.to_string(), - })?; - token::multitoken_balance_key(&prefix, account) - }; - - let dest = token::balance_key( - &token, - &Address::Internal(InternalAddress::IbcBurn), - ); + let (ibc_token, amount) = get_token_amount(coin)?; + // The burn is "unminting" from the minted balance self.ctx .borrow_mut() - .transfer_token(&src, &dest, amount) + .burn_token(account, &ibc_token, amount) .map_err(|_| { TokenTransferError::ContextError(ContextError::ChannelError( ChannelError::Other { description: format!( - "Sending a coin failed: from {}, to {}, amount {}", - src, - dest, + "Burning a coin failed: account {}, amount {}", + account, amount.to_string_native() ), }, @@ -605,16 +549,15 @@ where } } -/// Get the token address and the amount from PrefixedCoin +/// Get the token address and the amount from PrefixedCoin. If the base denom is +/// not an address, it returns `IbcToken` fn get_token_amount( coin: &PrefixedCoin, ) -> Result<(Address, token::Amount), TokenTransferError> { - let token = - Address::decode(coin.denom.base_denom.as_str()).map_err(|_| { - TokenTransferError::InvalidCoin { - coin: coin.denom.base_denom.to_string(), - } - })?; + let token = match Address::decode(coin.denom.base_denom.as_str()) { + Ok(token_addr) if coin.denom.trace_path.is_empty() => token_addr, + _ => storage::ibc_token(coin.denom.to_string()), + }; let amount = coin.amount.try_into().map_err(|_| { TokenTransferError::InvalidCoin { diff --git a/core/src/ledger/ibc/mod.rs b/core/src/ledger/ibc/mod.rs index 9a184ee51e..b56e8ce54a 100644 --- a/core/src/ledger/ibc/mod.rs +++ b/core/src/ledger/ibc/mod.rs @@ -9,6 +9,7 @@ use std::fmt::Debug; use std::rc::Rc; use std::time::Duration; +use borsh::BorshDeserialize; pub use context::common::IbcCommonContext; pub use context::storage::{IbcStorageContext, ProofSpec}; pub use context::transfer_mod::{ModuleWrapper, TransferModule}; @@ -140,7 +141,7 @@ where } } - /// Restore the denom when it is hashed, i.e. the denom is `ibc/{hash}`. + /// Restore the denom when it is hashed fn restore_denom(&self, msg: MsgTransfer) -> Result { let mut msg = msg; // lookup the original denom with the IBC token hash @@ -151,7 +152,7 @@ where { let denom_key = storage::ibc_denom_key(token_hash); let denom = match self.ctx.borrow().read(&denom_key) { - Ok(Some(v)) => String::from_utf8(v).map_err(|e| { + Ok(Some(v)) => String::try_from_slice(&v[..]).map_err(|e| { Error::Denom(format!( "Decoding the denom string failed: {}", e diff --git a/core/src/ledger/ibc/storage.rs b/core/src/ledger/ibc/storage.rs index 3f0eb94d2a..1a47680f00 100644 --- a/core/src/ledger/ibc/storage.rs +++ b/core/src/ledger/ibc/storage.rs @@ -23,8 +23,6 @@ const CLIENTS_COUNTER: &str = "clients/counter"; const CONNECTIONS_COUNTER: &str = "connections/counter"; const CHANNELS_COUNTER: &str = "channelEnds/counter"; const DENOM: &str = "ibc_denom"; -/// Key segment for a multitoken related to IBC -pub const MULTITOKEN_STORAGE_KEY: &str = "ibc"; #[allow(missing_docs)] #[derive(Error, Debug)] @@ -44,67 +42,6 @@ pub enum Error { /// IBC storage functions result pub type Result = std::result::Result; -/// IBC prefix -#[allow(missing_docs)] -pub enum IbcPrefix { - Client, - Connection, - Channel, - Port, - Capability, - SeqSend, - SeqRecv, - SeqAck, - Commitment, - Receipt, - Ack, - Event, - Denom, - Unknown, -} - -/// Returns the prefix from the given key -pub fn ibc_prefix(key: &Key) -> Option { - match &key.segments[..] { - [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(prefix), ..] - if addr == &Address::Internal(InternalAddress::Ibc) => - { - Some(match &*prefix.raw() { - "clients" => IbcPrefix::Client, - "connections" => IbcPrefix::Connection, - "channelEnds" => IbcPrefix::Channel, - "ports" => IbcPrefix::Port, - "capabilities" => IbcPrefix::Capability, - "nextSequenceSend" => IbcPrefix::SeqSend, - "nextSequenceRecv" => IbcPrefix::SeqRecv, - "nextSequenceAck" => IbcPrefix::SeqAck, - "commitments" => IbcPrefix::Commitment, - "receipts" => IbcPrefix::Receipt, - "acks" => IbcPrefix::Ack, - "event" => IbcPrefix::Event, - "ibc_denom" => IbcPrefix::Denom, - _ => IbcPrefix::Unknown, - }) - } - _ => None, - } -} - -/// Check if the given key is a key of the client counter -pub fn is_client_counter_key(key: &Key) -> bool { - *key == client_counter_key() -} - -/// Check if the given key is a key of the connection counter -pub fn is_connection_counter_key(key: &Key) -> bool { - *key == connection_counter_key() -} - -/// Check if the given key is a key of the channel counter -pub fn is_channel_counter_key(key: &Key) -> bool { - *key == channel_counter_key() -} - /// Returns a key of the IBC-related data pub fn ibc_key(path: impl AsRef) -> Result { let path = Key::parse(path).map_err(Error::StorageKey)?; @@ -473,26 +410,20 @@ pub fn token(denom: impl AsRef) -> Result
{ /// Get the hash of IBC token address from the denom string pub fn token_hash_from_denom(denom: impl AsRef) -> Result> { - match denom - .as_ref() - .strip_prefix(&format!("{}/", MULTITOKEN_STORAGE_KEY)) - { - Some(addr_str) => { - let addr = Address::decode(addr_str).map_err(|e| { - Error::Denom(format!( - "Decoding the denom failed: ibc_token {}, error {}", - addr_str, e - )) - })?; - match addr { - Address::Internal(InternalAddress::IbcToken(h)) => Ok(Some(h)), - _ => Err(Error::Denom(format!( - "Unexpected address was given: {}", - addr - ))), - } - } - None => Ok(None), + let addr = Address::decode(denom.as_ref()).map_err(|e| { + Error::Denom(format!( + "Decoding the denom failed: denom {}, error {}", + denom.as_ref(), + e + )) + })?; + match addr { + Address::Established(_) => Ok(None), + Address::Internal(InternalAddress::IbcToken(h)) => Ok(Some(h)), + _ => Err(Error::Denom(format!( + "Unexpected address was given: {}", + addr + ))), } } @@ -503,17 +434,10 @@ pub fn calc_hash(denom: impl AsRef) -> String { format!("{:.width$x}", hasher.finalize(), width = HASH_HEX_LEN) } -/// Key's prefix of the received token over IBC -pub fn ibc_token_prefix(denom: impl AsRef) -> Result { - let token = token(&denom)?; +/// Obtain the IbcToken with the hash from the given denom +pub fn ibc_token(denom: impl AsRef) -> Address { let hash = calc_hash(&denom); - let ibc_token = Address::Internal(InternalAddress::IbcToken(hash)); - let prefix = Key::from(token.to_db_key()) - .push(&MULTITOKEN_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a storage key") - .push(&ibc_token.to_db_key()) - .expect("Cannot obtain a storage key"); - Ok(prefix) + Address::Internal(InternalAddress::IbcToken(hash)) } /// Returns true if the given key is for IBC @@ -522,18 +446,24 @@ pub fn is_ibc_key(key: &Key) -> bool { DbKeySeg::AddressSeg(addr) if *addr == Address::Internal(InternalAddress::Ibc)) } -/// Returns true if the sub prefix is for IBC -pub fn is_ibc_sub_prefix(sub_prefix: &Key) -> bool { - matches!(&sub_prefix.segments[0], - DbKeySeg::StringSeg(s) if s == MULTITOKEN_STORAGE_KEY) -} - -/// Returns true if the given key is the denom key -pub fn is_ibc_denom_key(key: &Key) -> bool { +/// Returns the token hash if the given key is the denom key +pub fn is_ibc_denom_key(key: &Key) -> Option { match &key.segments[..] { - [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(prefix), ..] => { - addr == &Address::Internal(InternalAddress::Ibc) && prefix == DENOM + [ + DbKeySeg::AddressSeg(addr), + DbKeySeg::StringSeg(prefix), + DbKeySeg::AddressSeg(Address::Internal(InternalAddress::IbcToken( + hash, + ))), + ] => { + if addr == &Address::Internal(InternalAddress::Ibc) + && prefix == DENOM + { + Some(hash.clone()) + } else { + None + } } - _ => false, + _ => None, } } diff --git a/core/src/ledger/storage/masp_conversions.rs b/core/src/ledger/storage/masp_conversions.rs index 4dec02d4f5..bbb61ff50d 100644 --- a/core/src/ledger/storage/masp_conversions.rs +++ b/core/src/ledger/storage/masp_conversions.rs @@ -9,7 +9,7 @@ use masp_primitives::merkle_tree::FrozenCommitmentTree; use masp_primitives::sapling::Node; use crate::types::address::Address; -use crate::types::storage::{Epoch, Key}; +use crate::types::storage::Epoch; use crate::types::token::MaspDenom; /// A representation of the conversion state @@ -23,12 +23,7 @@ pub struct ConversionState { #[allow(clippy::type_complexity)] pub assets: BTreeMap< AssetType, - ( - (Address, Option, MaspDenom), - Epoch, - AllowedConversion, - usize, - ), + ((Address, MaspDenom), Epoch, AllowedConversion, usize), >, } @@ -66,24 +61,16 @@ where // have to use. This trick works under the assumption that reward tokens // from different epochs are exactly equivalent. let reward_asset = - encode_asset_type(address::nam(), &None, MaspDenom::Zero, Epoch(0)); + encode_asset_type(address::nam(), MaspDenom::Zero, Epoch(0)); // Conversions from the previous to current asset for each address let mut current_convs = - BTreeMap::<(Address, Option, MaspDenom), AllowedConversion>::new(); + BTreeMap::<(Address, MaspDenom), AllowedConversion>::new(); // Reward all tokens according to above reward rates - for ((addr, sub_prefix), reward) in &masp_rewards { + for (addr, reward) in &masp_rewards { // Dispense a transparent reward in parallel to the shielded rewards - let addr_bal: token::Amount = match sub_prefix { - None => wl_storage - .read(&token::balance_key(addr, &masp_addr))? - .unwrap_or_default(), - Some(sub) => wl_storage - .read(&token::multitoken_balance_key( - &token::multitoken_balance_prefix(addr, sub), - &masp_addr, - ))? - .unwrap_or_default(), - }; + let addr_bal: token::Amount = wl_storage + .read(&token::balance_key(addr, &masp_addr))? + .unwrap_or_default(); // The reward for each reward.1 units of the current asset is // reward.0 units of the reward token // Since floor(a) + floor(b) <= floor(a+b), there will always be @@ -95,18 +82,16 @@ where // cancelled out/replaced with the new asset let old_asset = encode_asset_type( addr.clone(), - sub_prefix, denom, wl_storage.storage.last_epoch, ); let new_asset = encode_asset_type( addr.clone(), - sub_prefix, denom, wl_storage.storage.block.epoch, ); current_convs.insert( - (addr.clone(), sub_prefix.clone(), denom), + (addr.clone(), denom), (MaspAmount::from_pair(old_asset, -(reward.1 as i64)).unwrap() + MaspAmount::from_pair(new_asset, reward.1).unwrap() + MaspAmount::from_pair(reward_asset, reward.0).unwrap()) @@ -116,7 +101,7 @@ where wl_storage.storage.conversion_state.assets.insert( old_asset, ( - (addr.clone(), sub_prefix.clone(), denom), + (addr.clone(), denom), wl_storage.storage.last_epoch, MaspAmount::zero().into(), 0, @@ -188,20 +173,19 @@ where // Add purely decoding entries to the assets map. These will be // overwritten before the creation of the next commitment tree - for (addr, sub_prefix) in masp_rewards.keys() { + for addr in masp_rewards.keys() { for denom in token::MaspDenom::iter() { // Add the decoding entry for the new asset type. An uncommited // node position is used since this is not a conversion. let new_asset = encode_asset_type( addr.clone(), - sub_prefix, denom, wl_storage.storage.block.epoch, ); wl_storage.storage.conversion_state.assets.insert( new_asset, ( - (addr.clone(), sub_prefix.clone(), denom), + (addr.clone(), denom), wl_storage.storage.block.epoch, MaspAmount::zero().into(), wl_storage.storage.conversion_state.tree.size(), @@ -229,19 +213,10 @@ where /// Construct MASP asset type with given epoch for given token pub fn encode_asset_type( addr: Address, - sub_prefix: &Option, denom: MaspDenom, epoch: Epoch, ) -> AssetType { - let new_asset_bytes = ( - addr, - sub_prefix - .as_ref() - .map(|k| k.to_string()) - .unwrap_or_default(), - denom, - epoch.0, - ) + let new_asset_bytes = (addr, denom, epoch.0) .try_to_vec() .expect("unable to serialize address and epoch"); AssetType::new(new_asset_bytes.as_ref()) diff --git a/core/src/ledger/storage/mockdb.rs b/core/src/ledger/storage/mockdb.rs index 24ffd0e59b..971584e742 100644 --- a/core/src/ledger/storage/mockdb.rs +++ b/core/src/ledger/storage/mockdb.rs @@ -53,7 +53,7 @@ impl DB for MockDB { Ok(()) } - fn read_last_block(&mut self) -> Result> { + fn read_last_block(&self) -> Result> { // Block height let height: BlockHeight = match self.0.borrow().get("height") { Some(bytes) => types::decode(bytes).map_err(Error::CodingError)?, @@ -211,8 +211,8 @@ impl DB for MockDB { } } - fn write_block( - &mut self, + fn add_block_to_batch( + &self, state: BlockStateWrite, _batch: &mut Self::WriteBatch, _is_full_commit: bool, diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index 5d022eadb1..94669a34f4 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -256,12 +256,12 @@ pub trait DB: std::fmt::Debug { fn flush(&self, wait: bool) -> Result<()>; /// Read the last committed block's metadata - fn read_last_block(&mut self) -> Result>; + fn read_last_block(&self) -> Result>; /// Write block's metadata. Merkle tree sub-stores are committed only when /// `is_full_commit` is `true` (typically on a beginning of a new epoch). - fn write_block( - &mut self, + fn add_block_to_batch( + &self, state: BlockStateWrite, batch: &mut Self::WriteBatch, is_full_commit: bool, @@ -532,7 +532,8 @@ where ethereum_height: self.ethereum_height.as_ref(), eth_events_queue: &self.eth_events_queue, }; - self.db.write_block(state, &mut batch, is_full_commit)?; + self.db + .add_block_to_batch(state, &mut batch, is_full_commit)?; let header = self .header .take() diff --git a/core/src/ledger/storage/wl_storage.rs b/core/src/ledger/storage/wl_storage.rs index 1cb7e56a27..4fb2490ab9 100644 --- a/core/src/ledger/storage/wl_storage.rs +++ b/core/src/ledger/storage/wl_storage.rs @@ -143,6 +143,12 @@ where /// Commit the current block's write log to the storage and commit the block /// to DB. Starts a new block write log. pub fn commit_block(&mut self) -> storage_api::Result<()> { + if self.storage.last_epoch != self.storage.block.epoch { + self.storage + .update_epoch_in_merkle_tree() + .into_storage_result()?; + } + let mut batch = D::batch(); self.write_log .commit_block(&mut self.storage, &mut batch) @@ -205,7 +211,6 @@ where .new_epoch(height, evidence_max_age_num_blocks); tracing::info!("Began a new epoch {}", self.storage.block.epoch); } - self.storage.update_epoch_in_merkle_tree()?; Ok(new_epoch) } } diff --git a/core/src/ledger/storage/write_log.rs b/core/src/ledger/storage/write_log.rs index 4b4c055c50..641fa7fc19 100644 --- a/core/src/ledger/storage/write_log.rs +++ b/core/src/ledger/storage/write_log.rs @@ -9,10 +9,13 @@ use thiserror::Error; use crate::ledger; use crate::ledger::storage::traits::StorageHasher; use crate::ledger::storage::Storage; -use crate::types::address::{Address, EstablishedAddressGen}; +use crate::types::address::{Address, EstablishedAddressGen, InternalAddress}; use crate::types::hash::Hash; use crate::types::ibc::IbcEvent; use crate::types::storage; +use crate::types::token::{ + is_any_minted_balance_key, is_any_minter_key, is_any_token_balance_key, +}; #[allow(missing_docs)] #[derive(Error, Debug)] @@ -470,21 +473,33 @@ impl WriteLog { // get changed keys grouped by the address for key in changed_keys.iter() { - for addr in &key.find_addresses() { - if verifiers_from_tx.contains(addr) - || initialized_accounts.contains(addr) - { - // We can skip this when the address has been added from the - // Tx above. - // Also skip if it's an address of a newly initialized - // account, because anything can be written into an - // account's storage in the same tx in which it's - // initialized (there is no VP in the state prior to tx - // execution). - continue; + // for token keys, trigger Multitoken VP and the owner's VP + if let Some([_, owner]) = is_any_token_balance_key(key) { + verifiers + .insert(Address::Internal(InternalAddress::Multitoken)); + verifiers.insert(owner.clone()); + } else if is_any_minted_balance_key(key).is_some() + || is_any_minter_key(key).is_some() + { + verifiers + .insert(Address::Internal(InternalAddress::Multitoken)); + } else { + for addr in &key.find_addresses() { + if verifiers_from_tx.contains(addr) + || initialized_accounts.contains(addr) + { + // We can skip this when the address has been added from + // the Tx above. + // Also skip if it's an address of a newly initialized + // account, because anything can be written into an + // account's storage in the same tx in which it's + // initialized (there is no VP in the state prior to tx + // execution). + continue; + } + // Add the address as a verifier + verifiers.insert(addr.clone()); } - // Add the address as a verifier - verifiers.insert(addr.clone()); } } (verifiers, changed_keys) diff --git a/core/src/ledger/storage_api/token.rs b/core/src/ledger/storage_api/token.rs index 880d748274..1985d8325c 100644 --- a/core/src/ledger/storage_api/token.rs +++ b/core/src/ledger/storage_api/token.rs @@ -3,12 +3,10 @@ use super::{StorageRead, StorageWrite}; use crate::ledger::storage_api; use crate::types::address::Address; -use crate::types::storage::DbKeySeg::StringSeg; -use crate::types::storage::Key; use crate::types::token; pub use crate::types::token::{ - balance_key, is_balance_key, is_total_supply_key, total_supply_key, Amount, - Change, + balance_key, is_any_minted_balance_key, is_balance_key, minted_balance_key, + minter_key, Amount, Change, }; /// Read the balance of a given token and owner. @@ -33,7 +31,7 @@ pub fn read_total_supply( where S: StorageRead, { - let key = token::total_supply_key(token); + let key = token::minted_balance_key(token); let balance = storage.read::(&key)?.unwrap_or_default(); Ok(balance) } @@ -44,17 +42,11 @@ where pub fn read_denom( storage: &S, token: &Address, - sub_prefix: Option<&Key>, ) -> storage_api::Result> where S: StorageRead, { - if let Some(sub_prefix) = sub_prefix { - if sub_prefix.segments.contains(&StringSeg("ibc".to_string())) { - return Ok(Some(token::NATIVE_MAX_DECIMAL_PLACES.into())); - } - } - let key = token::denom_key(token, sub_prefix); + let key = token::denom_key(token); storage.read(&key).map(|opt_denom| { Some( opt_denom @@ -67,13 +59,12 @@ where pub fn write_denom( storage: &mut S, token: &Address, - sub_prefix: Option<&Key>, denom: token::Denomination, ) -> storage_api::Result<()> where S: StorageRead + StorageWrite, { - let key = token::denom_key(token, sub_prefix); + let key = token::denom_key(token); storage.write(&key, denom) } @@ -132,7 +123,7 @@ where storage_api::Error::new_const("Token balance overflow") })?; - let total_supply_key = token::total_supply_key(token); + let total_supply_key = token::minted_balance_key(token); let cur_supply = storage .read::(&total_supply_key)? .unwrap_or_default(); diff --git a/core/src/ledger/testnet_pow.rs b/core/src/ledger/testnet_pow.rs index aa1257a886..dcb7c9feb2 100644 --- a/core/src/ledger/testnet_pow.rs +++ b/core/src/ledger/testnet_pow.rs @@ -12,7 +12,7 @@ use crate::ledger::storage_api::collections::LazyMap; use crate::types::address::Address; use crate::types::hash::Hash; use crate::types::storage::{self, DbKeySeg, Key}; -use crate::types::token; +use crate::types::uint::Uint; /// Initialize faucet's storage. This must be called at genesis if faucet /// account is being used. @@ -20,7 +20,7 @@ pub fn init_faucet_storage( storage: &mut S, address: &Address, difficulty: Difficulty, - withdrawal_limit: token::Amount, + withdrawal_limit: Uint, ) -> storage_api::Result<()> where S: StorageWrite, @@ -457,7 +457,7 @@ where pub fn read_withdrawal_limit( storage: &S, address: &Address, -) -> storage_api::Result +) -> storage_api::Result where S: StorageRead, { @@ -471,7 +471,7 @@ where pub fn write_withdrawal_limit( storage: &mut S, address: &Address, - withdrawal_limit: token::Amount, + withdrawal_limit: Uint, ) -> Result<(), storage_api::Error> where S: StorageWrite, diff --git a/core/src/proto/types.rs b/core/src/proto/types.rs index 8246dd8a13..4fae2faf87 100644 --- a/core/src/proto/types.rs +++ b/core/src/proto/types.rs @@ -31,7 +31,7 @@ use crate::types::address::Address; use crate::types::chain::ChainId; use crate::types::keccak::{keccak_hash, KeccakHash}; use crate::types::key::{self, *}; -use crate::types::storage::{Epoch, Key}; +use crate::types::storage::Epoch; use crate::types::time::DateTimeUtc; use crate::types::token::MaspDenom; #[cfg(feature = "ferveo-tpke")] @@ -816,7 +816,7 @@ pub struct MaspBuilder { pub target: crate::types::hash::Hash, /// The decoded set of asset types used by the transaction. Useful for /// offline wallets trying to display AssetTypes. - pub asset_types: HashSet<(Address, Option, MaspDenom, Epoch)>, + pub asset_types: HashSet<(Address, MaspDenom, Epoch)>, /// Track how Info objects map to descriptors and outputs #[serde( serialize_with = "borsh_serde::", diff --git a/core/src/types/address.rs b/core/src/types/address.rs index eca6c87251..1a6611a2f5 100644 --- a/core/src/types/address.rs +++ b/core/src/types/address.rs @@ -17,7 +17,6 @@ use crate::ibc::signer::Signer; use crate::types::ethereum_events::EthAddress; use crate::types::key; use crate::types::key::PublicKeyHash; -use crate::types::storage::Key; use crate::types::token::Denomination; /// The length of an established [`Address`] encoded with Borsh. @@ -54,10 +53,6 @@ pub const FIXED_LEN_STRING_BYTES: usize = 45; /// Internal IBC address pub const IBC: Address = Address::Internal(InternalAddress::Ibc); -/// Internal IBC token burn address -pub const IBC_BURN: Address = Address::Internal(InternalAddress::IbcBurn); -/// Internal IBC token mint address -pub const IBC_MINT: Address = Address::Internal(InternalAddress::IbcMint); /// Internal ledger parameters address pub const PARAMETERS: Address = Address::Internal(InternalAddress::Parameters); /// Internal PoS address @@ -84,18 +79,14 @@ mod internal { "ano::Slash Fund "; pub const IBC: &str = "ibc::Inter-Blockchain Communication "; - pub const IBC_ESCROW: &str = - "ibc::IBC Escrow Address "; - pub const IBC_BURN: &str = - "ibc::IBC Burn Address "; - pub const IBC_MINT: &str = - "ibc::IBC Mint Address "; pub const ETH_BRIDGE: &str = "ano::ETH Bridge Address "; pub const ETH_BRIDGE_POOL: &str = "ano::ETH Bridge Pool Address "; pub const REPLAY_PROTECTION: &str = "ano::Replay Protection "; + pub const MULTITOKEN: &str = + "ano::Multitoken "; } /// Fixed-length address strings prefix for established addresses. @@ -106,6 +97,8 @@ const PREFIX_IMPLICIT: &str = "imp"; const PREFIX_INTERNAL: &str = "ano"; /// Fixed-length address strings prefix for IBC addresses. const PREFIX_IBC: &str = "ibc"; +/// Fixed-length address strings prefix for Ethereum addresses. +const PREFIX_ETH: &str = "eth"; #[allow(missing_docs)] #[derive(Error, Debug)] @@ -233,20 +226,23 @@ impl Address { InternalAddress::IbcToken(hash) => { format!("{}::{}", PREFIX_IBC, hash) } - InternalAddress::IbcEscrow => { - internal::IBC_ESCROW.to_string() - } - InternalAddress::IbcBurn => internal::IBC_BURN.to_string(), - InternalAddress::IbcMint => internal::IBC_MINT.to_string(), InternalAddress::EthBridge => { internal::ETH_BRIDGE.to_string() } InternalAddress::EthBridgePool => { internal::ETH_BRIDGE_POOL.to_string() } + InternalAddress::Erc20(eth_addr) => { + let eth_addr = + eth_addr.to_canonical().replace("0x", ""); + format!("{}::{}", PREFIX_ETH, eth_addr) + } InternalAddress::ReplayProtection => { internal::REPLAY_PROTECTION.to_string() } + InternalAddress::Multitoken => { + internal::MULTITOKEN.to_string() + } }; debug_assert_eq!(string.len(), FIXED_LEN_STRING_BYTES); string @@ -320,6 +316,9 @@ impl Address { internal::REPLAY_PROTECTION => { Ok(Address::Internal(InternalAddress::ReplayProtection)) } + internal::MULTITOKEN => { + Ok(Address::Internal(InternalAddress::Multitoken)) + } _ => Err(Error::new( ErrorKind::InvalidData, "Invalid internal address", @@ -327,15 +326,6 @@ impl Address { }, Some((PREFIX_IBC, raw)) => match string { internal::IBC => Ok(Address::Internal(InternalAddress::Ibc)), - internal::IBC_ESCROW => { - Ok(Address::Internal(InternalAddress::IbcEscrow)) - } - internal::IBC_BURN => { - Ok(Address::Internal(InternalAddress::IbcBurn)) - } - internal::IBC_MINT => { - Ok(Address::Internal(InternalAddress::IbcMint)) - } _ if raw.len() == HASH_HEX_LEN => Ok(Address::Internal( InternalAddress::IbcToken(raw.to_string()), )), @@ -344,6 +334,23 @@ impl Address { "Invalid IBC internal address", )), }, + Some((PREFIX_ETH, raw)) => match string { + _ if raw.len() == HASH_HEX_LEN => { + match EthAddress::from_str(&format!("0x{}", raw)) { + Ok(eth_addr) => Ok(Address::Internal( + InternalAddress::Erc20(eth_addr), + )), + Err(e) => Err(Error::new( + ErrorKind::InvalidData, + e.to_string(), + )), + } + } + _ => Err(Error::new( + ErrorKind::InvalidData, + "Invalid ERC20 internal address", + )), + }, _ => Err(Error::new( ErrorKind::InvalidData, "Invalid address prefix", @@ -532,12 +539,6 @@ pub enum InternalAddress { Ibc, /// IBC-related token IbcToken(String), - /// Escrow for IBC token transfer - IbcEscrow, - /// Burn tokens with IBC token transfer - IbcBurn, - /// Mint tokens from this address with IBC token transfer - IbcMint, /// Governance address Governance, /// SlashFund address for governance @@ -546,24 +547,12 @@ pub enum InternalAddress { EthBridge, /// The pool of transactions to be relayed to Ethereum EthBridgePool, + /// ERC20 token for Ethereum bridge + Erc20(EthAddress), /// Replay protection contains transactions' hash ReplayProtection, -} - -impl InternalAddress { - /// Get an IBC token address from the port ID and channel ID - pub fn ibc_token_address( - port_id: String, - channel_id: String, - token: &Address, - ) -> Self { - let mut hasher = Sha256::new(); - let s = format!("{}/{}/{}", port_id, channel_id, token); - hasher.update(&s); - let hash = - format!("{:.width$x}", hasher.finalize(), width = HASH_HEX_LEN); - InternalAddress::IbcToken(hash) - } + /// Multitoken + Multitoken, } impl Display for InternalAddress { @@ -579,12 +568,11 @@ impl Display for InternalAddress { Self::SlashFund => "SlashFund".to_string(), Self::Ibc => "IBC".to_string(), Self::IbcToken(hash) => format!("IbcToken: {}", hash), - Self::IbcEscrow => "IbcEscrow".to_string(), - Self::IbcBurn => "IbcBurn".to_string(), - Self::IbcMint => "IbcMint".to_string(), Self::EthBridge => "EthBridge".to_string(), Self::EthBridgePool => "EthBridgePool".to_string(), + Self::Erc20(eth_addr) => format!("Erc20: {}", eth_addr), Self::ReplayProtection => "ReplayProtection".to_string(), + Self::Multitoken => "Multitoken".to_string(), } ) } @@ -670,15 +658,15 @@ pub fn tokens() -> HashMap { /// Temporary helper for testing, a hash map of tokens addresses with their /// MASP XAN incentive schedules. If the reward is (a, b) then a rewarded tokens /// are dispensed for every b possessed tokens. -pub fn masp_rewards() -> HashMap<(Address, Option), (u64, u64)> { +pub fn masp_rewards() -> HashMap { vec![ - ((nam(), None), (0, 100)), - ((btc(), None), (1, 100)), - ((eth(), None), (2, 100)), - ((dot(), None), (3, 100)), - ((schnitzel(), None), (4, 100)), - ((apfel(), None), (5, 100)), - ((kartoffel(), None), (6, 100)), + (nam(), (0, 100)), + (btc(), (1, 100)), + (eth(), (2, 100)), + (dot(), (3, 100)), + (schnitzel(), (4, 100)), + (apfel(), (5, 100)), + (kartoffel(), (6, 100)), ] .into_iter() .collect() @@ -875,35 +863,30 @@ pub mod testing { InternalAddress::Parameters => {} InternalAddress::Ibc => {} InternalAddress::IbcToken(_) => {} - InternalAddress::IbcEscrow => {} - InternalAddress::IbcBurn => {} - InternalAddress::IbcMint => {} InternalAddress::EthBridge => {} InternalAddress::EthBridgePool => {} - InternalAddress::ReplayProtection => {} /* Add new addresses in - * the - * `prop_oneof` below. */ + InternalAddress::Erc20(_) => {} + InternalAddress::ReplayProtection => {} + InternalAddress::Multitoken => {} /* Add new addresses in the + * `prop_oneof` below. */ }; prop_oneof![ Just(InternalAddress::PoS), Just(InternalAddress::PosSlashPool), Just(InternalAddress::Ibc), Just(InternalAddress::Parameters), - Just(InternalAddress::Ibc), arb_ibc_token(), - Just(InternalAddress::IbcEscrow), - Just(InternalAddress::IbcBurn), - Just(InternalAddress::IbcMint), Just(InternalAddress::Governance), Just(InternalAddress::SlashFund), Just(InternalAddress::EthBridge), Just(InternalAddress::EthBridgePool), - Just(InternalAddress::ReplayProtection) + Just(arb_erc20()), + Just(InternalAddress::ReplayProtection), + Just(InternalAddress::Multitoken), ] } fn arb_ibc_token() -> impl Strategy { - // use sha2::{Digest, Sha256}; ("[a-zA-Z0-9_]{2,128}", any::()).prop_map(|(id, counter)| { let mut hasher = sha2::Sha256::new(); let s = format!( @@ -918,4 +901,9 @@ pub mod testing { InternalAddress::IbcToken(hash) }) } + + fn arb_erc20() -> InternalAddress { + use crate::types::ethereum_events::testing::arbitrary_eth_address; + InternalAddress::Erc20(arbitrary_eth_address()) + } } diff --git a/core/src/types/eth_abi.rs b/core/src/types/eth_abi.rs index 5beaa6fe8f..adda87e6c9 100644 --- a/core/src/types/eth_abi.rs +++ b/core/src/types/eth_abi.rs @@ -198,7 +198,7 @@ mod tests { ) .expect("Test failed"), ], - voting_powers: vec![8828299.into()], + voting_powers: vec![8828299.try_into().unwrap()], epoch: 0.into(), }; let encoded = valset_update.encode().into_inner(); diff --git a/core/src/types/token.rs b/core/src/types/token.rs index b7c731e0e1..bf4e23cbe3 100644 --- a/core/src/types/token.rs +++ b/core/src/types/token.rs @@ -1,6 +1,6 @@ //! A basic fungible token -use std::fmt::{Display, Formatter}; +use std::fmt::Display; use std::iter::Sum; use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign}; use std::str::FromStr; @@ -15,7 +15,9 @@ use super::dec::POS_DECIMAL_PRECISION; use crate::ibc::applications::transfer::Amount as IbcAmount; use crate::ledger::storage_api::token::read_denom; use crate::ledger::storage_api::{self, StorageRead}; -use crate::types::address::{masp, Address, DecodeError as AddressError}; +use crate::types::address::{ + masp, Address, DecodeError as AddressError, InternalAddress, +}; use crate::types::dec::Dec; use crate::types::hash::Hash; use crate::types::storage; @@ -199,15 +201,13 @@ impl Amount { pub fn denominated( &self, token: &Address, - sub_prefix: Option<&Key>, storage: &impl StorageRead, ) -> storage_api::Result { - let denom = - read_denom(storage, token, sub_prefix)?.ok_or_else(|| { - storage_api::Error::SimpleMessage( - "No denomination found in storage for the given token", - ) - })?; + let denom = read_denom(storage, token)?.ok_or_else(|| { + storage_api::Error::SimpleMessage( + "No denomination found in storage for the given token", + ) + })?; Ok(DenominatedAmount { amount: *self, denom, @@ -767,6 +767,10 @@ impl TryFrom for Amount { pub const BALANCE_STORAGE_KEY: &str = "balance"; /// Key segment for a denomination key pub const DENOM_STORAGE_KEY: &str = "denomination"; +/// Key segment for multitoken minter +pub const MINTER_STORAGE_KEY: &str = "minter"; +/// Key segment for minted balance +pub const MINTED_STORAGE_KEY: &str = "minted"; /// Key segment for head shielded transaction pointer keys pub const HEAD_TX_KEY: &str = "head-tx"; /// Key segment prefix for shielded transaction key @@ -775,91 +779,41 @@ pub const TX_KEY_PREFIX: &str = "tx-"; pub const CONVERSION_KEY_PREFIX: &str = "conv"; /// Key segment prefix for pinned shielded transactions pub const PIN_KEY_PREFIX: &str = "pin-"; -const TOTAL_SUPPLY_STORAGE_KEY: &str = "total_supply"; - -/// A fully qualified (multi-) token address. -#[derive( - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - Debug, - Hash, - BorshSerialize, - BorshDeserialize, -)] -pub struct TokenAddress { - /// The address of the (multi-) token - pub address: Address, - /// If it is a mutli-token, this indicates the sub-token. - pub sub_prefix: Option, -} - -impl TokenAddress { - /// A function for displaying a [`TokenAddress`]. Takes a - /// human readable name of the token as input. - pub fn format_with_alias(&self, alias: &str) -> String { - format!( - "{}{}", - alias, - self.sub_prefix - .as_ref() - .map(|k| format!("/{}", k)) - .unwrap_or_default() - ) - } -} - -impl Display for TokenAddress { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let formatted = format!( - "{}{}", - self.address, - self.sub_prefix - .as_ref() - .map(|k| format!("/{}", k)) - .unwrap_or_default() - ); - f.write_str(&formatted) - } -} /// Obtain a storage key for user's balance. pub fn balance_key(token_addr: &Address, owner: &Address) -> Key { - Key::from(token_addr.to_db_key()) - .push(&BALANCE_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a storage key") + balance_prefix(token_addr) .push(&owner.to_db_key()) .expect("Cannot obtain a storage key") } /// Obtain a storage key prefix for all users' balances. pub fn balance_prefix(token_addr: &Address) -> Key { - Key::from(token_addr.to_db_key()) + Key::from(Address::Internal(InternalAddress::Multitoken).to_db_key()) + .push(&token_addr.to_db_key()) + .expect("Cannot obtain a storage key") .push(&BALANCE_STORAGE_KEY.to_owned()) .expect("Cannot obtain a storage key") } -/// Obtain a storage key prefix for multitoken balances. -pub fn multitoken_balance_prefix( - token_addr: &Address, - sub_prefix: &Key, -) -> Key { - Key::from(token_addr.to_db_key()).join(sub_prefix) +/// Obtain a storage key for the multitoken minter. +pub fn minter_key(token_addr: &Address) -> Key { + Key::from(Address::Internal(InternalAddress::Multitoken).to_db_key()) + .push(&token_addr.to_db_key()) + .expect("Cannot obtain a storage key") + .push(&MINTER_STORAGE_KEY.to_owned()) + .expect("Cannot obtain a storage key") } -/// Obtain a storage key for user's multitoken balance. -pub fn multitoken_balance_key(prefix: &Key, owner: &Address) -> Key { - prefix - .push(&BALANCE_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a storage key") - .push(&owner.to_db_key()) +/// Obtain a storage key for the minted multitoken balance. +pub fn minted_balance_key(token_addr: &Address) -> Key { + balance_prefix(token_addr) + .push(&MINTED_STORAGE_KEY.to_owned()) .expect("Cannot obtain a storage key") } /// Check if the given storage key is balance key for the given token. If it is, -/// returns the owner. +/// returns the owner. For minted balances, use [`is_any_minted_balance_key()`]. pub fn is_balance_key<'a>( token_addr: &Address, key: &'a Key, @@ -867,9 +821,15 @@ pub fn is_balance_key<'a>( match &key.segments[..] { [ DbKeySeg::AddressSeg(addr), - DbKeySeg::StringSeg(key), + DbKeySeg::AddressSeg(token), + DbKeySeg::StringSeg(balance), DbKeySeg::AddressSeg(owner), - ] if key == BALANCE_STORAGE_KEY && addr == token_addr => Some(owner), + ] if *addr == Address::Internal(InternalAddress::Multitoken) + && token == token_addr + && balance == BALANCE_STORAGE_KEY => + { + Some(owner) + } _ => None, } } @@ -879,25 +839,24 @@ pub fn is_balance_key<'a>( pub fn is_any_token_balance_key(key: &Key) -> Option<[&Address; 2]> { match &key.segments[..] { [ + DbKeySeg::AddressSeg(addr), DbKeySeg::AddressSeg(token), - DbKeySeg::StringSeg(key), + DbKeySeg::StringSeg(balance), DbKeySeg::AddressSeg(owner), - ] if key == BALANCE_STORAGE_KEY => Some([token, owner]), + ] if *addr == Address::Internal(InternalAddress::Multitoken) + && balance == BALANCE_STORAGE_KEY => + { + Some([token, owner]) + } _ => None, } } /// Obtain a storage key denomination of a token. -pub fn denom_key(token_addr: &Address, sub_prefix: Option<&Key>) -> Key { - match sub_prefix { - Some(sub) => Key::from(token_addr.to_db_key()) - .join(sub) - .push(&DENOM_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a storage key"), - None => Key::from(token_addr.to_db_key()) - .push(&DENOM_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a storage key"), - } +pub fn denom_key(token_addr: &Address) -> Key { + Key::from(token_addr.to_db_key()) + .push(&DENOM_STORAGE_KEY.to_owned()) + .expect("Cannot obtain a storage key") } /// Check if the given storage key is a denomination key for the given token. @@ -920,71 +879,37 @@ pub fn is_masp_key(key: &Key) -> bool { || key.starts_with(PIN_KEY_PREFIX))) } -/// Storage key for total supply of a token -pub fn total_supply_key(token_address: &Address) -> Key { - Key::from(token_address.to_db_key()) - .push(&TOTAL_SUPPLY_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a storage key") -} - -/// Is storage key for total supply of a specific token? -pub fn is_total_supply_key(key: &Key, token_address: &Address) -> bool { - matches!(&key.segments[..], [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(key)] if addr == token_address && key == TOTAL_SUPPLY_STORAGE_KEY) -} - -/// Check if the given storage key is multitoken balance key for the given -/// token. If it is, returns the sub prefix and the owner. -pub fn is_multitoken_balance_key<'a>( - token_addr: &Address, - key: &'a Key, -) -> Option<(Key, &'a Address)> { - match key.segments.first() { - Some(DbKeySeg::AddressSeg(addr)) if addr == token_addr => { - multitoken_balance_owner(key) +/// Check if the given storage key is for a minter of a unspecified token. +/// If it is, returns the token. +pub fn is_any_minter_key(key: &Key) -> Option<&Address> { + match &key.segments[..] { + [ + DbKeySeg::AddressSeg(addr), + DbKeySeg::AddressSeg(token), + DbKeySeg::StringSeg(minter), + ] if *addr == Address::Internal(InternalAddress::Multitoken) + && minter == MINTER_STORAGE_KEY => + { + Some(token) } _ => None, } } -/// Check if the given storage key is multitoken balance key for unspecified -/// token. If it is, returns the sub prefix and the token and owner addresses. -pub fn is_any_multitoken_balance_key( - key: &Key, -) -> Option<(Key, [&Address; 2])> { - match key.segments.first() { - Some(DbKeySeg::AddressSeg(token)) => multitoken_balance_owner(key) - .map(|(sub, owner)| (sub, [token, owner])), - _ => None, - } -} - -/// Check if the given storage key is token or multitoken balance key for -/// unspecified token. If it is, returns the token and owner addresses. -pub fn is_any_token_or_multitoken_balance_key( - key: &Key, -) -> Option<[&Address; 2]> { - is_any_multitoken_balance_key(key) - .map(|a| a.1) - .or_else(|| is_any_token_balance_key(key)) -} - -fn multitoken_balance_owner(key: &Key) -> Option<(Key, &Address)> { - let len = key.segments.len(); - if len < 4 { - // the key of a multitoken should have 1 or more segments other than - // token, balance, owner - return None; - } +/// Check if the given storage key is for total supply of a unspecified token. +/// If it is, returns the token. +pub fn is_any_minted_balance_key(key: &Key) -> Option<&Address> { match &key.segments[..] { [ - .., + DbKeySeg::AddressSeg(addr), + DbKeySeg::AddressSeg(token), DbKeySeg::StringSeg(balance), - DbKeySeg::AddressSeg(owner), - ] if balance == BALANCE_STORAGE_KEY => { - let sub_prefix = Key { - segments: key.segments[1..(len - 2)].to_vec(), - }; - Some((sub_prefix, owner)) + DbKeySeg::StringSeg(owner), + ] if *addr == Address::Internal(InternalAddress::Multitoken) + && balance == BALANCE_STORAGE_KEY + && owner == MINTED_STORAGE_KEY => + { + Some(token) } _ => None, } @@ -1011,8 +936,6 @@ pub struct Transfer { pub target: Address, /// Token's address pub token: Address, - /// Source token's sub prefix - pub sub_prefix: Option, /// The amount of tokens pub amount: DenominatedAmount, /// The unused storage location at which to place TxId diff --git a/core/src/types/uint.rs b/core/src/types/uint.rs index 637694a29c..ea935c1cc1 100644 --- a/core/src/types/uint.rs +++ b/core/src/types/uint.rs @@ -8,7 +8,6 @@ use std::ops::{Add, AddAssign, BitAnd, Div, Mul, Neg, Rem, Sub, SubAssign}; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use impl_num_traits::impl_uint_num_traits; use num_integer::Integer; -use serde::{Deserialize, Serialize}; use uint::construct_uint; use crate::types::token; @@ -31,8 +30,6 @@ construct_uint! { /// Namada native type to replace for unsigned 256 bit /// integers. #[derive( - Serialize, - Deserialize, BorshSerialize, BorshDeserialize, BorshSchema, @@ -41,6 +38,62 @@ construct_uint! { pub struct Uint(4); } +impl serde::Serialize for Uint { + fn serialize( + &self, + serializer: S, + ) -> std::result::Result + where + S: serde::Serializer, + { + let amount_string = self.to_string(); + serde::Serialize::serialize(&amount_string, serializer) + } +} + +impl<'de> serde::Deserialize<'de> for Uint { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + use serde::de::Error as serdeError; + let amount_string: String = + serde::Deserialize::deserialize(deserializer)?; + + let digits = amount_string + .chars() + .filter_map(|c| { + if c.is_ascii_digit() { + c.to_digit(10).map(Uint::from) + } else { + None + } + }) + .rev() + .collect::>(); + if digits.len() != amount_string.len() { + return Err(D::Error::custom(AmountParseError::FromString)); + } + if digits.len() > 77 { + return Err(D::Error::custom(AmountParseError::ScaleTooLarge( + digits.len() as u32, + 77, + ))); + } + let mut value = Uint::default(); + let ten = Uint::from(10); + for (pow, digit) in digits.into_iter().enumerate() { + value = ten + .checked_pow(Uint::from(pow)) + .and_then(|scaling| scaling.checked_mul(digit)) + .and_then(|scaled| value.checked_add(scaled)) + .ok_or(AmountParseError::PrecisionOverflow) + .map_err(D::Error::custom)?; + } + Ok(value) + } +} + impl_uint_num_traits!(Uint, 4); impl Integer for Uint { @@ -624,4 +677,15 @@ mod test_uint { assert!(-that <= -this); assert!(-that <= this); } + + #[test] + fn test_serialization_roundtrip() { + let amount: Uint = serde_json::from_str(r#""1000000000""#).unwrap(); + assert_eq!(amount, Uint::from(1000000000)); + let serialized = serde_json::to_string(&amount).unwrap(); + assert_eq!(serialized, r#""1000000000""#); + + let amount: Result = serde_json::from_str(r#""1000000000.2""#); + assert!(amount.is_err()); + } } diff --git a/core/src/types/voting_power.rs b/core/src/types/voting_power.rs index 96a9579279..946e08b834 100644 --- a/core/src/types/voting_power.rs +++ b/core/src/types/voting_power.rs @@ -31,9 +31,22 @@ use crate::types::uint::Uint; )] pub struct EthBridgeVotingPower(u64); -impl From for EthBridgeVotingPower { - fn from(val: u64) -> Self { - Self(val) +impl EthBridgeVotingPower { + /// Maximum value that can be represented for the voting power + /// stored in an Ethereum bridge smart contract. + pub const MAX: Self = Self(1 << 32); +} + +impl TryFrom for EthBridgeVotingPower { + type Error = (); + + #[inline] + fn try_from(val: u64) -> Result { + if val <= Self::MAX.0 { + Ok(Self(val)) + } else { + Err(()) + } } } @@ -41,7 +54,8 @@ impl From<&FractionalVotingPower> for EthBridgeVotingPower { fn from(ratio: &FractionalVotingPower) -> Self { // normalize the voting power // https://github.com/anoma/ethereum-bridge/blob/fe93d2e95ddb193a759811a79c8464ad4d709c12/test/utils/utilities.js#L29 - const NORMALIZED_VOTING_POWER: Uint = Uint::from_u64(1 << 32); + const NORMALIZED_VOTING_POWER: Uint = + Uint::from_u64(EthBridgeVotingPower::MAX.0); let voting_power = ratio.0 * NORMALIZED_VOTING_POWER; let voting_power = voting_power.round().to_integer().low_u64(); diff --git a/encoding_spec/Cargo.toml b/encoding_spec/Cargo.toml index 8ac5dc567d..61fe37995d 100644 --- a/encoding_spec/Cargo.toml +++ b/encoding_spec/Cargo.toml @@ -22,5 +22,5 @@ abciplus = [ namada = {path = "../shared"} borsh.workspace = true itertools.workspace = true -lazy_static = "1.4.0" +lazy_static.workspace = true madato = "0.5.3" diff --git a/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs b/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs index d4cd0370aa..0052fb01b1 100644 --- a/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs +++ b/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs @@ -26,9 +26,7 @@ use namada_core::types::ethereum_events::{ }; use namada_core::types::storage::{BlockHeight, Key, KeySeg}; use namada_core::types::token; -use namada_core::types::token::{ - balance_key, multitoken_balance_key, multitoken_balance_prefix, -}; +use namada_core::types::token::{balance_key, minted_balance_key}; use crate::parameters::read_native_erc20_address; use crate::protocol::transactions::update; @@ -234,8 +232,8 @@ where H: 'static + StorageHasher + Sync, { let mut changed_keys = BTreeSet::default(); - let keys: wrapped_erc20s::Keys = asset.into(); - let balance_key = keys.balance(receiver); + let token = wrapped_erc20s::token(asset); + let balance_key = balance_key(&token, receiver); update::amount(wl_storage, &balance_key, |balance| { tracing::debug!( %balance_key, @@ -251,7 +249,7 @@ where })?; _ = changed_keys.insert(balance_key); - let supply_key = keys.supply(); + let supply_key = minted_balance_key(&token); update::amount(wl_storage, &supply_key, |supply| { tracing::debug!( %supply_key, @@ -266,6 +264,10 @@ where ); })?; _ = changed_keys.insert(supply_key); + + // mint the token without a minter because a protocol tx doesn't need to + // trigger a VP + Ok(changed_keys) } @@ -477,12 +479,9 @@ where ); (escrow_balance_key, sender_balance_key) } else { - let sub_prefix = wrapped_erc20s::sub_prefix(&transfer.transfer.asset); - let prefix = multitoken_balance_prefix(&BRIDGE_ADDRESS, &sub_prefix); - let escrow_balance_key = - multitoken_balance_key(&prefix, &BRIDGE_POOL_ADDRESS); - let sender_balance_key = - multitoken_balance_key(&prefix, &transfer.transfer.sender); + let token = wrapped_erc20s::token(&transfer.transfer.asset); + let escrow_balance_key = balance_key(&token, &BRIDGE_POOL_ADDRESS); + let sender_balance_key = balance_key(&token, &transfer.transfer.sender); (escrow_balance_key, sender_balance_key) }; update::amount(wl_storage, &source, |balance| { @@ -518,15 +517,15 @@ where return Ok(changed_keys); } - let keys: wrapped_erc20s::Keys = (&transfer.transfer.asset).into(); + let token = wrapped_erc20s::token(&transfer.transfer.asset); - let escrow_balance_key = keys.balance(&BRIDGE_POOL_ADDRESS); + let escrow_balance_key = balance_key(&token, &BRIDGE_POOL_ADDRESS); update::amount(wl_storage, &escrow_balance_key, |balance| { balance.spend(&transfer.transfer.amount); })?; _ = changed_keys.insert(escrow_balance_key); - let supply_key = keys.supply(); + let supply_key = minted_balance_key(&token); update::amount(wl_storage, &supply_key, |supply| { supply.spend(&transfer.transfer.amount); })?; @@ -659,12 +658,8 @@ mod tests { ) .expect("Test failed"); } else { - let sub_prefix = - wrapped_erc20s::sub_prefix(&transfer.transfer.asset); - let prefix = - multitoken_balance_prefix(&BRIDGE_ADDRESS, &sub_prefix); - let sender_key = - multitoken_balance_key(&prefix, &transfer.transfer.sender); + let token = wrapped_erc20s::token(&transfer.transfer.asset); + let sender_key = balance_key(&token, &transfer.transfer.sender); let sender_balance = Amount::from(0); wl_storage .write_bytes( @@ -672,8 +667,7 @@ mod tests { sender_balance.try_to_vec().expect("Test failed"), ) .expect("Test failed"); - let escrow_key = - multitoken_balance_key(&prefix, &BRIDGE_POOL_ADDRESS); + let escrow_key = balance_key(&token, &BRIDGE_POOL_ADDRESS); let escrow_balance = Amount::from(10); wl_storage .write_bytes( @@ -681,11 +675,13 @@ mod tests { escrow_balance.try_to_vec().expect("Test failed"), ) .expect("Test failed"); - let asset_keys: wrapped_erc20s::Keys = - (&transfer.transfer.asset).into(); - update::amount(wl_storage, &asset_keys.supply(), |supply| { - supply.receive(&transfer.transfer.amount); - }) + update::amount( + wl_storage, + &minted_balance_key(&token), + |supply| { + supply.receive(&transfer.transfer.amount); + }, + ) .expect("Test failed"); }; let gas_fee = Amount::from(1); @@ -786,9 +782,9 @@ mod tests { ) .unwrap(); - let wdai: wrapped_erc20s::Keys = (&DAI_ERC20_ETH_ADDRESS).into(); - let receiver_balance_key = wdai.balance(&receiver); - let wdai_supply_key = wdai.supply(); + let wdai = wrapped_erc20s::token(&DAI_ERC20_ETH_ADDRESS); + let receiver_balance_key = balance_key(&wdai, &receiver); + let wdai_supply_key = minted_balance_key(&wdai); assert_eq!( stored_keys_count(&wl_storage), @@ -814,7 +810,7 @@ mod tests { let native_erc20 = read_native_erc20_address(&wl_storage).expect("Test failed"); let random_erc20 = EthAddress([0xff; 20]); - let random_erc20_keys: wrapped_erc20s::Keys = (&random_erc20).into(); + let random_erc20_token = wrapped_erc20s::token(&random_erc20); let pending_transfers = init_bridge_pool_transfers( &mut wl_storage, [native_erc20, random_erc20], @@ -853,10 +849,12 @@ mod tests { let mut changed_keys = act_on(&mut wl_storage, event).unwrap(); assert!( - changed_keys - .remove(&random_erc20_keys.balance(&BRIDGE_POOL_ADDRESS)) + changed_keys.remove(&balance_key( + &random_erc20_token, + &BRIDGE_POOL_ADDRESS + )) ); - assert!(changed_keys.remove(&random_erc20_keys.supply())); + assert!(changed_keys.remove(&minted_balance_key(&random_erc20_token))); assert!(changed_keys.remove(&payer_balance_key)); assert!(changed_keys.remove(&pool_balance_key)); assert!(changed_keys.remove(&get_nonce_key())); @@ -987,20 +985,15 @@ mod tests { .expect("Test failed"); assert_eq!(escrow_balance, Amount::from(0)); } else { - let sub_prefix = - wrapped_erc20s::sub_prefix(&transfer.transfer.asset); - let prefix = - multitoken_balance_prefix(&BRIDGE_ADDRESS, &sub_prefix); - let sender_key = - multitoken_balance_key(&prefix, &transfer.transfer.sender); + let token = wrapped_erc20s::token(&transfer.transfer.asset); + let sender_key = balance_key(&token, &transfer.transfer.sender); let value = wl_storage.read_bytes(&sender_key).expect("Test failed"); let sender_balance = Amount::try_from_slice(&value.expect("Test failed")) .expect("Test failed"); assert_eq!(sender_balance, transfer.transfer.amount); - let escrow_key = - multitoken_balance_key(&prefix, &BRIDGE_POOL_ADDRESS); + let escrow_key = balance_key(&token, &BRIDGE_POOL_ADDRESS); let value = wl_storage.read_bytes(&escrow_key).expect("Test failed"); let escrow_balance = @@ -1129,12 +1122,12 @@ mod tests { if asset == &native_erc20 { return None; } - let asset_keys: wrapped_erc20s::Keys = asset.into(); + let erc20_token = wrapped_erc20s::token(asset); let prev_balance = wl_storage - .read(&asset_keys.balance(&BRIDGE_POOL_ADDRESS)) + .read(&balance_key(&erc20_token, &BRIDGE_POOL_ADDRESS)) .expect("Test failed"); let prev_supply = wl_storage - .read(&asset_keys.supply()) + .read(&minted_balance_key(&erc20_token)) .expect("Test failed"); Some(Delta { asset: *asset, @@ -1163,14 +1156,14 @@ mod tests { .checked_sub(sent_amount) .expect("Test failed"); - let asset_keys: wrapped_erc20s::Keys = asset.into(); + let erc20_token = wrapped_erc20s::token(asset); let balance: token::Amount = wl_storage - .read(&asset_keys.balance(&BRIDGE_POOL_ADDRESS)) + .read(&balance_key(&erc20_token, &BRIDGE_POOL_ADDRESS)) .expect("Read must succeed") .expect("Balance must exist"); let supply: token::Amount = wl_storage - .read(&asset_keys.supply()) + .read(&minted_balance_key(&erc20_token)) .expect("Read must succeed") .expect("Balance must exist"); @@ -1189,19 +1182,19 @@ mod tests { test_wrapped_erc20s_aux(|wl_storage, event| { let native_erc20 = read_native_erc20_address(wl_storage).expect("Test failed"); - let wnam_keys: wrapped_erc20s::Keys = (&native_erc20).into(); + let wnam = wrapped_erc20s::token(&native_erc20); let escrow_balance_key = balance_key(&nam(), &BRIDGE_ADDRESS); // check pre supply assert!( wl_storage - .read_bytes(&wnam_keys.balance(&BRIDGE_POOL_ADDRESS)) + .read_bytes(&balance_key(&wnam, &BRIDGE_POOL_ADDRESS)) .expect("Test failed") .is_none() ); assert!( wl_storage - .read_bytes(&wnam_keys.supply()) + .read_bytes(&minted_balance_key(&wnam)) .expect("Test failed") .is_none() ); @@ -1217,13 +1210,13 @@ mod tests { // check post supply assert!( wl_storage - .read_bytes(&wnam_keys.balance(&BRIDGE_POOL_ADDRESS)) + .read_bytes(&balance_key(&wnam, &BRIDGE_POOL_ADDRESS)) .expect("Test failed") .is_none() ); assert!( wl_storage - .read_bytes(&wnam_keys.supply()) + .read_bytes(&minted_balance_key(&wnam)) .expect("Test failed") .is_none() ); diff --git a/ethereum_bridge/src/protocol/transactions/ethereum_events/mod.rs b/ethereum_bridge/src/protocol/transactions/ethereum_events/mod.rs index bb7b614187..d3cd32972d 100644 --- a/ethereum_bridge/src/protocol/transactions/ethereum_events/mod.rs +++ b/ethereum_bridge/src/protocol/transactions/ethereum_events/mod.rs @@ -284,7 +284,7 @@ mod tests { use namada_core::types::ethereum_events::{ EthereumEvent, TransferToNamada, }; - use namada_core::types::token::Amount; + use namada_core::types::token::{balance_key, minted_balance_key, Amount}; use super::*; use crate::protocol::transactions::utils::GetVoters; @@ -335,7 +335,7 @@ mod tests { apply_updates(&mut wl_storage, updates, voting_powers)?; let eth_msg_keys: vote_tallies::Keys = (&body).into(); - let wrapped_erc20_keys: wrapped_erc20s::Keys = (&asset).into(); + let wrapped_erc20_token = wrapped_erc20s::token(&asset); assert_eq!( BTreeSet::from_iter(vec![ eth_msg_keys.body(), @@ -343,8 +343,8 @@ mod tests { eth_msg_keys.seen_by(), eth_msg_keys.voting_power(), eth_msg_keys.voting_started_epoch(), - wrapped_erc20_keys.balance(&receiver), - wrapped_erc20_keys.supply(), + balance_key(&wrapped_erc20_token, &receiver), + minted_balance_key(&wrapped_erc20_token), ]), changed_keys ); @@ -375,8 +375,8 @@ mod tests { let epoch_bytes = epoch_bytes.unwrap(); assert_eq!(Epoch::try_from_slice(&epoch_bytes)?, Epoch(0)); - let wrapped_erc20_balance_bytes = - wl_storage.read_bytes(&wrapped_erc20_keys.balance(&receiver))?; + let wrapped_erc20_balance_bytes = wl_storage + .read_bytes(&balance_key(&wrapped_erc20_token, &receiver))?; let wrapped_erc20_balance_bytes = wrapped_erc20_balance_bytes.unwrap(); assert_eq!( Amount::try_from_slice(&wrapped_erc20_balance_bytes)?, @@ -384,7 +384,7 @@ mod tests { ); let wrapped_erc20_supply_bytes = - wl_storage.read_bytes(&wrapped_erc20_keys.supply())?; + wl_storage.read_bytes(&minted_balance_key(&wrapped_erc20_token))?; let wrapped_erc20_supply_bytes = wrapped_erc20_supply_bytes.unwrap(); assert_eq!( Amount::try_from_slice(&wrapped_erc20_supply_bytes)?, @@ -435,7 +435,7 @@ mod tests { "No gas should be used for a derived transaction" ); let eth_msg_keys = vote_tallies::Keys::from(&event); - let dai_keys = wrapped_erc20s::Keys::from(&DAI_ERC20_ETH_ADDRESS); + let dai_token = wrapped_erc20s::token(&DAI_ERC20_ETH_ADDRESS); assert_eq!( tx_result.changed_keys, BTreeSet::from_iter(vec![ @@ -444,8 +444,8 @@ mod tests { eth_msg_keys.seen_by(), eth_msg_keys.voting_power(), eth_msg_keys.voting_started_epoch(), - dai_keys.balance(&receiver), - dai_keys.supply(), + balance_key(&dai_token, &receiver), + minted_balance_key(&dai_token), ]) ); assert!(tx_result.vps_result.accepted_vps.is_empty()); diff --git a/genesis/dev.toml b/genesis/dev.toml index b6eb070b42..19985a3b9e 100644 --- a/genesis/dev.toml +++ b/genesis/dev.toml @@ -28,7 +28,6 @@ net_address = "127.0.0.1:26656" [token.NAM] address = "atest1v4ehgw36x3prswzxggunzv6pxqmnvdj9xvcyzvpsggeyvs3cg9qnywf589qnwvfsg5erg3fkl09rg5" denom = 8 -vp = "vp_token" [token.NAM.balances] # In token balances, we can use: # 1. An address any account @@ -45,7 +44,6 @@ Bertha = "1000000" [token.BTC] address = "atest1v4ehgw36xdzryve5gsc52veeg5cnsv2yx5eygvp38qcrvd29xy6rys6p8yc5xvp4xfpy2v694wgwcp" denom = 8 -vp = "vp_token" [token.BTC.balances] atest1v4ehgw368ycryv2z8qcnxv3cxgmrgvjpxs6yg333gym5vv2zxepnj334g4rryvj9xucrgve4x3xvr4 = 1000000 atest1v4ehgw36x3qng3jzggu5yvpsxgcngv2xgguy2dpkgvu5x33kx3pr2w2zgep5xwfkxscrxs2pj8075p = 1000000 @@ -55,7 +53,6 @@ a1qyqzsqqqqqcyvvf5xcu5vd6rg4z5233hg9pn23pjgdryzdjy8pz52wzxxscnvvjxx3rryvzz8y5p6m [token.ETH] address = "atest1v4ehgw36xqmr2d3nx3ryvd2xxgmrq33j8qcns33sxezrgv6zxdzrydjrxveygd2yxumrsdpsf9jc2p" denom = 18 -vp = "vp_token" [token.ETH.balances] atest1v4ehgw368ycryv2z8qcnxv3cxgmrgvjpxs6yg333gym5vv2zxepnj334g4rryvj9xucrgve4x3xvr4 = 1000000 atest1v4ehgw36x3qng3jzggu5yvpsxgcngv2xgguy2dpkgvu5x33kx3pr2w2zgep5xwfkxscrxs2pj8075p = 1000000 @@ -65,7 +62,6 @@ a1qyqzsqqqqqcyvvf5xcu5vd6rg4z5233hg9pn23pjgdryzdjy8pz52wzxxscnvvjxx3rryvzz8y5p6m [token.DOT] address = "atest1v4ehgw36gg6nvs2zgfpyxsfjgc65yv6pxy6nwwfsxgungdzrggeyzv35gveyxsjyxymyz335hur2jn" denom = 10 -vp = "vp_token" [token.DOT.balances] atest1v4ehgw368ycryv2z8qcnxv3cxgmrgvjpxs6yg333gym5vv2zxepnj334g4rryvj9xucrgve4x3xvr4 = 1000000 atest1v4ehgw36x3qng3jzggu5yvpsxgcngv2xgguy2dpkgvu5x33kx3pr2w2zgep5xwfkxscrxs2pj8075p = 1000000 @@ -75,7 +71,6 @@ a1qyqzsqqqqqcyvvf5xcu5vd6rg4z5233hg9pn23pjgdryzdjy8pz52wzxxscnvvjxx3rryvzz8y5p6m [token.schnitzel] address = "atest1v4ehgw36xue5xvf5xvuyzvpjx5un2v3k8qeyvd3cxdqns32p89rrxd6xx9zngvpegccnzs699rdnnt" denom = 6 -vp = "vp_token" [token.schnitzel.balances] atest1v4ehgw368ycryv2z8qcnxv3cxgmrgvjpxs6yg333gym5vv2zxepnj334g4rryvj9xucrgve4x3xvr4 = 1000000 atest1v4ehgw36x3qng3jzggu5yvpsxgcngv2xgguy2dpkgvu5x33kx3pr2w2zgep5xwfkxscrxs2pj8075p = 1000000 @@ -85,7 +80,6 @@ a1qyqzsqqqqqcyvvf5xcu5vd6rg4z5233hg9pn23pjgdryzdjy8pz52wzxxscnvvjxx3rryvzz8y5p6m [token.apfel] address = "atest1v4ehgw36gfryydj9g3p5zv3kg9znyd358ycnzsfcggc5gvecgc6ygs2rxv6ry3zpg4zrwdfeumqcz9" denom = 6 -vp = "vp_token" [token.apfel.balances] atest1v4ehgw368ycryv2z8qcnxv3cxgmrgvjpxs6yg333gym5vv2zxepnj334g4rryvj9xucrgve4x3xvr4 = 1000000 atest1v4ehgw36x3qng3jzggu5yvpsxgcngv2xgguy2dpkgvu5x33kx3pr2w2zgep5xwfkxscrxs2pj8075p = 1000000 @@ -96,7 +90,6 @@ a1qyqzsqqqqqcyvvf5xcu5vd6rg4z5233hg9pn23pjgdryzdjy8pz52wzxxscnvvjxx3rryvzz8y5p6m address = "atest1v4ehgw36gep5ysecxq6nyv3jg3zygv3e89qn2vp48pryxsf4xpznvve5gvmy23fs89pryvf5a6ht90" denom = 6 public_key = "" -vp = "vp_token" [token.kartoffel.balances] atest1v4ehgw368ycryv2z8qcnxv3cxgmrgvjpxs6yg333gym5vv2zxepnj334g4rryvj9xucrgve4x3xvr4 = 1000000 atest1v4ehgw36x3qng3jzggu5yvpsxgcngv2xgguy2dpkgvu5x33kx3pr2w2zgep5xwfkxscrxs2pj8075p = 1000000 @@ -140,11 +133,6 @@ sha256 = "dc7b97f0448f2369bd2401c3c1d8898f53cac8c464a8c1b1f7f81415a658625d" # filename (relative to wasm path used by the node) filename = "vp_validator.wasm" -# Token VP -[wasm.vp_token] -filename = "vp_token.wasm" -sha256 = "e428a11f570d21dd3c871f5d35de6fe18098eb8ee0456b3e11a72ccdd8685cd0" - # General protocol parameters. [parameters] # Minimum number of blocks in an epoch. diff --git a/genesis/e2e-tests-single-node.toml b/genesis/e2e-tests-single-node.toml index b17ab94e8b..fcb109d034 100644 --- a/genesis/e2e-tests-single-node.toml +++ b/genesis/e2e-tests-single-node.toml @@ -4,8 +4,8 @@ genesis_time = "2021-09-30T10:00:00Z" native_token = "NAM" -faucet_pow_difficulty = 0 -faucet_withdrawal_limit = "1000000000" +faucet_pow_difficulty = 1 +faucet_withdrawal_limit = "1000" [validator.validator-0] # Validator's staked NAM at genesis. @@ -28,7 +28,6 @@ net_address = "127.0.0.1:27656" [token.NAM] address = "atest1v4ehgw36x3prswzxggunzv6pxqmnvdj9xvcyzvpsggeyvs3cg9qnywf589qnwvfsg5erg3fkl09rg5" denom = 6 -vp = "vp_token" [token.NAM.balances] Albert = "1000000" "Albert.public_key" = "100" @@ -45,7 +44,6 @@ faucet = "9223372036854" [token.BTC] address = "atest1v4ehgw36xdzryve5gsc52veeg5cnsv2yx5eygvp38qcrvd29xy6rys6p8yc5xvp4xfpy2v694wgwcp" denom = 8 -vp = "vp_token" [token.BTC.balances] Albert = "1000000" Bertha = "1000000" @@ -57,7 +55,6 @@ faucet = "9223372036854" [token.ETH] address = "atest1v4ehgw36xqmr2d3nx3ryvd2xxgmrq33j8qcns33sxezrgv6zxdzrydjrxveygd2yxumrsdpsf9jc2p" denom = 18 -vp = "vp_token" [token.ETH.balances] Albert = "1000000" Bertha = "1000000" @@ -69,7 +66,6 @@ faucet = "9223372036854" [token.DOT] address = "atest1v4ehgw36gg6nvs2zgfpyxsfjgc65yv6pxy6nwwfsxgungdzrggeyzv35gveyxsjyxymyz335hur2jn" denom = 10 -vp = "vp_token" [token.DOT.balances] Albert = "1000000" Bertha = "1000000" @@ -81,7 +77,6 @@ faucet = "9223372036854" [token.Schnitzel] address = "atest1v4ehgw36xue5xvf5xvuyzvpjx5un2v3k8qeyvd3cxdqns32p89rrxd6xx9zngvpegccnzs699rdnnt" denom = 6 -vp = "vp_token" [token.Schnitzel.balances] Albert = "1000000" Bertha = "1000000" @@ -93,7 +88,6 @@ faucet = "9223372036854" [token.Apfel] address = "atest1v4ehgw36gfryydj9g3p5zv3kg9znyd358ycnzsfcggc5gvecgc6ygs2rxv6ry3zpg4zrwdfeumqcz9" denom = 6 -vp = "vp_token" [token.Apfel.balances] Albert = "1000000" Bertha = "1000000" @@ -106,7 +100,6 @@ faucet = "9223372036854" address = "atest1v4ehgw36gep5ysecxq6nyv3jg3zygv3e89qn2vp48pryxsf4xpznvve5gvmy23fs89pryvf5a6ht90" public_key = "" denom = 6 -vp = "vp_token" [token.Kartoffel.balances] Albert = "1000000" Bertha = "1000000" @@ -151,10 +144,6 @@ filename = "vp_user.wasm" # filename (relative to wasm path used by the node) filename = "vp_validator.wasm" -# Token VP -[wasm.vp_token] -filename = "vp_token.wasm" - # Faucet VP [wasm.vp_testnet_faucet] filename = "vp_testnet_faucet.wasm" diff --git a/proof_of_stake/src/epoched.rs b/proof_of_stake/src/epoched.rs index 4899ae1e1d..d5a567fc94 100644 --- a/proof_of_stake/src/epoched.rs +++ b/proof_of_stake/src/epoched.rs @@ -24,11 +24,14 @@ pub const LAST_UPDATE_SUB_KEY: &str = "last_update"; /// Sub-key for an epoched data structure's oldest epoch with some data pub const OLDEST_EPOCH_SUB_KEY: &str = "oldest_epoch"; +/// Default number of past epochs to keep. +const DEFAULT_NUM_PAST_EPOCHS: u64 = 2; + /// Discrete epoched data handle pub struct Epoched< Data, FutureEpochs, - const NUM_PAST_EPOCHS: u64 = 0, + const NUM_PAST_EPOCHS: u64 = DEFAULT_NUM_PAST_EPOCHS, SON = collections::Simple, > { storage_prefix: storage::Key, @@ -38,8 +41,11 @@ pub struct Epoched< } /// Discrete epoched data handle with nested lazy structure -pub type NestedEpoched = - Epoched; +pub type NestedEpoched< + Data, + FutureEpochs, + const NUM_PAST_EPOCHS: u64 = DEFAULT_NUM_PAST_EPOCHS, +> = Epoched; /// Delta epoched data handle pub struct EpochedDelta { @@ -659,6 +665,29 @@ where } } +/// Zero offset +#[derive( + Debug, + Clone, + BorshDeserialize, + BorshSerialize, + BorshSchema, + PartialEq, + Eq, + PartialOrd, + Ord, +)] +pub struct OffsetZero; +impl EpochOffset for OffsetZero { + fn value(_paras: &PosParams) -> u64 { + 0 + } + + fn dyn_offset() -> DynEpochOffset { + DynEpochOffset::Zero + } +} + /// Offset at pipeline length. #[derive( Debug, @@ -731,6 +760,8 @@ impl EpochOffset for OffsetPipelinePlusUnbondingLen { /// Offset length dynamic choice. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum DynEpochOffset { + /// Zero offset + Zero, /// Offset at pipeline length - 1 PipelineLenMinusOne, /// Offset at pipeline length. diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 7f7265be91..4c087dbbd6 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -56,8 +56,9 @@ use storage::{ validator_address_raw_hash_key, validator_last_slash_key, validator_max_commission_rate_change_key, BondDetails, BondsAndUnbondsDetail, BondsAndUnbondsDetails, EpochedSlashes, - ReverseOrdTokenAmount, RewardsAccumulator, SlashedAmount, UnbondDetails, - ValidatorAddresses, ValidatorUnbondRecords, + ReverseOrdTokenAmount, RewardsAccumulator, SlashedAmount, + TotalConsensusStakes, UnbondDetails, ValidatorAddresses, + ValidatorUnbondRecords, }; use thiserror::Error; use types::{ @@ -84,6 +85,10 @@ pub fn staking_token_address(storage: &impl StorageRead) -> Address { .expect("Must be able to read native token address") } +/// Number of epochs below the current epoch for which full validator sets are +/// stored +const STORE_VALIDATOR_SETS_LEN: u64 = 2; + #[allow(missing_docs)] #[derive(Error, Debug)] pub enum GenesisError { @@ -96,6 +101,8 @@ pub enum GenesisError { pub enum InflationError { #[error("Error in calculating rewards: {0}")] Rewards(rewards::RewardsError), + #[error("Expected validator {0} to be in consensus set but got: {1:?}")] + ExpectedValidatorInConsensus(Address, Option), } #[allow(missing_docs)] @@ -274,6 +281,12 @@ pub fn validator_eth_cold_key_handle( ValidatorEthColdKeys::open(key) } +/// Get the storage handle to the total consensus validator stake +pub fn total_consensus_stake_key_handle() -> TotalConsensusStakes { + let key = storage::total_consensus_stake_key(); + TotalConsensusStakes::open(key) +} + /// Get the storage handle to a PoS validator's state pub fn validator_state_handle(validator: &Address) -> ValidatorStates { let key = storage::validator_state_key(validator); @@ -476,6 +489,9 @@ where )?; } + // Store the total consensus validator stake to storage + store_total_consensus_stake(storage, current_epoch)?; + // Write total deltas to storage total_deltas_handle().init_at_genesis( storage, @@ -488,13 +504,7 @@ where credit_tokens(storage, &staking_token, &ADDRESS, total_bonded)?; // Copy the genesis validator set into the pipeline epoch as well for epoch in (current_epoch.next()).iter_range(params.pipeline_len) { - copy_validator_sets_and_positions( - storage, - current_epoch, - epoch, - &consensus_validator_set_handle(), - &below_capacity_validator_set_handle(), - )?; + copy_validator_sets_and_positions(storage, current_epoch, epoch)?; } tracing::debug!("Genesis initialized"); @@ -748,7 +758,7 @@ where pub fn read_consensus_validator_set_addresses_with_stake( storage: &S, epoch: namada_core::types::storage::Epoch, -) -> storage_api::Result> +) -> storage_api::Result> where S: StorageRead, { @@ -792,7 +802,7 @@ where pub fn read_below_capacity_validator_set_addresses_with_stake( storage: &S, epoch: namada_core::types::storage::Epoch, -) -> storage_api::Result> +) -> storage_api::Result> where S: StorageRead, { @@ -1528,14 +1538,15 @@ pub fn copy_validator_sets_and_positions( storage: &mut S, current_epoch: Epoch, target_epoch: Epoch, - consensus_validator_set: &ConsensusValidatorSets, - below_capacity_validator_set: &BelowCapacityValidatorSets, ) -> storage_api::Result<()> where S: StorageRead + StorageWrite, { let prev_epoch = target_epoch.prev(); + let consensus_validator_set = consensus_validator_set_handle(); + let below_capacity_validator_set = below_capacity_validator_set_handle(); + let (consensus, below_capacity) = ( consensus_validator_set.at(&prev_epoch), below_capacity_validator_set.at(&prev_epoch), @@ -1597,28 +1608,31 @@ where // Copy validator positions let mut positions = HashMap::::default(); - let positions_handle = validator_set_positions_handle().at(&prev_epoch); + let validator_set_positions_handle = validator_set_positions_handle(); + let positions_handle = validator_set_positions_handle.at(&prev_epoch); + for result in positions_handle.iter(storage)? { let (validator, position) = result?; positions.insert(validator, position); } - let new_positions_handle = - validator_set_positions_handle().at(&target_epoch); + + let new_positions_handle = validator_set_positions_handle.at(&target_epoch); for (validator, position) in positions { let prev = new_positions_handle.insert(storage, validator, position)?; debug_assert!(prev.is_none()); } - validator_set_positions_handle().set_last_update(storage, current_epoch)?; + validator_set_positions_handle.set_last_update(storage, current_epoch)?; // Copy set of all validator addresses let mut all_validators = HashSet::
::default(); - let all_validators_handle = validator_addresses_handle().at(&prev_epoch); + let validator_addresses_handle = validator_addresses_handle(); + let all_validators_handle = validator_addresses_handle.at(&prev_epoch); for result in all_validators_handle.iter(storage)? { let validator = result?; all_validators.insert(validator); } let new_all_validators_handle = - validator_addresses_handle().at(&target_epoch); + validator_addresses_handle.at(&target_epoch); for validator in all_validators { let was_in = new_all_validators_handle.insert(storage, validator)?; debug_assert!(!was_in); @@ -1627,6 +1641,68 @@ where Ok(()) } +/// Compute total validator stake for the current epoch +fn compute_total_consensus_stake( + storage: &S, + epoch: Epoch, +) -> storage_api::Result +where + S: StorageRead, +{ + consensus_validator_set_handle() + .at(&epoch) + .iter(storage)? + .fold(Ok(token::Amount::zero()), |acc, entry| { + let acc = acc?; + let ( + NestedSubKey::Data { + key: amount, + nested_sub_key: _, + }, + _validator, + ) = entry?; + Ok(acc + amount) + }) +} + +/// Store total consensus stake +pub fn store_total_consensus_stake( + storage: &mut S, + epoch: Epoch, +) -> storage_api::Result<()> +where + S: StorageRead + StorageWrite, +{ + let total = compute_total_consensus_stake(storage, epoch)?; + tracing::debug!( + "Computed total consensus stake for epoch {}: {}", + epoch, + total.to_string_native() + ); + total_consensus_stake_key_handle().set(storage, total, epoch, 0) +} + +/// Purge the validator sets from the epochs older than the current epoch minus +/// `STORE_VALIDATOR_SETS_LEN` +pub fn purge_validator_sets_for_old_epoch( + storage: &mut S, + epoch: Epoch, +) -> storage_api::Result<()> +where + S: StorageRead + StorageWrite, +{ + if Epoch(STORE_VALIDATOR_SETS_LEN) < epoch { + let old_epoch = epoch - STORE_VALIDATOR_SETS_LEN - 1; + consensus_validator_set_handle() + .get_data_handler() + .remove_all(storage, &old_epoch)?; + below_capacity_validator_set_handle() + .get_data_handler() + .remove_all(storage, &old_epoch)?; + } + Ok(()) +} + /// Read the position of the validator in the subset of validators that have the /// same bonded stake. This information is held in its own epoched structure in /// addition to being inside the validator sets. @@ -2455,6 +2531,41 @@ where Ok((total, total_active)) } +/// Get the genesis consensus validators stake and consensus key for Tendermint, +/// converted from [`ValidatorSetUpdate`]s using the given function. +pub fn genesis_validator_set_tendermint( + storage: &S, + params: &PosParams, + current_epoch: Epoch, + mut f: impl FnMut(ValidatorSetUpdate) -> T, +) -> storage_api::Result> +where + S: StorageRead, +{ + let consensus_validator_handle = + consensus_validator_set_handle().at(¤t_epoch); + let iter = consensus_validator_handle.iter(storage)?; + + iter.map(|validator| { + let ( + NestedSubKey::Data { + key: new_stake, + nested_sub_key: _, + }, + address, + ) = validator?; + let consensus_key = validator_consensus_key_handle(&address) + .get(storage, current_epoch, params)? + .unwrap(); + let converted = f(ValidatorSetUpdate::Consensus(ConsensusValidator { + consensus_key, + bonded_stake: new_stake, + })); + Ok(converted) + }) + .collect() +} + /// Communicate imminent validator set updates to Tendermint. This function is /// called two blocks before the start of a new epoch because Tendermint /// validator updates become active two blocks after the updates are submitted. @@ -2753,6 +2864,42 @@ where } } +/// Collect the details of all of the enqueued slashes to be processed in future +/// epochs into a nested map +pub fn find_all_enqueued_slashes( + storage: &S, + epoch: Epoch, +) -> storage_api::Result>>> +where + S: StorageRead, +{ + let mut enqueued = HashMap::>>::new(); + for res in enqueued_slashes_handle().get_data_handler().iter(storage)? { + let ( + NestedSubKey::Data { + key: processing_epoch, + nested_sub_key: + NestedSubKey::Data { + key: address, + nested_sub_key: _, + }, + }, + slash, + ) = res?; + if processing_epoch <= epoch { + continue; + } + + let slashes = enqueued + .entry(address) + .or_default() + .entry(processing_epoch) + .or_default(); + slashes.push(slash); + } + Ok(enqueued) +} + /// Find all slashes and the associated validators in the PoS system pub fn find_all_slashes( storage: &S, @@ -3136,7 +3283,11 @@ where let state = validator_state_handle(&validator_address) .get(storage, epoch, ¶ms)?; if state != Some(ValidatorState::Consensus) { - continue; + return Err(InflationError::ExpectedValidatorInConsensus( + validator_address, + state, + )) + .into_storage_result(); } let stake_from_deltas = @@ -3252,9 +3403,9 @@ where for epoch in Epoch::iter_bounds_inclusive(start_epoch, end_epoch) { let consensus_stake = - Dec::from(get_total_consensus_stake(storage, epoch)?); + Dec::from(get_total_consensus_stake(storage, epoch, params)?); tracing::debug!( - "Consensus stake in epoch {}: {}", + "Total consensus stake in epoch {}: {}", epoch, consensus_stake ); @@ -3263,6 +3414,7 @@ where let infracting_stake = slashes.iter(storage)?.fold( Ok(Dec::zero()), |acc: storage_api::Result, res| { + let acc = acc?; let ( NestedSubKey::Data { key: validator, @@ -3276,11 +3428,7 @@ where .unwrap_or_default(); // println!("Val {} stake: {}", &validator, validator_stake); - if let Ok(inner) = acc { - Ok(inner + Dec::from(validator_stake)) - } else { - acc - } + Ok(acc + Dec::from(validator_stake)) // TODO: does something more complex need to be done // here in the event some of these slashes correspond to // the same validator? @@ -3438,8 +3586,10 @@ where } } } + // Safe sub cause `validator_set_update_epoch > current_epoch` + let start_offset = validator_set_update_epoch.0 - current_epoch.0; // Set the validator state as `Jailed` thru the pipeline epoch - for offset in 1..=params.pipeline_len { + for offset in start_offset..=params.pipeline_len { validator_state_handle(validator).set( storage, ValidatorState::Jailed, @@ -3856,22 +4006,14 @@ where fn get_total_consensus_stake( storage: &S, epoch: Epoch, + params: &PosParams, ) -> storage_api::Result where S: StorageRead, { - let mut total = token::Amount::default(); - for res in consensus_validator_set_handle().at(&epoch).iter(storage)? { - let ( - NestedSubKey::Data { - key: bonded_stake, - nested_sub_key: _, - }, - _validator, - ) = res?; - total += bonded_stake; - } - Ok(total) + total_consensus_stake_key_handle() + .get(storage, epoch, params) + .map(|o| o.expect("Total consensus stake could not be retrieved.")) } /// Find slashes applicable to a validator with inclusive `start` and exclusive diff --git a/proof_of_stake/src/storage.rs b/proof_of_stake/src/storage.rs index 79124fe3bb..54bd7cfe6b 100644 --- a/proof_of_stake/src/storage.rs +++ b/proof_of_stake/src/storage.rs @@ -36,6 +36,7 @@ const VALIDATOR_TOTAL_UNBONDED_STORAGE_KEY: &str = "total_unbonded"; const VALIDATOR_SETS_STORAGE_PREFIX: &str = "validator_sets"; const CONSENSUS_VALIDATOR_SET_STORAGE_KEY: &str = "consensus"; const BELOW_CAPACITY_VALIDATOR_SET_STORAGE_KEY: &str = "below_capacity"; +const TOTAL_CONSENSUS_STAKE_STORAGE_KEY: &str = "total_consensus_stake"; const TOTAL_DELTAS_STORAGE_KEY: &str = "total_deltas"; const VALIDATOR_SET_POSITIONS_KEY: &str = "validator_set_positions"; const CONSENSUS_KEYS: &str = "consensus_keys"; @@ -584,6 +585,21 @@ pub fn is_below_capacity_validator_set_key(key: &Key) -> bool { matches!(&key.segments[..], [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(key), DbKeySeg::StringSeg(set_type), DbKeySeg::StringSeg(lazy_map), DbKeySeg::StringSeg(data), DbKeySeg::StringSeg(_epoch), DbKeySeg::StringSeg(_), DbKeySeg::StringSeg(_amount), DbKeySeg::StringSeg(_), DbKeySeg::StringSeg(_position)] if addr == &ADDRESS && key == VALIDATOR_SETS_STORAGE_PREFIX && set_type == BELOW_CAPACITY_VALIDATOR_SET_STORAGE_KEY && lazy_map == LAZY_MAP_SUB_KEY && data == lazy_map::DATA_SUBKEY) } +/// Storage key for total consensus stake +pub fn total_consensus_stake_key() -> Key { + Key::from(ADDRESS.to_db_key()) + .push(&TOTAL_CONSENSUS_STAKE_STORAGE_KEY.to_owned()) + .expect("Cannot obtain a total consensus stake key") +} + +/// Is storage key for the total consensus stake? +pub fn is_total_consensus_stake_key(key: &Key) -> bool { + matches!(&key.segments[..], [ + DbKeySeg::AddressSeg(addr), + DbKeySeg::StringSeg(key) + ] if addr == &ADDRESS && key == TOTAL_CONSENSUS_STAKE_STORAGE_KEY) +} + /// Storage key for total deltas of all validators. pub fn total_deltas_key() -> Key { Key::from(ADDRESS.to_db_key()) diff --git a/proof_of_stake/src/tests.rs b/proof_of_stake/src/tests.rs index 451a6880fe..6476827417 100644 --- a/proof_of_stake/src/tests.rs +++ b/proof_of_stake/src/tests.rs @@ -43,15 +43,17 @@ use crate::{ copy_validator_sets_and_positions, find_validator_by_raw_hash, get_num_consensus_validators, init_genesis, insert_validator_into_validator_set, is_validator, process_slashes, + purge_validator_sets_for_old_epoch, read_below_capacity_validator_set_addresses_with_stake, read_below_threshold_validator_set_addresses, read_consensus_validator_set_addresses_with_stake, read_total_stake, read_validator_delta_value, read_validator_stake, slash, - staking_token_address, total_deltas_handle, unbond_handle, unbond_tokens, - unjail_validator, update_validator_deltas, update_validator_set, - validator_consensus_key_handle, validator_set_update_tendermint, - validator_slashes_handle, validator_state_handle, withdraw_tokens, - write_validator_address_raw_hash, BecomeValidator, + staking_token_address, store_total_consensus_stake, total_deltas_handle, + unbond_handle, unbond_tokens, unjail_validator, update_validator_deltas, + update_validator_set, validator_consensus_key_handle, + validator_set_update_tendermint, validator_slashes_handle, + validator_state_handle, withdraw_tokens, write_validator_address_raw_hash, + BecomeValidator, STORE_VALIDATOR_SETS_LEN, }; proptest! { @@ -1159,8 +1161,7 @@ fn test_validator_sets() { .unwrap(); }; - // Start with two genesis validators with 1 NAM stake - let epoch = Epoch::default(); + // Create genesis validators let ((val1, pk1), stake1) = (gen_validator(), token::Amount::native_whole(1)); let ((val2, pk2), stake2) = @@ -1183,6 +1184,9 @@ fn test_validator_sets() { println!("val6: {val6}, {pk6}, {}", stake6.to_string_native()); println!("val7: {val7}, {pk7}, {}", stake7.to_string_native()); + let start_epoch = Epoch::default(); + let epoch = start_epoch; + init_genesis( &mut s, ¶ms, @@ -1749,6 +1753,28 @@ fn test_validator_sets() { }) ); assert_eq!(tm_updates[1], ValidatorSetUpdate::Deactivated(pk4)); + + // Check that the validator sets were purged for the old epochs + let last_epoch = epoch; + for e in Epoch::iter_bounds_inclusive( + start_epoch, + last_epoch + .sub_or_default(Epoch(STORE_VALIDATOR_SETS_LEN)) + .sub_or_default(Epoch(1)), + ) { + assert!( + consensus_validator_set_handle() + .at(&e) + .is_empty(&s) + .unwrap() + ); + assert!( + below_capacity_validator_set_handle() + .at(&e) + .is_empty(&s) + .unwrap() + ); + } } /// When a consensus set validator with 0 voting power adds a bond in the same @@ -2003,14 +2029,14 @@ fn get_tendermint_set_updates( fn advance_epoch(s: &mut TestWlStorage, params: &PosParams) -> Epoch { s.storage.block.epoch = s.storage.block.epoch.next(); let current_epoch = s.storage.block.epoch; + store_total_consensus_stake(s, current_epoch).unwrap(); copy_validator_sets_and_positions( s, current_epoch, current_epoch + params.pipeline_len, - &consensus_validator_set_handle(), - &below_capacity_validator_set_handle(), ) .unwrap(); + purge_validator_sets_for_old_epoch(s, current_epoch).unwrap(); // process_slashes(s, current_epoch).unwrap(); // dbg!(current_epoch); current_epoch diff --git a/proof_of_stake/src/types.rs b/proof_of_stake/src/types.rs index caf705fce3..736ffe7a46 100644 --- a/proof_of_stake/src/types.rs +++ b/proof_of_stake/src/types.rs @@ -29,6 +29,10 @@ use crate::parameters::PosParams; // core::types::token::NATIVE_MAX_DECIMAL_PLACES?? const U64_MAX: u64 = u64::MAX; +/// Number of epochs below the current epoch for which validator deltas and +/// slashes are stored +const VALIDATOR_DELTAS_SLASHES_LEN: u64 = 23; + // TODO: add this to the spec /// Stored positions of validators in validator sets pub type ValidatorSetPositions = crate::epoched::NestedEpoched< @@ -121,18 +125,22 @@ pub type BelowCapacityValidatorSets = crate::epoched::NestedEpoched< crate::epoched::OffsetPipelineLen, >; +/// Epoched total consensus validator stake +pub type TotalConsensusStakes = + crate::epoched::Epoched; + /// Epoched validator's deltas. pub type ValidatorDeltas = crate::epoched::EpochedDelta< token::Change, crate::epoched::OffsetUnbondingLen, - 23, + VALIDATOR_DELTAS_SLASHES_LEN, >; /// Epoched total deltas. pub type TotalDeltas = crate::epoched::EpochedDelta< token::Change, crate::epoched::OffsetUnbondingLen, - 23, + VALIDATOR_DELTAS_SLASHES_LEN, >; /// Epoched validator commission rate @@ -164,7 +172,7 @@ pub type ValidatorSlashes = NestedMap; pub type EpochedSlashes = crate::epoched::NestedEpoched< ValidatorSlashes, crate::epoched::OffsetUnbondingLen, - 23, + VALIDATOR_DELTAS_SLASHES_LEN, >; /// Epoched validator's unbonds diff --git a/scripts/generator.sh b/scripts/generator.sh index 16ced33298..701f8fbabe 100755 --- a/scripts/generator.sh +++ b/scripts/generator.sh @@ -19,7 +19,7 @@ elif [ "$1" = "server" ]; then sed -i 's/^epochs_per_year = 31_536_000$/epochs_per_year = 262_800/' genesis/test-vectors-single-node.toml - NAMADA_GENESIS_FILE=$(cargo run --bin namadac -- --mode validator utils init-network --genesis-path genesis/test-vectors-single-node.toml --wasm-checksums-path wasm/checksums.json --chain-prefix e2e-test --unsafe-dont-encrypt --localhost --allow-duplicate-ip | grep 'Genesis file generated at ' | sed 's/^Genesis file generated at //') + NAMADA_GENESIS_FILE=$(cargo run --bin namadac -- utils init-network --genesis-path genesis/test-vectors-single-node.toml --wasm-checksums-path wasm/checksums.json --chain-prefix e2e-test --unsafe-dont-encrypt --localhost --allow-duplicate-ip | grep 'Genesis file generated at ' | sed 's/^Genesis file generated at //') rm genesis/test-vectors-single-node.toml @@ -31,7 +31,7 @@ elif [ "$1" = "server" ]; then cp $NAMADA_BASE_DIR/setup/other/wallet.toml $NAMADA_BASE_DIR/wallet.toml - cargo run --bin namadan -- --mode validator --base-dir $NAMADA_BASE_DIR/setup/validator-0/.namada/ ledger + cargo run --bin namadan -- --base-dir $NAMADA_BASE_DIR/setup/validator-0/.namada/ ledger elif [ "$1" = "client" ]; then echo > $NAMADA_TX_LOG_PATH @@ -296,17 +296,17 @@ elif [ "$1" = "client" ]; then cargo run --bin namadaw -- masp add --alias bb_payment_address --value patest1vqe0vyxh6wmhahwa52gthgd6edgqxfmgyv8e94jtwn55mdvpvylcyqnp59595272qrz3zxn0ysg - cargo run --bin namadac --features std -- --mode full transfer --source albert --target aa_payment_address --token btc --amount 20 --force --ledger-address 127.0.0.1:27657 + cargo run --bin namadac --features std -- transfer --source albert --target aa_payment_address --token btc --amount 20 --force --ledger-address 127.0.0.1:27657 - cargo run --bin namadac --features std -- --mode full transfer --source a_spending_key --target ab_payment_address --token btc --amount 7 --force --ledger-address 127.0.0.1:27657 + cargo run --bin namadac --features std -- transfer --source a_spending_key --target ab_payment_address --token btc --amount 7 --force --ledger-address 127.0.0.1:27657 - until cargo run --bin namadac -- --mode full epoch --ledger-address 127.0.0.1:27657 | grep -m1 "Last committed epoch: 2" ; do sleep 10 ; done; + until cargo run --bin namadac -- epoch --ledger-address 127.0.0.1:27657 | grep -m1 "Last committed epoch: 2" ; do sleep 10 ; done; - cargo run --bin namadac --features std -- --mode full transfer --source a_spending_key --target bb_payment_address --token btc --amount 7 --force --ledger-address 127.0.0.1:27657 + cargo run --bin namadac --features std -- transfer --source a_spending_key --target bb_payment_address --token btc --amount 7 --force --ledger-address 127.0.0.1:27657 - cargo run --bin namadac --features std -- --mode full transfer --source a_spending_key --target bb_payment_address --token btc --amount 6 --force --ledger-address 127.0.0.1:27657 + cargo run --bin namadac --features std -- transfer --source a_spending_key --target bb_payment_address --token btc --amount 6 --force --ledger-address 127.0.0.1:27657 - cargo run --bin namadac --features std -- --mode full transfer --source b_spending_key --target bb_payment_address --token btc --amount 6 --force --ledger-address 127.0.0.1:27657 + cargo run --bin namadac --features std -- transfer --source b_spending_key --target bb_payment_address --token btc --amount 6 --force --ledger-address 127.0.0.1:27657 rm proposal_submission_valid_proposal.json diff --git a/shared/src/ledger/args.rs b/shared/src/ledger/args.rs index 815dea3792..151e2a089c 100644 --- a/shared/src/ledger/args.rs +++ b/shared/src/ledger/args.rs @@ -117,8 +117,6 @@ pub struct TxTransfer { pub target: C::TransferTarget, /// Transferred token address pub token: C::Address, - /// Transferred token address - pub sub_prefix: Option, /// Transferred token amount pub amount: InputAmount, /// Native token address @@ -149,8 +147,6 @@ pub struct TxIbcTransfer { pub receiver: String, /// Transferred token addres s pub token: C::Address, - /// Transferred token address - pub sub_prefix: Option, /// Transferred token amount pub amount: token::Amount, /// Port ID @@ -332,8 +328,6 @@ pub struct QueryBalance { pub token: Option, /// Whether not to convert balances pub no_conversions: bool, - /// Sub prefix of an account - pub sub_prefix: Option, } /// Query historical transfer(s) @@ -345,8 +339,6 @@ pub struct QueryTransfers { pub owner: Option, /// Address of a token pub token: Option, - /// sub-prefix if querying a multi-token - pub sub_prefix: Option, } /// Query PoS bond(s) @@ -371,6 +363,17 @@ pub struct QueryBondedStake { pub epoch: Option, } +/// Query the state of a validator (its validator set or if it is jailed) +#[derive(Clone, Debug)] +pub struct QueryValidatorState { + /// Common query args + pub query: Query, + /// Address of a validator + pub validator: C::Address, + /// Epoch in which to find the validator state + pub epoch: Option, +} + #[derive(Clone, Debug)] /// Commission rate change args pub struct CommissionRateChange { @@ -392,7 +395,7 @@ pub struct TxUnjailValidator { /// Validator address (should be self) pub validator: C::Address, /// Path to the TX WASM code file - pub tx_code_path: C::Data, + pub tx_code_path: PathBuf, } /// Query PoS commission rate diff --git a/shared/src/ledger/eth_bridge/bridge_pool.rs b/shared/src/ledger/eth_bridge/bridge_pool.rs index f25de56fe5..c6110866ba 100644 --- a/shared/src/ledger/eth_bridge/bridge_pool.rs +++ b/shared/src/ledger/eth_bridge/bridge_pool.rs @@ -8,7 +8,6 @@ use std::sync::Arc; use borsh::BorshSerialize; use ethbridge_bridge_contract::Bridge; use ethers::providers::Middleware; -use namada_core::ledger::eth_bridge::storage::wrapped_erc20s; use namada_core::ledger::eth_bridge::ADDRESS as BRIDGE_ADDRESS; use namada_core::types::key::common; use owo_colors::OwoColorize; @@ -50,10 +49,8 @@ pub async fn build_bridge_pool_tx( }: args::EthereumBridgePool, fee_payer: common::PublicKey, ) -> Result { - let sub_prefix = Some(wrapped_erc20s::sub_prefix(&asset)); - let DenominatedAmount { amount, .. } = - validate_amount(client, amount, &BRIDGE_ADDRESS, &sub_prefix, tx.force) + validate_amount(client, amount, &BRIDGE_ADDRESS, tx.force) .await .expect("Failed to validate amount"); diff --git a/shared/src/ledger/events/log.rs b/shared/src/ledger/events/log.rs index 931f0088c4..a2dc3978d0 100644 --- a/shared/src/ledger/events/log.rs +++ b/shared/src/ledger/events/log.rs @@ -67,6 +67,12 @@ impl EventLog { /// Returns a new iterator over this [`EventLog`]. #[inline] + pub fn iter(&self) -> impl Iterator { + self.queue.iter() + } + + /// Returns a filtering iterator over this [`EventLog`]. + #[inline] pub fn iter_with_matcher( &self, matcher: dumb_queries::QueryMatcher, diff --git a/shared/src/ledger/ibc/vp/context.rs b/shared/src/ledger/ibc/vp/context.rs index 0da78bba53..daf2246cbe 100644 --- a/shared/src/ledger/ibc/vp/context.rs +++ b/shared/src/ledger/ibc/vp/context.rs @@ -2,7 +2,7 @@ use std::collections::{BTreeSet, HashMap, HashSet}; -use borsh::{BorshDeserialize, BorshSerialize}; +use borsh::BorshSerialize; use namada_core::ledger::ibc::storage::is_ibc_key; use namada_core::ledger::ibc::{IbcCommonContext, IbcStorageContext}; use namada_core::ledger::storage::write_log::StorageModification; @@ -11,9 +11,7 @@ use namada_core::ledger::storage_api::StorageRead; use namada_core::types::address::{Address, InternalAddress}; use namada_core::types::ibc::IbcEvent; use namada_core::types::storage::{BlockHeight, Header, Key}; -use namada_core::types::token::{ - is_any_token_balance_key, is_any_token_or_multitoken_balance_key, Amount, -}; +use namada_core::types::token::{self, Amount}; use super::Error; use crate::ledger::native_vp::CtxPreStorageRead; @@ -117,50 +115,104 @@ where fn transfer_token( &mut self, - src: &Key, - dest: &Key, + src: &Address, + dest: &Address, + token: &Address, amount: Amount, ) -> Result<(), Self::Error> { - let src_owner = is_any_token_or_multitoken_balance_key(src); - let mut src_bal = match src_owner { - Some([_, Address::Internal(InternalAddress::IbcMint)]) => { - Amount::max() - } - Some([_, Address::Internal(InternalAddress::IbcBurn)]) => { - unreachable!("Invalid transfer from IBC burn address") - } - _ => match self.read(src)? { - Some(v) => { - Amount::try_from_slice(&v[..]).map_err(Error::Decoding)? - } - None => unreachable!("The source has no balance"), - }, - }; + let src_key = token::balance_key(token, src); + let dest_key = token::balance_key(token, dest); + let src_bal: Option = + self.ctx.read(&src_key).map_err(Error::NativeVpError)?; + let mut src_bal = src_bal.expect("The source has no balance"); src_bal.spend(&amount); - let dest_owner = is_any_token_balance_key(dest); - let mut dest_bal = match dest_owner { - Some([_, Address::Internal(InternalAddress::IbcMint)]) => { - unreachable!("Invalid transfer to IBC mint address") - } - _ => match self.read(dest)? { - Some(v) => { - Amount::try_from_slice(&v[..]).map_err(Error::Decoding)? - } - None => Amount::default(), - }, - }; + let mut dest_bal: Amount = self + .ctx + .read(&dest_key) + .map_err(Error::NativeVpError)? + .unwrap_or_default(); dest_bal.receive(&amount); self.write( - src, + &src_key, src_bal.try_to_vec().expect("encoding shouldn't failed"), )?; self.write( - dest, + &dest_key, dest_bal.try_to_vec().expect("encoding shouldn't failed"), + ) + } + + fn mint_token( + &mut self, + target: &Address, + token: &Address, + amount: Amount, + ) -> Result<(), Self::Error> { + let target_key = token::balance_key(token, target); + let mut target_bal: Amount = self + .ctx + .read(&target_key) + .map_err(Error::NativeVpError)? + .unwrap_or_default(); + target_bal.receive(&amount); + + let minted_key = token::minted_balance_key(token); + let mut minted_bal: Amount = self + .ctx + .read(&minted_key) + .map_err(Error::NativeVpError)? + .unwrap_or_default(); + minted_bal.receive(&amount); + + self.write( + &target_key, + target_bal.try_to_vec().expect("encoding shouldn't failed"), + )?; + self.write( + &minted_key, + minted_bal.try_to_vec().expect("encoding shouldn't failed"), )?; - Ok(()) + let minter_key = token::minter_key(token); + self.write( + &minter_key, + Address::Internal(InternalAddress::Ibc) + .try_to_vec() + .expect("encoding shouldn't failed"), + ) + } + + fn burn_token( + &mut self, + target: &Address, + token: &Address, + amount: Amount, + ) -> Result<(), Self::Error> { + let target_key = token::balance_key(token, target); + let mut target_bal: Amount = self + .ctx + .read(&target_key) + .map_err(Error::NativeVpError)? + .unwrap_or_default(); + target_bal.spend(&amount); + + let minted_key = token::minted_balance_key(token); + let mut minted_bal: Amount = self + .ctx + .read(&minted_key) + .map_err(Error::NativeVpError)? + .unwrap_or_default(); + minted_bal.spend(&amount); + + self.write( + &target_key, + target_bal.try_to_vec().expect("encoding shouldn't failed"), + )?; + self.write( + &minted_key, + minted_bal.try_to_vec().expect("encoding shouldn't failed"), + ) } /// Get the current height of this chain @@ -254,16 +306,34 @@ where unimplemented!("Validation doesn't emit an event") } - /// Transfer token fn transfer_token( &mut self, - _src: &Key, - _dest: &Key, + _src: &Address, + _dest: &Address, + _token: &Address, _amount: Amount, ) -> Result<(), Self::Error> { unimplemented!("Validation doesn't transfer") } + fn mint_token( + &mut self, + _target: &Address, + _token: &Address, + _amount: Amount, + ) -> Result<(), Self::Error> { + unimplemented!("Validation doesn't mint") + } + + fn burn_token( + &mut self, + _target: &Address, + _token: &Address, + _amount: Amount, + ) -> Result<(), Self::Error> { + unimplemented!("Validation doesn't burn") + } + fn get_height(&self) -> Result { self.ctx.get_block_height().map_err(Error::NativeVpError) } diff --git a/shared/src/ledger/ibc/vp/denom.rs b/shared/src/ledger/ibc/vp/denom.rs deleted file mode 100644 index d58edfdc33..0000000000 --- a/shared/src/ledger/ibc/vp/denom.rs +++ /dev/null @@ -1,85 +0,0 @@ -//! IBC validity predicate for denom - -use prost::Message; -use thiserror::Error; - -use super::Ibc; -use crate::ibc::applications::transfer::packet::PacketData; -use crate::ibc::core::ics04_channel::msgs::PacketMsg; -use crate::ibc::core::ics26_routing::msgs::MsgEnvelope; -use crate::ibc_proto::google::protobuf::Any; -use crate::ledger::ibc::storage; -use crate::ledger::native_vp::VpEnv; -use crate::ledger::storage::{self as ledger_storage, StorageHasher}; -use crate::types::storage::KeySeg; -use crate::vm::WasmCacheAccess; - -#[allow(missing_docs)] -#[derive(Error, Debug)] -pub enum Error { - #[error("Decoding IBC data error: {0}")] - DecodingData(prost::DecodeError), - #[error("Invalid message: {0}")] - IbcMessage(String), - #[error("Decoding PacketData error: {0}")] - DecodingPacketData(serde_json::Error), - #[error("Denom error: {0}")] - Denom(String), -} - -/// IBC channel functions result -pub type Result = std::result::Result; - -impl<'a, DB, H, CA> Ibc<'a, DB, H, CA> -where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, - H: 'static + StorageHasher, - CA: 'static + WasmCacheAccess, -{ - pub(super) fn validate_denom(&self, tx_data: &[u8]) -> Result<()> { - let ibc_msg = Any::decode(tx_data).map_err(Error::DecodingData)?; - let envelope: MsgEnvelope = ibc_msg.try_into().map_err(|e| { - Error::IbcMessage(format!( - "Decoding a MsgRecvPacket failed: Error {}", - e - )) - })?; - // A transaction only with MsgRecvPacket can update the denom store - let msg = match envelope { - MsgEnvelope::Packet(PacketMsg::Recv(msg)) => msg, - _ => { - return Err(Error::IbcMessage( - "Non-MsgRecvPacket message updated the denom store" - .to_string(), - )); - } - }; - let data = serde_json::from_slice::(&msg.packet.data) - .map_err(Error::DecodingPacketData)?; - let denom = format!( - "{}/{}/{}", - &msg.packet.port_id_on_b, - &msg.packet.chan_id_on_b, - &data.token.denom, - ); - let token_hash = storage::calc_hash(&denom); - let denom_key = storage::ibc_denom_key(token_hash.raw()); - match self.ctx.read_bytes_post(&denom_key) { - Ok(Some(v)) => match std::str::from_utf8(&v) { - Ok(d) if d == denom => Ok(()), - Ok(d) => Err(Error::Denom(format!( - "Mismatch the denom: original {}, denom {}", - denom, d - ))), - Err(e) => Err(Error::Denom(format!( - "Decoding the denom failed: key {}, error {}", - denom_key, e - ))), - }, - _ => Err(Error::Denom(format!( - "Looking up the denom failed: Key {}", - denom_key - ))), - } - } -} diff --git a/shared/src/ledger/ibc/vp/mod.rs b/shared/src/ledger/ibc/vp/mod.rs index 730dc40f33..b8537f2463 100644 --- a/shared/src/ledger/ibc/vp/mod.rs +++ b/shared/src/ledger/ibc/vp/mod.rs @@ -1,8 +1,6 @@ //! IBC integration as a native validity predicate mod context; -mod denom; -mod token; use std::cell::RefCell; use std::collections::{BTreeSet, HashSet}; @@ -10,7 +8,6 @@ use std::rc::Rc; use std::time::Duration; use context::{PseudoExecutionContext, VpValidationContext}; -use namada_core::ledger::ibc::storage::{is_ibc_denom_key, is_ibc_key}; use namada_core::ledger::ibc::{ Error as ActionError, IbcActions, TransferModule, ValidationParams, }; @@ -21,8 +18,8 @@ use namada_core::types::address::{Address, InternalAddress}; use namada_core::types::storage::Key; use namada_proof_of_stake::read_pos_params; use thiserror::Error; -pub use token::{Error as IbcTokenError, IbcToken}; +use crate::ledger::ibc::storage::{calc_hash, is_ibc_denom_key, is_ibc_key}; use crate::ledger::native_vp::{self, Ctx, NativeVp, VpEnv}; use crate::ledger::parameters::read_epoch_duration_parameter; use crate::vm::WasmCacheAccess; @@ -40,8 +37,8 @@ pub enum Error { IbcAction(ActionError), #[error("State change error: {0}")] StateChange(String), - #[error("Denom store error: {0}")] - Denom(denom::Error), + #[error("Denom error: {0}")] + Denom(String), #[error("IBC event error: {0}")] IbcEvent(String), } @@ -86,9 +83,7 @@ where self.validate_with_msg(&tx_data)?; // Validate the denom store if a denom key has been changed - if keys_changed.iter().any(is_ibc_denom_key) { - self.validate_denom(&tx_data).map_err(Error::Denom)?; - } + self.validate_denom(keys_changed)?; Ok(true) } @@ -173,6 +168,35 @@ where upgrade_path: Vec::new(), }) } + + fn validate_denom(&self, keys_changed: &BTreeSet) -> VpResult<()> { + for key in keys_changed { + if let Some(hash) = is_ibc_denom_key(key) { + match self.ctx.read_post::(key).map_err(|e| { + Error::Denom(format!( + "Getting the denom failed: Key {}, Error {}", + key, e + )) + })? { + Some(denom) => { + if calc_hash(&denom) != hash { + return Err(Error::Denom(format!( + "The denom is invalid: Key {}, Denom {}", + key, denom + ))); + } + } + None => { + return Err(Error::Denom(format!( + "The corresponding denom wasn't stored: Key {}", + key + ))); + } + } + } + } + Ok(()) + } } fn match_value( @@ -1022,7 +1046,7 @@ mod tests { // client connection list let client_conn_key = client_connections_key(&msg.client_id_on_a); let conn_list = conn_id.to_string(); - let bytes = conn_list.as_bytes().to_vec(); + let bytes = conn_list.try_to_vec().expect("encoding failed"); wl_storage .write_log .write(&client_conn_key, bytes) @@ -1129,7 +1153,7 @@ mod tests { // client connection list let client_conn_key = client_connections_key(&msg.client_id_on_a); let conn_list = conn_id.to_string(); - let bytes = conn_list.as_bytes().to_vec(); + let bytes = conn_list.try_to_vec().expect("encoding failed"); wl_storage .write_log .write(&client_conn_key, bytes) @@ -1244,7 +1268,7 @@ mod tests { // client connection list let client_conn_key = client_connections_key(&msg.client_id_on_b); let conn_list = conn_id.to_string(); - let bytes = conn_list.as_bytes().to_vec(); + let bytes = conn_list.try_to_vec().expect("encoding failed"); wl_storage .write_log .write(&client_conn_key, bytes) @@ -2188,7 +2212,7 @@ mod tests { )); let trace_hash = calc_hash(coin.denom.to_string()); let denom_key = ibc_denom_key(&trace_hash); - let bytes = coin.denom.to_string().as_bytes().to_vec(); + let bytes = coin.denom.to_string().try_to_vec().unwrap(); wl_storage .write_log .write(&denom_key, bytes) @@ -2435,7 +2459,7 @@ mod tests { .expect("write failed"); // init the escrow balance let balance_key = - balance_key(&nam(), &Address::Internal(InternalAddress::IbcEscrow)); + balance_key(&nam(), &Address::Internal(InternalAddress::Ibc)); let amount = Amount::native_whole(100); wl_storage .write_log @@ -2585,7 +2609,7 @@ mod tests { .expect("write failed"); // init the escrow balance let balance_key = - balance_key(&nam(), &Address::Internal(InternalAddress::IbcEscrow)); + balance_key(&nam(), &Address::Internal(InternalAddress::Ibc)); let amount = Amount::native_whole(100); wl_storage .write_log diff --git a/shared/src/ledger/ibc/vp/token.rs b/shared/src/ledger/ibc/vp/token.rs deleted file mode 100644 index 18234abd35..0000000000 --- a/shared/src/ledger/ibc/vp/token.rs +++ /dev/null @@ -1,377 +0,0 @@ -//! IBC token transfer validation as a native validity predicate - -use std::collections::{BTreeSet, HashMap, HashSet}; - -use borsh::BorshDeserialize; -use prost::Message; -use thiserror::Error; - -use crate::ibc::applications::transfer::coin::PrefixedCoin; -use crate::ibc::applications::transfer::error::TokenTransferError; -use crate::ibc::applications::transfer::msgs::transfer::{ - MsgTransfer, TYPE_URL as MSG_TRANSFER_TYPE_URL, -}; -use crate::ibc::applications::transfer::packet::PacketData; -use crate::ibc::applications::transfer::{ - is_receiver_chain_source, is_sender_chain_source, -}; -use crate::ibc::core::ics04_channel::msgs::PacketMsg; -use crate::ibc::core::ics04_channel::packet::Packet; -use crate::ibc::core::ics26_routing::error::RouterError; -use crate::ibc::core::ics26_routing::msgs::MsgEnvelope; -use crate::ibc_proto::google::protobuf::Any; -use crate::ledger::ibc::storage as ibc_storage; -use crate::ledger::native_vp::{self, Ctx, NativeVp, VpEnv}; -use crate::ledger::storage::{self as ledger_storage, StorageHasher}; -use crate::proto::Tx; -use crate::types::address::{Address, InternalAddress}; -use crate::types::storage::Key; -use crate::types::token::{self, Amount, AmountParseError}; -use crate::vm::WasmCacheAccess; - -#[allow(missing_docs)] -#[derive(Error, Debug)] -pub enum Error { - #[error("Native VP error: {0}")] - NativeVpError(native_vp::Error), - #[error("IBC message error: {0}")] - IbcMessage(RouterError), - #[error("Invalid message")] - InvalidMessage, - #[error("Parsing amount error: {0}")] - Amount(AmountParseError), - #[error("Decoding error: {0}")] - Decoding(std::io::Error), - #[error("Decoding IBC data error: {0}")] - DecodingIbcData(prost::DecodeError), - #[error("Decoding PacketData error: {0}")] - DecodingPacketData(serde_json::Error), - #[error("IBC message is required as transaction data")] - NoTxData, - #[error("Invalid denom: {0}")] - Denom(String), - #[error("Invalid MsgTransfer: {0}")] - MsgTransfer(TokenTransferError), - #[error("Invalid token transfer: {0}")] - TokenTransfer(String), -} - -/// Result for IBC token VP -pub type Result = std::result::Result; - -/// IBC token VP to validate the transfer for an IBC-specific account. The -/// account is a sub-prefixed account with an IBC token hash, or a normal -/// account for `IbcEscrow`, `IbcBurn`, or `IbcMint`. -pub struct IbcToken<'a, DB, H, CA> -where - DB: ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, - H: StorageHasher, - CA: 'static + WasmCacheAccess, -{ - /// Context to interact with the host structures. - pub ctx: Ctx<'a, DB, H, CA>, -} - -impl<'a, DB, H, CA> NativeVp for IbcToken<'a, DB, H, CA> -where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, - H: 'static + StorageHasher, - CA: 'static + WasmCacheAccess, -{ - type Error = Error; - - const ADDR: InternalAddress = InternalAddress::IbcBurn; - - fn validate_tx( - &self, - tx_data: &Tx, - keys_changed: &BTreeSet, - _verifiers: &BTreeSet
, - ) -> Result { - let signed = tx_data; - let tx_data = signed.data().ok_or(Error::NoTxData)?; - - // Check the non-onwer balance updates - let ibc_keys_changed: HashSet = keys_changed - .iter() - .filter(|k| { - matches!( - token::is_any_token_balance_key(k), - Some([ - _, - Address::Internal( - InternalAddress::IbcEscrow - | InternalAddress::IbcBurn - | InternalAddress::IbcMint - ) - ]) - ) - }) - .cloned() - .collect(); - if ibc_keys_changed.is_empty() { - // some multitoken balances are changed - let mut changes = HashMap::new(); - for key in keys_changed { - if let Some((sub_prefix, _)) = - token::is_any_multitoken_balance_key(key) - { - if !ibc_storage::is_ibc_sub_prefix(&sub_prefix) { - continue; - } - let pre: token::Amount = - self.ctx.read_pre(key)?.unwrap_or_default(); - let post: token::Amount = - self.ctx.read_post(key)?.unwrap_or_default(); - let this_change = post.change() - pre.change(); - let change: token::Change = - changes.get(&sub_prefix).cloned().unwrap_or_default(); - changes.insert(sub_prefix, change + this_change); - } - } - if changes.iter().all(|(_, c)| c.is_zero()) { - return Ok(true); - } else { - return Err(Error::TokenTransfer( - "Invalid transfer between different origin accounts" - .to_owned(), - )); - } - } else if ibc_keys_changed.len() > 1 { - // a transaction can update at most 1 special IBC account for now - return Err(Error::TokenTransfer( - "Invalid transfer for multiple non-owner balances".to_owned(), - )); - } - - // Check the message - let ibc_msg = - Any::decode(&tx_data[..]).map_err(Error::DecodingIbcData)?; - match ibc_msg.type_url.as_str() { - MSG_TRANSFER_TYPE_URL => { - let msg = MsgTransfer::try_from(ibc_msg) - .map_err(Error::MsgTransfer)?; - self.validate_sending_token(&msg) - } - _ => { - let envelope: MsgEnvelope = - ibc_msg.try_into().map_err(Error::IbcMessage)?; - match envelope { - MsgEnvelope::Packet(PacketMsg::Recv(msg)) => { - self.validate_receiving_token(&msg.packet) - } - MsgEnvelope::Packet(PacketMsg::Ack(msg)) => { - self.validate_refunding_token(&msg.packet) - } - MsgEnvelope::Packet(PacketMsg::Timeout(msg)) => { - self.validate_refunding_token(&msg.packet) - } - MsgEnvelope::Packet(PacketMsg::TimeoutOnClose(msg)) => { - self.validate_refunding_token(&msg.packet) - } - _ => Err(Error::InvalidMessage), - } - } - } - } -} - -impl<'a, DB, H, CA> IbcToken<'a, DB, H, CA> -where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, - H: 'static + StorageHasher, - CA: 'static + WasmCacheAccess, -{ - fn validate_sending_token(&self, msg: &MsgTransfer) -> Result { - let mut coin = msg.token.clone(); - // lookup the original denom with the IBC token hash - if let Some(token_hash) = - ibc_storage::token_hash_from_denom(&coin.denom).map_err(|e| { - Error::Denom(format!("Invalid denom: error {}", e)) - })? - { - let denom_key = ibc_storage::ibc_denom_key(token_hash); - coin.denom = match self.ctx.read_bytes_pre(&denom_key) { - Ok(Some(v)) => String::from_utf8(v).map_err(|e| { - Error::Denom(format!( - "Decoding the denom string failed: {}", - e - )) - })?, - _ => { - return Err(Error::Denom(format!( - "No original denom: denom_key {}", - denom_key - ))); - } - }; - } - let coin = PrefixedCoin::try_from(coin).map_err(Error::MsgTransfer)?; - let token = ibc_storage::token(coin.denom.to_string()) - .map_err(|e| Error::Denom(e.to_string()))?; - let amount = Amount::try_from(coin.amount).map_err(Error::Amount)?; - - // check the denomination field - let change = if is_sender_chain_source( - msg.port_id_on_a.clone(), - msg.chan_id_on_a.clone(), - &coin.denom, - ) { - // source zone - // check the amount of the token has been escrowed - let target_key = token::balance_key( - &token, - &Address::Internal(InternalAddress::IbcEscrow), - ); - let pre = - try_decode_token_amount(self.ctx.read_bytes_pre(&target_key)?)? - .unwrap_or_default(); - let post = try_decode_token_amount( - self.ctx.read_bytes_post(&target_key)?, - )? - .unwrap_or_default(); - post.change() - pre.change() - } else { - // sink zone - // check the amount of the token has been burned - let target_key = token::balance_key( - &token, - &Address::Internal(InternalAddress::IbcBurn), - ); - let post = try_decode_token_amount( - self.ctx.read_bytes_temp(&target_key)?, - )? - .unwrap_or_default(); - // the previous balance of the burn address should be zero - post.change() - }; - - if change == amount.change() { - Ok(true) - } else { - Err(Error::TokenTransfer(format!( - "Sending the token is invalid: coin {}", - coin, - ))) - } - } - - fn validate_receiving_token(&self, packet: &Packet) -> Result { - let data = serde_json::from_slice::(&packet.data) - .map_err(Error::DecodingPacketData)?; - let token = ibc_storage::token(data.token.denom.to_string()) - .map_err(|e| Error::Denom(e.to_string()))?; - let amount = - Amount::try_from(data.token.amount).map_err(Error::Amount)?; - - let change = if is_receiver_chain_source( - packet.port_id_on_a.clone(), - packet.chan_id_on_a.clone(), - &data.token.denom, - ) { - // this chain is the source - // check the amount of the token has been unescrowed - let source_key = token::balance_key( - &token, - &Address::Internal(InternalAddress::IbcEscrow), - ); - let pre = - try_decode_token_amount(self.ctx.read_bytes_pre(&source_key)?)? - .unwrap_or_default(); - let post = try_decode_token_amount( - self.ctx.read_bytes_post(&source_key)?, - )? - .unwrap_or_default(); - pre.change() - post.change() - } else { - // the sender is the source - // check the amount of the token has been minted - let source_key = token::balance_key( - &token, - &Address::Internal(InternalAddress::IbcMint), - ); - let post = try_decode_token_amount( - self.ctx.read_bytes_temp(&source_key)?, - )? - .unwrap_or_default(); - // the previous balance of the mint address should be the maximum - Amount::max_signed().change() - post.change() - }; - - if change == amount.change() { - Ok(true) - } else { - Err(Error::TokenTransfer(format!( - "Receivinging the token is invalid: coin {}", - data.token - ))) - } - } - - fn validate_refunding_token(&self, packet: &Packet) -> Result { - let data = serde_json::from_slice::(&packet.data) - .map_err(Error::DecodingPacketData)?; - let token = ibc_storage::token(data.token.denom.to_string()) - .map_err(|e| Error::Denom(e.to_string()))?; - let amount = - Amount::try_from(data.token.amount).map_err(Error::Amount)?; - - // check the denom field - let change = if is_sender_chain_source( - packet.port_id_on_a.clone(), - packet.chan_id_on_a.clone(), - &data.token.denom, - ) { - // source zone: unescrow the token for the refund - let source_key = token::balance_key( - &token, - &Address::Internal(InternalAddress::IbcEscrow), - ); - let pre = - try_decode_token_amount(self.ctx.read_bytes_pre(&source_key)?)? - .unwrap_or_default(); - let post = try_decode_token_amount( - self.ctx.read_bytes_post(&source_key)?, - )? - .unwrap_or_default(); - pre.change() - post.change() - } else { - // sink zone: mint the token for the refund - let source_key = token::balance_key( - &token, - &Address::Internal(InternalAddress::IbcMint), - ); - let post = try_decode_token_amount( - self.ctx.read_bytes_temp(&source_key)?, - )? - .unwrap_or_default(); - // the previous balance of the mint address should be the maximum - Amount::max_signed().change() - post.change() - }; - - if change == amount.change() { - Ok(true) - } else { - Err(Error::TokenTransfer(format!( - "Refunding the token is invalid: coin {}", - data.token, - ))) - } - } -} - -impl From for Error { - fn from(err: native_vp::Error) -> Self { - Self::NativeVpError(err) - } -} - -fn try_decode_token_amount( - bytes: Option>, -) -> Result> { - if let Some(bytes) = bytes { - let tokens = Amount::try_from_slice(&bytes).map_err(Error::Decoding)?; - return Ok(Some(tokens)); - } - Ok(None) -} diff --git a/shared/src/ledger/masp.rs b/shared/src/ledger/masp.rs index b6ccd8672b..a843e050cf 100644 --- a/shared/src/ledger/masp.rs +++ b/shared/src/ledger/masp.rs @@ -50,7 +50,7 @@ use masp_proofs::bellman::groth16::PreparedVerifyingKey; use masp_proofs::bls12_381::Bls12; use masp_proofs::prover::LocalTxProver; use masp_proofs::sapling::SaplingVerificationContext; -use namada_core::types::token::{Change, MaspDenom, TokenAddress}; +use namada_core::types::token::{Change, MaspDenom}; use namada_core::types::transaction::AffineCurve; #[cfg(feature = "masp-tx-gen")] use rand_core::{CryptoRng, OsRng, RngCore}; @@ -345,9 +345,6 @@ impl pub trait ShieldedUtils: Sized + BorshDeserialize + BorshSerialize + Default + Clone { - /// The type of the Tendermint client to make queries with - type C: crate::ledger::queries::Client + std::marker::Sync; - /// Get a MASP transaction prover fn local_tx_prover(&self) -> LocalTxProver; @@ -407,7 +404,7 @@ pub enum PinnedBalanceError { // #[derive(BorshSerialize, BorshDeserialize, Debug, Clone)] // pub struct MaspAmount { -// pub asset: TokenAddress, +// pub asset: Address, // pub amount: token::Amount, // } @@ -415,7 +412,7 @@ pub enum PinnedBalanceError { #[derive(BorshSerialize, BorshDeserialize, Debug, Clone)] pub struct MaspChange { /// the token address - pub asset: TokenAddress, + pub asset: Address, /// the change in the token pub change: token::Change, } @@ -424,10 +421,10 @@ pub struct MaspChange { #[derive( BorshSerialize, BorshDeserialize, Debug, Clone, Default, PartialEq, Eq, )] -pub struct MaspAmount(HashMap<(Epoch, TokenAddress), token::Change>); +pub struct MaspAmount(HashMap<(Epoch, Address), token::Change>); impl std::ops::Deref for MaspAmount { - type Target = HashMap<(Epoch, TokenAddress), token::Change>; + type Target = HashMap<(Epoch, Address), token::Change>; fn deref(&self) -> &Self::Target { &self.0 @@ -495,14 +492,9 @@ impl std::ops::Mul for MaspAmount { impl<'a> From<&'a MaspAmount> for Amount { fn from(masp_amount: &'a MaspAmount) -> Amount { let mut res = Amount::zero(); - for ((epoch, key), val) in masp_amount.iter() { + for ((epoch, token), val) in masp_amount.iter() { for denom in MaspDenom::iter() { - let asset = make_asset_type( - Some(*epoch), - &key.address, - &key.sub_prefix, - denom, - ); + let asset = make_asset_type(Some(*epoch), token, denom); res += Amount::from_pair(asset, denom.denominate_i128(val)) .unwrap(); } @@ -558,8 +550,7 @@ pub struct ShieldedContext { /// The set of note positions that have been spent pub spents: HashSet, /// Maps asset types to their decodings - pub asset_types: - HashMap, MaspDenom, Epoch)>, + pub asset_types: HashMap, /// Maps note positions to their corresponding viewing keys pub vk_map: HashMap, } @@ -631,9 +622,9 @@ impl ShieldedContext { /// Fetch the current state of the multi-asset shielded pool into a /// ShieldedContext - pub async fn fetch( + pub async fn fetch( &mut self, - client: &U::C, + client: &C, sks: &[ExtendedSpendingKey], fvks: &[ViewingKey], ) { @@ -699,8 +690,8 @@ impl ShieldedContext { /// transactions as a vector. More concretely, the HEAD_TX_KEY location /// stores the index of the last accepted transaction and each transaction /// is stored at a key derived from its index. - pub async fn fetch_shielded_transfers( - client: &U::C, + pub async fn fetch_shielded_transfers( + client: &C, last_txidx: u64, ) -> BTreeMap<(BlockHeight, TxIndex), (Epoch, Transfer, Transaction)> { // The address of the MASP account @@ -710,7 +701,7 @@ impl ShieldedContext { .push(&HEAD_TX_KEY.to_owned()) .expect("Cannot obtain a storage key"); // Query for the index of the last accepted transaction - let head_txidx = query_storage_value::(client, &head_tx_key) + let head_txidx = query_storage_value::(client, &head_tx_key) .await .unwrap_or(0); let mut shielded_txs = BTreeMap::new(); @@ -723,7 +714,7 @@ impl ShieldedContext { // Obtain the current transaction let (tx_epoch, tx_height, tx_index, current_tx, current_stx) = query_storage_value::< - U::C, + C, (Epoch, BlockHeight, TxIndex, Transfer, Transaction), >(client, ¤t_tx_key) .await @@ -744,10 +735,10 @@ impl ShieldedContext { /// associated to notes, memos, and diversifiers. And the set of notes that /// we have spent are updated. The witness map is maintained to make it /// easier to construct note merkle paths in other code. See - /// . - pub async fn scan_tx( + /// + pub async fn scan_tx( &mut self, - client: &U::C, + client: &C, height: BlockHeight, index: TxIndex, epoch: Epoch, @@ -850,10 +841,7 @@ impl ShieldedContext { } // Record the changes to the transparent accounts let mut transfer_delta = TransferDelta::new(); - let token_addr = TokenAddress { - address: tx.token.clone(), - sub_prefix: tx.sub_prefix.clone(), - }; + let token_addr = tx.token.clone(); transfer_delta.insert( tx.source.clone(), MaspChange { @@ -883,9 +871,9 @@ impl ShieldedContext { /// Compute the total unspent notes associated with the viewing key in the /// context. If the key is not in the context, then we do not know the /// balance and hence we return None. - pub async fn compute_shielded_balance( + pub async fn compute_shielded_balance( &mut self, - client: &U::C, + client: &C, vk: &ViewingKey, ) -> Option { // Cannot query the balance of a key that's not in the map @@ -913,44 +901,42 @@ impl ShieldedContext { /// Query the ledger for the decoding of the given asset type and cache it /// if it is found. - pub async fn decode_asset_type( + pub async fn decode_asset_type( &mut self, - client: &U::C, + client: &C, asset_type: AssetType, - ) -> Option<(Address, Option, MaspDenom, Epoch)> { + ) -> Option<(Address, MaspDenom, Epoch)> { // Try to find the decoding in the cache if let decoded @ Some(_) = self.asset_types.get(&asset_type) { return decoded.cloned(); } // Query for the ID of the last accepted transaction - let (addr, sub_prefix, denom, ep, _conv, _path): ( + let (addr, denom, ep, _conv, _path): ( Address, - Option, MaspDenom, _, Amount, MerklePath, ) = rpc::query_conversion(client, asset_type).await?; self.asset_types - .insert(asset_type, (addr.clone(), sub_prefix.clone(), denom, ep)); - Some((addr, sub_prefix, denom, ep)) + .insert(asset_type, (addr.clone(), denom, ep)); + Some((addr, denom, ep)) } /// Query the ledger for the conversion that is allowed for the given asset /// type and cache it. - async fn query_allowed_conversion<'a>( + async fn query_allowed_conversion<'a, C: Client + Sync>( &'a mut self, - client: &U::C, + client: &C, asset_type: AssetType, conversions: &'a mut Conversions, ) { if let Entry::Vacant(conv_entry) = conversions.entry(asset_type) { // Query for the ID of the last accepted transaction - if let Some((addr, sub_prefix, denom, ep, conv, path)) = + if let Some((addr, denom, ep, conv, path)) = query_conversion(client, asset_type).await { - self.asset_types - .insert(asset_type, (addr, sub_prefix, denom, ep)); + self.asset_types.insert(asset_type, (addr, denom, ep)); // If the conversion is 0, then we just have a pure decoding if conv != Amount::zero() { conv_entry.insert((conv.into(), path, 0)); @@ -963,9 +949,9 @@ impl ShieldedContext { /// context and express that value in terms of the currently timestamped /// asset types. If the key is not in the context, then we do not know the /// balance and hence we return None. - pub async fn compute_exchanged_balance( + pub async fn compute_exchanged_balance( &mut self, - client: &U::C, + client: &C, vk: &ViewingKey, target_epoch: Epoch, ) -> Option { @@ -993,11 +979,11 @@ impl ShieldedContext { /// the trace amount that could not be converted is moved from input to /// output. #[allow(clippy::too_many_arguments)] - async fn apply_conversion( + async fn apply_conversion( &mut self, - client: &U::C, + client: &C, conv: AllowedConversion, - asset_type: (Epoch, TokenAddress, MaspDenom), + asset_type: (Epoch, Address, MaspDenom), value: i128, usage: &mut i128, input: &mut MaspAmount, @@ -1010,12 +996,8 @@ impl ShieldedContext { // If conversion if possible, accumulate the exchanged amount let conv: Amount = conv.into(); // The amount required of current asset to qualify for conversion - let masp_asset = make_asset_type( - Some(asset_type.0), - &asset_type.1.address, - &asset_type.1.sub_prefix, - asset_type.2, - ); + let masp_asset = + make_asset_type(Some(asset_type.0), &asset_type.1, asset_type.2); let threshold = -conv[&masp_asset]; if threshold == 0 { eprintln!( @@ -1047,9 +1029,9 @@ impl ShieldedContext { /// note of the conversions that were used. Note that this function does /// not assume that allowed conversions from the ledger are expressed in /// terms of the latest asset types. - pub async fn compute_exchanged_amount( + pub async fn compute_exchanged_amount( &mut self, - client: &U::C, + client: &C, mut input: MaspAmount, target_epoch: Epoch, mut conversions: Conversions, @@ -1063,18 +1045,10 @@ impl ShieldedContext { let asset_epoch = *asset_epoch; let token_addr = token_addr.clone(); for denom in MaspDenom::iter() { - let target_asset_type = make_asset_type( - Some(target_epoch), - &token_addr.address, - &token_addr.sub_prefix, - denom, - ); - let asset_type = make_asset_type( - Some(asset_epoch), - &token_addr.address, - &token_addr.sub_prefix, - denom, - ); + let target_asset_type = + make_asset_type(Some(target_epoch), &token_addr, denom); + let asset_type = + make_asset_type(Some(asset_epoch), &token_addr, denom); let at_target_asset_type = target_epoch == asset_epoch; let denom_value = denom.denominate_i128(&value); @@ -1139,9 +1113,9 @@ impl ShieldedContext { (asset_epoch, token_addr.clone()), denom_value.into(), ); - for ((e, key), val) in input.iter() { - if *key == token_addr && *e == asset_epoch { - comp.insert((*e, key.clone()), *val); + for ((e, token), val) in input.iter() { + if *token == token_addr && *e == asset_epoch { + comp.insert((*e, token.clone()), *val); } } output += comp.clone(); @@ -1156,9 +1130,9 @@ impl ShieldedContext { /// of the specified asset type. Return the total value accumulated plus /// notes and the corresponding diversifiers/merkle paths that were used to /// achieve the total value. - pub async fn collect_unspent_notes( + pub async fn collect_unspent_notes( &mut self, - client: &U::C, + client: &C, vk: &ViewingKey, target: Amount, target_epoch: Epoch, @@ -1226,8 +1200,8 @@ impl ShieldedContext { /// keys to try to decrypt the output notes. If no transaction is pinned at /// the given payment address fails with /// `PinnedBalanceError::NoTransactionPinned`. - pub async fn compute_pinned_balance( - client: &U::C, + pub async fn compute_pinned_balance( + client: &C, owner: PaymentAddress, viewing_key: &ViewingKey, ) -> Result<(Amount, Epoch), PinnedBalanceError> { @@ -1249,7 +1223,7 @@ impl ShieldedContext { .push(&(PIN_KEY_PREFIX.to_owned() + &owner.hash())) .expect("Cannot obtain a storage key"); // Obtain the transaction pointer at the key - let txidx = rpc::query_storage_value::(client, &pin_key) + let txidx = rpc::query_storage_value::(client, &pin_key) .await .ok_or(PinnedBalanceError::NoTransactionPinned)?; // Construct the key for where the pinned transaction is stored @@ -1259,7 +1233,7 @@ impl ShieldedContext { // Obtain the pointed to transaction let (tx_epoch, _tx_height, _tx_index, _tx, shielded) = rpc::query_storage_value::< - U::C, + C, (Epoch, BlockHeight, TxIndex, Transfer, Transaction), >(client, &tx_key) .await @@ -1297,9 +1271,9 @@ impl ShieldedContext { /// the epoch of the transaction or even before, so exchange all these /// amounts to the epoch of the transaction in order to get the value that /// would have been displayed in the epoch of the transaction. - pub async fn compute_exchanged_pinned_balance( + pub async fn compute_exchanged_pinned_balance( &mut self, - client: &U::C, + client: &C, owner: PaymentAddress, viewing_key: &ViewingKey, ) -> Result<(MaspAmount, Epoch), PinnedBalanceError> { @@ -1322,29 +1296,24 @@ impl ShieldedContext { /// Convert an amount whose units are AssetTypes to one whose units are /// Addresses that they decode to. All asset types not corresponding to /// the given epoch are ignored. - pub async fn decode_amount( + pub async fn decode_amount( &mut self, - client: &U::C, + client: &C, amt: Amount, target_epoch: Epoch, - ) -> HashMap { + ) -> HashMap { let mut res = HashMap::new(); for (asset_type, val) in amt.components() { // Decode the asset type let decoded = self.decode_asset_type(client, *asset_type).await; // Only assets with the target timestamp count match decoded { - Some(asset_type @ (_, _, _, epoch)) - if epoch == target_epoch => - { + Some(asset_type @ (_, _, epoch)) if epoch == target_epoch => { decode_component( asset_type, *val, &mut res, - |address, sub_prefix, _| TokenAddress { - address, - sub_prefix, - }, + |address, _| address, ); } _ => {} @@ -1355,32 +1324,20 @@ impl ShieldedContext { /// Convert an amount whose units are AssetTypes to one whose units are /// Addresses that they decode to. - pub async fn decode_all_amounts( + pub async fn decode_all_amounts( &mut self, - client: &U::C, + client: &C, amt: Amount, ) -> MaspAmount { - let mut res: HashMap<(Epoch, TokenAddress), Change> = - HashMap::default(); + let mut res: HashMap<(Epoch, Address), Change> = HashMap::default(); for (asset_type, val) in amt.components() { // Decode the asset type if let Some(decoded) = self.decode_asset_type(client, *asset_type).await { - decode_component( - decoded, - *val, - &mut res, - |address, sub_prefix, epoch| { - ( - epoch, - TokenAddress { - address, - sub_prefix, - }, - ) - }, - ) + decode_component(decoded, *val, &mut res, |address, epoch| { + (epoch, address) + }) } } MaspAmount(res) @@ -1394,9 +1351,9 @@ impl ShieldedContext { /// understood that transparent account changes are effected only by the /// amounts and signatures specified by the containing Transfer object. #[cfg(feature = "masp-tx-gen")] - pub async fn gen_shielded_transfer( + pub async fn gen_shielded_transfer( &mut self, - client: &U::C, + client: &C, args: &args::TxTransfer, shielded_gas: bool, ) -> Result< @@ -1440,12 +1397,8 @@ impl ShieldedContext { unreachable!("The function `gen_shielded_transfer` is only called by `submit_tx` which validates amounts.") }; // Convert transaction amount into MASP types - let (asset_types, amount) = convert_amount( - epoch, - &args.token, - &args.sub_prefix.as_ref(), - amt.amount, - ); + let (asset_types, amount) = + convert_amount(epoch, &args.token, amt.amount); let tx_fee = // If there are shielded inputs @@ -1456,7 +1409,7 @@ impl ShieldedContext { // Transaction fees need to match the amount in the wrapper Transfer // when MASP source is used let (_, shielded_fee) = - convert_amount(epoch, &args.tx.fee_token, &None, fee.amount); + convert_amount(epoch, &args.tx.fee_token, fee.amount); let required_amt = if shielded_gas { amount + shielded_fee.clone() } else { @@ -1611,9 +1564,9 @@ impl ShieldedContext { /// transactions. If an owner is specified, then restrict the set to only /// transactions crediting/debiting the given owner. If token is specified, /// then restrict set to only transactions involving the given token. - pub async fn query_tx_deltas( + pub async fn query_tx_deltas( &mut self, - client: &U::C, + client: &C, query_owner: &Either>, query_token: &Option
, viewing_keys: &HashMap, @@ -1701,10 +1654,7 @@ impl ShieldedContext { let delta = TransferDelta::from([( transfer.source.clone(), MaspChange { - asset: TokenAddress { - address: transfer.token.clone(), - sub_prefix: transfer.sub_prefix.clone(), - }, + asset: transfer.token.clone(), change: -transfer.amount.amount.change(), }, )]); @@ -1744,33 +1694,15 @@ fn extract_payload( } /// Make asset type corresponding to given address and epoch -pub fn make_asset_type( +pub fn make_asset_type( epoch: Option, token: &Address, - sub_prefix: &Option, denom: MaspDenom, ) -> AssetType { // Typestamp the chosen token with the current epoch let token_bytes = match epoch { - None => ( - token, - sub_prefix - .as_ref() - .map(|k| k.to_string()) - .unwrap_or_default(), - denom, - ) - .try_to_vec() - .expect("token should serialize"), - Some(epoch) => ( - token, - sub_prefix - .as_ref() - .map(|k| k.to_string()) - .unwrap_or_default(), - denom, - epoch.0, - ) + None => (token, denom).try_to_vec().expect("token should serialize"), + Some(epoch) => (token, denom, epoch.0) .try_to_vec() .expect("token should serialize"), }; @@ -1782,14 +1714,12 @@ pub fn make_asset_type( fn convert_amount( epoch: Epoch, token: &Address, - sub_prefix: &Option<&String>, val: token::Amount, ) -> ([AssetType; 4], Amount) { let mut amount = Amount::zero(); let asset_types: [AssetType; 4] = MaspDenom::iter() .map(|denom| { - let asset_type = - make_asset_type(Some(epoch), token, sub_prefix, denom); + let asset_type = make_asset_type(Some(epoch), token, denom); // Combine the value and unit into one amount amount += Amount::from_nonnegative(asset_type, denom.denominate(&val)) diff --git a/shared/src/ledger/native_vp/ethereum_bridge/authorize.rs b/shared/src/ledger/native_vp/ethereum_bridge/authorize.rs deleted file mode 100644 index 8c998ad50b..0000000000 --- a/shared/src/ledger/native_vp/ethereum_bridge/authorize.rs +++ /dev/null @@ -1,56 +0,0 @@ -//! Functionality to do with checking whether a transaction is authorized by the -//! "owner" of some key under this account -use std::collections::BTreeSet; - -use namada_core::types::address::Address; - -/// For wrapped ERC20 transfers, checks that `verifiers` contains the `sender`'s -/// address - we delegate to the sender's VP to authorize the transfer (for -/// regular Namada accounts, this will be `vp_implicit` or `vp_user`). -pub(super) fn is_authorized( - verifiers: &BTreeSet
, - sender: &Address, - receiver: &Address, -) -> bool { - verifiers.contains(sender) && verifiers.contains(receiver) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::types::address; - - #[test] - fn test_is_authorized_passes() { - let sender = address::testing::established_address_1(); - let receiver = address::testing::established_address_2(); - let verifiers = BTreeSet::from([sender.clone(), receiver.clone()]); - - let authorized = is_authorized(&verifiers, &sender, &receiver); - - assert!(authorized); - } - - #[test] - fn test_is_authorized_fails() { - let sender = address::testing::established_address_1(); - let receiver = address::testing::established_address_2(); - let verifiers = BTreeSet::default(); - - let authorized = is_authorized(&verifiers, &sender, &receiver); - - assert!(!authorized); - - let verifiers = BTreeSet::from([sender.clone()]); - - let authorized = is_authorized(&verifiers, &sender, &receiver); - - assert!(!authorized); - - let verifiers = BTreeSet::from([receiver.clone()]); - - let authorized = is_authorized(&verifiers, &sender, &receiver); - - assert!(!authorized); - } -} diff --git a/shared/src/ledger/native_vp/ethereum_bridge/bridge_pool_vp.rs b/shared/src/ledger/native_vp/ethereum_bridge/bridge_pool_vp.rs index bc4b5be451..f5bd77e152 100644 --- a/shared/src/ledger/native_vp/ethereum_bridge/bridge_pool_vp.rs +++ b/shared/src/ledger/native_vp/ethereum_bridge/bridge_pool_vp.rs @@ -92,30 +92,20 @@ where transfer: &PendingTransfer, ) -> Result { // check that the assets to be transferred were escrowed - let asset_key = wrapped_erc20s::Keys::from(&transfer.transfer.asset); - let owner_key = asset_key.balance(&transfer.transfer.sender); - let escrow_key = asset_key.balance(&BRIDGE_POOL_ADDRESS); + let token = wrapped_erc20s::token(&transfer.transfer.asset); + let owner_key = balance_key(&token, &transfer.transfer.sender); + let escrow_key = balance_key(&token, &BRIDGE_POOL_ADDRESS); if keys_changed.contains(&owner_key) && keys_changed.contains(&escrow_key) { - match check_balance_changes( - &self.ctx, - (&self.ctx.storage.native_token, &escrow_key) - .try_into() - .expect("This should not fail"), - (&self.ctx.storage.native_token, &owner_key) - .try_into() - .expect("This should not fail"), - ) { - Ok(Some((sender, _, amount))) - if check_delta(&sender, &amount, transfer) => {} - other => { + match check_balance_changes(&self.ctx, &owner_key, &escrow_key)? { + Some(amount) if amount == transfer.transfer.amount => Ok(true), + _ => { tracing::debug!( "The assets of the transfer were not properly \ - escrowed into the Ethereum bridge pool: {:?}", - other + escrowed into the Ethereum bridge pool" ); - return Ok(false); + Ok(false) } } } else { @@ -123,14 +113,8 @@ where "The assets of the transfer were not properly escrowed into \ the Ethereum bridge pool." ); - return Ok(false); + Ok(false) } - - tracing::info!( - "The Ethereum bridge pool VP accepted the transfer {:?}.", - transfer - ); - Ok(true) } /// Check that the correct amount of Nam was sent @@ -235,15 +219,6 @@ where } } -/// Check if a delta matches the delta given by a transfer -fn check_delta( - sender: &Address, - amount: &Amount, - transfer: &PendingTransfer, -) -> bool { - *sender == transfer.transfer.sender && *amount == transfer.transfer.amount -} - /// Helper struct for handling the different escrow /// checking scenarios. struct EscrowDelta<'a> { @@ -486,7 +461,7 @@ mod test_bridge_pool_vp { ) -> BTreeSet { // get the balance keys let token_key = - wrapped_erc20s::Keys::from(&ASSET).balance(&balance.owner); + balance_key(&wrapped_erc20s::token(&ASSET), &balance.owner); let account_key = balance_key(&nam(), &balance.owner); // update the balance of nam @@ -1030,12 +1005,14 @@ mod test_bridge_pool_vp { BTreeSet::from([get_pending_key(&transfer)]) }; // We escrow 0 tokens - keys_changed.insert( - wrapped_erc20s::Keys::from(&ASSET).balance(&bertha_address()), - ); - keys_changed.insert( - wrapped_erc20s::Keys::from(&ASSET).balance(&BRIDGE_POOL_ADDRESS), - ); + keys_changed.insert(balance_key( + &wrapped_erc20s::token(&ASSET), + &bertha_address(), + )); + keys_changed.insert(balance_key( + &wrapped_erc20s::token(&ASSET), + &BRIDGE_POOL_ADDRESS, + )); let verifiers = BTreeSet::default(); // create the data to be given to the vp diff --git a/shared/src/ledger/native_vp/ethereum_bridge/mod.rs b/shared/src/ledger/native_vp/ethereum_bridge/mod.rs index 7e5062a251..85df785e79 100644 --- a/shared/src/ledger/native_vp/ethereum_bridge/mod.rs +++ b/shared/src/ledger/native_vp/ethereum_bridge/mod.rs @@ -2,6 +2,5 @@ //! This includes both the bridge vp and the vp for the bridge //! pool. -mod authorize; pub mod bridge_pool_vp; pub mod vp; diff --git a/shared/src/ledger/native_vp/ethereum_bridge/vp.rs b/shared/src/ledger/native_vp/ethereum_bridge/vp.rs index 47eed29f3c..f2b0f5245e 100644 --- a/shared/src/ledger/native_vp/ethereum_bridge/vp.rs +++ b/shared/src/ledger/native_vp/ethereum_bridge/vp.rs @@ -13,7 +13,6 @@ use namada_core::types::address::{Address, InternalAddress}; use namada_core::types::storage::Key; use namada_core::types::token::{balance_key, Amount, Change}; -use crate::ledger::native_vp::ethereum_bridge::authorize; use crate::ledger::native_vp::{Ctx, NativeVp, StorageReader, VpEnv}; use crate::proto::Tx; use crate::vm::WasmCacheAccess; @@ -94,7 +93,7 @@ where #[derive(Debug)] enum CheckType { Escrow, - Erc20Transfer(wrapped_erc20s::Key, wrapped_erc20s::Key), + Erc20Transfer, } #[derive(thiserror::Error, Debug)] @@ -138,35 +137,14 @@ where "Ethereum Bridge VP triggered", ); - let (key_a, key_b) = match determine_check_type( + match determine_check_type( &self.ctx.storage.native_token, keys_changed, )? { - Some(CheckType::Erc20Transfer(key_a, key_b)) => (key_a, key_b), - Some(CheckType::Escrow) => return self.check_escrow(verifiers), - None => return Ok(false), - }; - let (sender, receiver, _) = - match check_balance_changes(&self.ctx, key_a, key_b)? { - Some(sender) => sender, - None => return Ok(false), - }; - if authorize::is_authorized(verifiers, &sender, &receiver) { - tracing::info!( - ?verifiers, - ?sender, - ?receiver, - "Ethereum Bridge VP authorized transfer" - ); - Ok(true) - } else { - tracing::info!( - ?verifiers, - ?sender, - ?receiver, - "Ethereum Bridge VP rejected unauthorized transfer" - ); - Ok(false) + // Multitoken VP checks the balance changes for the ERC20 transfer + Some(CheckType::Erc20Transfer) => Ok(true), + Some(CheckType::Escrow) => self.check_escrow(verifiers), + None => Ok(false), } } } @@ -245,155 +223,93 @@ fn determine_check_type( ); return Ok(None); } - Ok(Some(CheckType::Erc20Transfer(key_a, key_b))) + Ok(Some(CheckType::Erc20Transfer)) } -/// Checks that the balances at both `key_a` and `key_b` have changed by some -/// amount, and that the changes balance each other out. If the balance changes -/// are invalid, the reason is logged and a `None` is returned. Otherwise, -/// return: -/// - the `Address` of the sender i.e. the owner of the balance which is -/// decreasing -/// - the `Address` of the receiver i.e. the owner of the balance which is -/// increasing -/// - the `Amount` of the transfer i.e. by how much the sender's balance -/// decreased, or equivalently by how much the receiver's balance increased +/// Checks that the balances at both `sender` and `receiver` have changed by +/// some amount, and that the changes balance each other out. If the balance +/// changes are invalid, the reason is logged and a `None` is returned. +/// Otherwise, return the `Amount` of the transfer i.e. by how much the sender's +/// balance decreased, or equivalently by how much the receiver's balance +/// increased pub(super) fn check_balance_changes( reader: impl StorageReader, - key_a: wrapped_erc20s::Key, - key_b: wrapped_erc20s::Key, -) -> Result> { - let (balance_a, balance_b) = - match (key_a.suffix.clone(), key_b.suffix.clone()) { - ( - wrapped_erc20s::KeyType::Balance { .. }, - wrapped_erc20s::KeyType::Balance { .. }, - ) => (Key::from(&key_a), Key::from(&key_b)), - ( - wrapped_erc20s::KeyType::Balance { .. }, - wrapped_erc20s::KeyType::Supply, - ) - | ( - wrapped_erc20s::KeyType::Supply, - wrapped_erc20s::KeyType::Balance { .. }, - ) => { - tracing::debug!( - ?key_a, - ?key_b, - "Rejecting transaction that is attempting to change a \ - supply key" - ); - return Ok(None); - } - ( - wrapped_erc20s::KeyType::Supply, - wrapped_erc20s::KeyType::Supply, - ) => { - // in theory, this should be unreachable!() as we would have - // already rejected if both supply keys were for - // the same asset - tracing::debug!( - ?key_a, - ?key_b, - "Rejecting transaction that is attempting to change two \ - supply keys" - ); - return Ok(None); - } - }; - let balance_a_pre = reader - .read_pre_value::(&balance_a)? + sender: &Key, + receiver: &Key, +) -> Result> { + let sender_balance_pre = reader + .read_pre_value::(sender)? .unwrap_or_default() .change(); - let balance_a_post = match reader.read_post_value::(&balance_a)? { + let sender_balance_post = match reader.read_post_value::(sender)? { Some(value) => value, None => { - tracing::debug!( - ?balance_a, - "Rejecting transaction as could not read_post balance key" - ); - return Ok(None); + return Err(eyre!( + "Rejecting transaction as could not read_post balance key {}", + sender, + )); } } .change(); - let balance_b_pre = reader - .read_pre_value::(&balance_b)? + let receiver_balance_pre = reader + .read_pre_value::(receiver)? .unwrap_or_default() .change(); - let balance_b_post = match reader.read_post_value::(&balance_b)? { + let receiver_balance_post = match reader + .read_post_value::(receiver)? + { Some(value) => value, None => { - tracing::debug!( - ?balance_b, - "Rejecting transaction as could not read_post balance key" - ); - return Ok(None); + return Err(eyre!( + "Rejecting transaction as could not read_post balance key {}", + receiver, + )); } } .change(); - let balance_a_delta = calculate_delta(balance_a_pre, balance_a_post)?; - let balance_b_delta = calculate_delta(balance_b_pre, balance_b_post)?; - if balance_a_delta != -balance_b_delta { + let sender_balance_delta = + calculate_delta(sender_balance_pre, sender_balance_post)?; + let receiver_balance_delta = + calculate_delta(receiver_balance_pre, receiver_balance_post)?; + if receiver_balance_delta != -sender_balance_delta { tracing::debug!( - ?balance_a_pre, - ?balance_b_pre, - ?balance_a_post, - ?balance_b_post, - ?balance_a_delta, - ?balance_b_delta, + ?sender_balance_pre, + ?receiver_balance_pre, + ?sender_balance_post, + ?receiver_balance_post, + ?sender_balance_delta, + ?receiver_balance_delta, "Rejecting transaction as balance changes do not match" ); return Ok(None); } - if balance_a_delta.is_zero() { - assert_eq!(balance_b_delta, Change::zero()); - tracing::debug!("Rejecting transaction as no balance change"); + if sender_balance_delta.is_zero() || sender_balance_delta > Change::zero() { + assert!( + receiver_balance_delta.is_zero() + || receiver_balance_delta < Change::zero() + ); + tracing::debug!( + "Rejecting transaction as no balance change or invalid change" + ); return Ok(None); } - if balance_a_post < Change::zero() { + if sender_balance_post < Change::zero() { tracing::debug!( - ?balance_a_post, + ?sender_balance_post, "Rejecting transaction as balance is negative" ); return Ok(None); } - if balance_b_post < Change::zero() { + if receiver_balance_post < Change::zero() { tracing::debug!( - ?balance_b_post, + ?receiver_balance_post, "Rejecting transaction as balance is negative" ); return Ok(None); } - if balance_a_delta < Change::zero() { - if let wrapped_erc20s::KeyType::Balance { owner: sender } = key_a.suffix - { - let wrapped_erc20s::KeyType::Balance { owner: receiver } = - key_b.suffix else { unreachable!() }; - Ok(Some(( - sender, - receiver, - Amount::from_change(balance_b_delta), - ))) - } else { - unreachable!() - } - } else { - assert!(balance_b_delta < Change::zero()); - if let wrapped_erc20s::KeyType::Balance { owner: sender } = key_b.suffix - { - let wrapped_erc20s::KeyType::Balance { owner: receiver } = - key_a.suffix else { unreachable!() }; - Ok(Some(( - sender, - receiver, - Amount::from_change(balance_a_delta), - ))) - } else { - unreachable!() - } - } + Ok(Some(Amount::from_change(receiver_balance_delta))) } /// Return the delta between `balance_pre` and `balance_post`, erroring if there @@ -437,6 +353,7 @@ mod tests { use crate::types::ethereum_events; use crate::types::ethereum_events::EthAddress; use crate::types::storage::TxIndex; + use crate::types::token::minted_balance_key; use crate::types::transaction::TxType; use crate::vm::wasm::VpCache; use crate::vm::WasmCacheRwAccess; @@ -563,10 +480,9 @@ mod tests { { let keys_changed = BTreeSet::from_iter(vec![ arbitrary_key(), - wrapped_erc20s::Keys::from( + minted_balance_key(&wrapped_erc20s::token( ðereum_events::testing::DAI_ERC20_ETH_ADDRESS, - ) - .supply(), + )), ]); let result = determine_check_type(&nam(), &keys_changed); @@ -577,10 +493,10 @@ mod tests { { let keys_changed = BTreeSet::from_iter(vec![ arbitrary_key(), - wrapped_erc20s::Keys::from( - ðereum_events::testing::DAI_ERC20_ETH_ADDRESS, - ) - .balance( + balance_key( + &wrapped_erc20s::token( + ðereum_events::testing::DAI_ERC20_ETH_ADDRESS, + ), &Address::decode(ARBITRARY_OWNER_A_ADDRESS) .expect("Couldn't set up test"), ), @@ -596,17 +512,17 @@ mod tests { fn test_rejects_if_multitoken_keys_for_different_assets() { { let keys_changed = BTreeSet::from_iter(vec![ - wrapped_erc20s::Keys::from( - ðereum_events::testing::DAI_ERC20_ETH_ADDRESS, - ) - .balance( + balance_key( + &wrapped_erc20s::token( + ðereum_events::testing::DAI_ERC20_ETH_ADDRESS, + ), &Address::decode(ARBITRARY_OWNER_A_ADDRESS) .expect("Couldn't set up test"), ), - wrapped_erc20s::Keys::from( - ðereum_events::testing::USDC_ERC20_ETH_ADDRESS, - ) - .balance( + balance_key( + &wrapped_erc20s::token( + ðereum_events::testing::USDC_ERC20_ETH_ADDRESS, + ), &Address::decode(ARBITRARY_OWNER_B_ADDRESS) .expect("Couldn't set up test"), ), @@ -623,8 +539,9 @@ mod tests { let asset = ðereum_events::testing::DAI_ERC20_ETH_ADDRESS; { let keys_changed = BTreeSet::from_iter(vec![ - wrapped_erc20s::Keys::from(asset).supply(), - wrapped_erc20s::Keys::from(asset).balance( + minted_balance_key(&wrapped_erc20s::token(asset)), + balance_key( + &wrapped_erc20s::token(asset), &Address::decode(ARBITRARY_OWNER_B_ADDRESS) .expect("Couldn't set up test"), ), diff --git a/shared/src/ledger/native_vp/mod.rs b/shared/src/ledger/native_vp/mod.rs index dcc0432ea7..c7d58f0563 100644 --- a/shared/src/ledger/native_vp/mod.rs +++ b/shared/src/ledger/native_vp/mod.rs @@ -3,6 +3,7 @@ pub mod ethereum_bridge; pub mod governance; +pub mod multitoken; pub mod parameters; pub mod replay_protection; pub mod slash_fund; diff --git a/shared/src/ledger/native_vp/multitoken.rs b/shared/src/ledger/native_vp/multitoken.rs new file mode 100644 index 0000000000..a932714240 --- /dev/null +++ b/shared/src/ledger/native_vp/multitoken.rs @@ -0,0 +1,597 @@ +//! Native VP for multitokens + +use std::collections::{BTreeSet, HashMap}; + +use thiserror::Error; + +use crate::ledger::native_vp::{self, Ctx, NativeVp}; +use crate::ledger::storage; +use crate::ledger::vp_env::VpEnv; +use crate::proto::Tx; +use crate::types::address::{Address, InternalAddress}; +use crate::types::storage::{Key, KeySeg}; +use crate::types::token::{ + is_any_minted_balance_key, is_any_minter_key, is_any_token_balance_key, + minter_key, Amount, Change, +}; +use crate::vm::WasmCacheAccess; + +#[allow(missing_docs)] +#[derive(Error, Debug)] +pub enum Error { + #[error("Native VP error: {0}")] + NativeVpError(#[from] native_vp::Error), +} + +/// Multitoken functions result +pub type Result = std::result::Result; + +/// Multitoken VP +pub struct MultitokenVp<'a, DB, H, CA> +where + DB: storage::DB + for<'iter> storage::DBIter<'iter>, + H: storage::StorageHasher, + CA: WasmCacheAccess, +{ + /// Context to interact with the host structures. + pub ctx: Ctx<'a, DB, H, CA>, +} + +impl<'a, DB, H, CA> NativeVp for MultitokenVp<'a, DB, H, CA> +where + DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, + H: 'static + storage::StorageHasher, + CA: 'static + WasmCacheAccess, +{ + type Error = Error; + + const ADDR: InternalAddress = InternalAddress::Multitoken; + + fn validate_tx( + &self, + _tx: &Tx, + keys_changed: &BTreeSet, + verifiers: &BTreeSet
, + ) -> Result { + let mut changes = HashMap::new(); + let mut mints = HashMap::new(); + for key in keys_changed { + if let Some([token, _]) = is_any_token_balance_key(key) { + let pre: Amount = self.ctx.read_pre(key)?.unwrap_or_default(); + let post: Amount = self.ctx.read_post(key)?.unwrap_or_default(); + let diff = post.change() - pre.change(); + match changes.get_mut(token) { + Some(change) => *change += diff, + None => _ = changes.insert(token, diff), + } + } else if let Some(token) = is_any_minted_balance_key(key) { + let pre: Amount = self.ctx.read_pre(key)?.unwrap_or_default(); + let post: Amount = self.ctx.read_post(key)?.unwrap_or_default(); + let diff = post.change() - pre.change(); + match mints.get_mut(token) { + Some(mint) => *mint += diff, + None => _ = mints.insert(token, diff), + } + + // Check if the minter is set + if !self.is_valid_minter(token, verifiers)? { + return Ok(false); + } + } else if let Some(token) = is_any_minter_key(key) { + if !self.is_valid_minter(token, verifiers)? { + return Ok(false); + } + } else if key.segments.get(0) + == Some( + &Address::Internal(InternalAddress::Multitoken).to_db_key(), + ) + { + // Reject when trying to update an unexpected key under + // `#Multitoken/...` + return Ok(false); + } + } + + Ok(changes.iter().all(|(token, change)| { + let mint = match mints.get(token) { + Some(mint) => *mint, + None => Change::zero(), + }; + *change == mint + })) + } +} + +impl<'a, DB, H, CA> MultitokenVp<'a, DB, H, CA> +where + DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, + H: 'static + storage::StorageHasher, + CA: 'static + WasmCacheAccess, +{ + /// Return the minter if the minter is valid and the minter VP exists + pub fn is_valid_minter( + &self, + token: &Address, + verifiers: &BTreeSet
, + ) -> Result { + match token { + Address::Internal(InternalAddress::IbcToken(_)) => { + // Check if the minter is set + let minter_key = minter_key(token); + match self.ctx.read_post::
(&minter_key)? { + Some(minter) + if minter + == Address::Internal(InternalAddress::Ibc) => + { + Ok(verifiers.contains(&minter)) + } + _ => Ok(false), + } + } + _ => { + // ERC20 and other tokens should not be minted by a wasm + // transaction + Ok(false) + } + } + } +} + +#[cfg(test)] +mod tests { + use std::collections::BTreeSet; + + use borsh::BorshSerialize; + + use super::*; + use crate::core::ledger::storage::testing::TestWlStorage; + use crate::core::types::address::nam; + use crate::core::types::address::testing::{ + established_address_1, established_address_2, + }; + use crate::ledger::gas::VpGasMeter; + use crate::ledger::ibc::storage::ibc_token; + use crate::proto::{Code, Data, Section, Signature, Tx}; + use crate::types::address::{Address, InternalAddress}; + use crate::types::key::testing::keypair_1; + use crate::types::storage::TxIndex; + use crate::types::token::{ + balance_key, minted_balance_key, minter_key, Amount, + }; + use crate::types::transaction::TxType; + use crate::vm::wasm::compilation_cache::common::testing::cache as wasm_cache; + + const ADDRESS: Address = Address::Internal(InternalAddress::Multitoken); + + fn dummy_tx(wl_storage: &TestWlStorage) -> Tx { + let tx_code = vec![]; + let tx_data = vec![]; + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = wl_storage.storage.chain_id.clone(); + tx.set_code(Code::new(tx_code)); + tx.set_data(Data::new(tx_data)); + tx.add_section(Section::Signature(Signature::new( + tx.sechashes(), + &keypair_1(), + ))); + tx + } + + #[test] + fn test_valid_transfer() { + let mut wl_storage = TestWlStorage::default(); + let mut keys_changed = BTreeSet::new(); + + let sender = established_address_1(); + let sender_key = balance_key(&nam(), &sender); + let amount = Amount::native_whole(100); + wl_storage + .storage + .write(&sender_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + + // transfer 10 + let amount = Amount::native_whole(90); + wl_storage + .write_log + .write(&sender_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(sender_key); + let receiver = established_address_2(); + let receiver_key = balance_key(&nam(), &receiver); + let amount = Amount::native_whole(10); + wl_storage + .write_log + .write(&receiver_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(receiver_key); + + let tx_index = TxIndex::default(); + let tx = dummy_tx(&wl_storage); + let gas_meter = VpGasMeter::new(0); + let (vp_wasm_cache, _vp_cache_dir) = wasm_cache(); + let mut verifiers = BTreeSet::new(); + verifiers.insert(sender); + let ctx = Ctx::new( + &ADDRESS, + &wl_storage.storage, + &wl_storage.write_log, + &tx, + &tx_index, + gas_meter, + &keys_changed, + &verifiers, + vp_wasm_cache, + ); + + let vp = MultitokenVp { ctx }; + assert!( + vp.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") + ); + } + + #[test] + fn test_invalid_transfer() { + let mut wl_storage = TestWlStorage::default(); + let mut keys_changed = BTreeSet::new(); + + let sender = established_address_1(); + let sender_key = balance_key(&nam(), &sender); + let amount = Amount::native_whole(100); + wl_storage + .storage + .write(&sender_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + + // transfer 10 + let amount = Amount::native_whole(90); + wl_storage + .write_log + .write(&sender_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(sender_key); + let receiver = established_address_2(); + let receiver_key = balance_key(&nam(), &receiver); + // receive more than 10 + let amount = Amount::native_whole(100); + wl_storage + .write_log + .write(&receiver_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(receiver_key); + + let tx_index = TxIndex::default(); + let tx = dummy_tx(&wl_storage); + let gas_meter = VpGasMeter::new(0); + let (vp_wasm_cache, _vp_cache_dir) = wasm_cache(); + let verifiers = BTreeSet::new(); + let ctx = Ctx::new( + &ADDRESS, + &wl_storage.storage, + &wl_storage.write_log, + &tx, + &tx_index, + gas_meter, + &keys_changed, + &verifiers, + vp_wasm_cache, + ); + + let vp = MultitokenVp { ctx }; + assert!( + !vp.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") + ); + } + + #[test] + fn test_valid_mint() { + let mut wl_storage = TestWlStorage::default(); + let mut keys_changed = BTreeSet::new(); + + // IBC token + let token = ibc_token("/port-42/channel-42/denom"); + + // mint 100 + let target = established_address_1(); + let target_key = balance_key(&token, &target); + let amount = Amount::native_whole(100); + wl_storage + .write_log + .write(&target_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(target_key); + let minted_key = minted_balance_key(&token); + let amount = Amount::native_whole(100); + wl_storage + .write_log + .write(&minted_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(minted_key); + + // minter + let minter = Address::Internal(InternalAddress::Ibc); + let minter_key = minter_key(&token); + wl_storage + .write_log + .write(&minter_key, minter.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(minter_key); + + let tx_index = TxIndex::default(); + let tx = dummy_tx(&wl_storage); + let gas_meter = VpGasMeter::new(0); + let (vp_wasm_cache, _vp_cache_dir) = wasm_cache(); + let mut verifiers = BTreeSet::new(); + // for the minter + verifiers.insert(minter); + let ctx = Ctx::new( + &ADDRESS, + &wl_storage.storage, + &wl_storage.write_log, + &tx, + &tx_index, + gas_meter, + &keys_changed, + &verifiers, + vp_wasm_cache, + ); + + let vp = MultitokenVp { ctx }; + assert!( + vp.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") + ); + } + + #[test] + fn test_invalid_mint() { + let mut wl_storage = TestWlStorage::default(); + let mut keys_changed = BTreeSet::new(); + + // mint 100 + let target = established_address_1(); + let target_key = balance_key(&nam(), &target); + // mint more than 100 + let amount = Amount::native_whole(1000); + wl_storage + .write_log + .write(&target_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(target_key); + let minted_key = minted_balance_key(&nam()); + let amount = Amount::native_whole(100); + wl_storage + .write_log + .write(&minted_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(minted_key); + + // minter + let minter = nam(); + let minter_key = minter_key(&nam()); + wl_storage + .write_log + .write(&minter_key, minter.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(minter_key); + + let tx_index = TxIndex::default(); + let tx = dummy_tx(&wl_storage); + let gas_meter = VpGasMeter::new(0); + let (vp_wasm_cache, _vp_cache_dir) = wasm_cache(); + let mut verifiers = BTreeSet::new(); + // for the minter + verifiers.insert(minter); + let ctx = Ctx::new( + &ADDRESS, + &wl_storage.storage, + &wl_storage.write_log, + &tx, + &tx_index, + gas_meter, + &keys_changed, + &verifiers, + vp_wasm_cache, + ); + + let vp = MultitokenVp { ctx }; + assert!( + !vp.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") + ); + } + + #[test] + fn test_no_minter() { + let mut wl_storage = TestWlStorage::default(); + let mut keys_changed = BTreeSet::new(); + + // IBC token + let token = ibc_token("/port-42/channel-42/denom"); + + // mint 100 + let target = established_address_1(); + let target_key = balance_key(&token, &target); + let amount = Amount::native_whole(100); + wl_storage + .write_log + .write(&target_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(target_key); + let minted_key = minted_balance_key(&token); + let amount = Amount::native_whole(100); + wl_storage + .write_log + .write(&minted_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(minted_key); + + // no minter is set + + let tx_index = TxIndex::default(); + let tx = dummy_tx(&wl_storage); + let gas_meter = VpGasMeter::new(0); + let (vp_wasm_cache, _vp_cache_dir) = wasm_cache(); + let verifiers = BTreeSet::new(); + let ctx = Ctx::new( + &ADDRESS, + &wl_storage.storage, + &wl_storage.write_log, + &tx, + &tx_index, + gas_meter, + &keys_changed, + &verifiers, + vp_wasm_cache, + ); + + let vp = MultitokenVp { ctx }; + assert!( + !vp.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") + ); + } + + #[test] + fn test_invalid_minter() { + let mut wl_storage = TestWlStorage::default(); + let mut keys_changed = BTreeSet::new(); + + // IBC token + let token = ibc_token("/port-42/channel-42/denom"); + + // mint 100 + let target = established_address_1(); + let target_key = balance_key(&token, &target); + let amount = Amount::native_whole(100); + wl_storage + .write_log + .write(&target_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(target_key); + let minted_key = minted_balance_key(&token); + let amount = Amount::native_whole(100); + wl_storage + .write_log + .write(&minted_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(minted_key); + + // invalid minter + let minter = established_address_1(); + let minter_key = minter_key(&token); + wl_storage + .write_log + .write(&minter_key, minter.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(minter_key); + + let tx_index = TxIndex::default(); + let tx = dummy_tx(&wl_storage); + let gas_meter = VpGasMeter::new(0); + let (vp_wasm_cache, _vp_cache_dir) = wasm_cache(); + let mut verifiers = BTreeSet::new(); + // for the minter + verifiers.insert(minter); + let ctx = Ctx::new( + &ADDRESS, + &wl_storage.storage, + &wl_storage.write_log, + &tx, + &tx_index, + gas_meter, + &keys_changed, + &verifiers, + vp_wasm_cache, + ); + + let vp = MultitokenVp { ctx }; + assert!( + !vp.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") + ); + } + + #[test] + fn test_invalid_minter_update() { + let mut wl_storage = TestWlStorage::default(); + let mut keys_changed = BTreeSet::new(); + + let minter_key = minter_key(&nam()); + let minter = established_address_1(); + wl_storage + .write_log + .write(&minter_key, minter.try_to_vec().unwrap()) + .expect("write failed"); + + keys_changed.insert(minter_key); + + let tx_index = TxIndex::default(); + let tx = dummy_tx(&wl_storage); + let gas_meter = VpGasMeter::new(0); + let (vp_wasm_cache, _vp_cache_dir) = wasm_cache(); + let mut verifiers = BTreeSet::new(); + // for the minter + verifiers.insert(minter); + let ctx = Ctx::new( + &ADDRESS, + &wl_storage.storage, + &wl_storage.write_log, + &tx, + &tx_index, + gas_meter, + &keys_changed, + &verifiers, + vp_wasm_cache, + ); + + let vp = MultitokenVp { ctx }; + assert!( + !vp.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") + ); + } + + #[test] + fn test_invalid_key_update() { + let mut wl_storage = TestWlStorage::default(); + let mut keys_changed = BTreeSet::new(); + + let key = Key::from( + Address::Internal(InternalAddress::Multitoken).to_db_key(), + ) + .push(&"invalid_segment".to_string()) + .unwrap(); + wl_storage + .write_log + .write(&key, 0.try_to_vec().unwrap()) + .expect("write failed"); + + keys_changed.insert(key); + + let tx_index = TxIndex::default(); + let tx = dummy_tx(&wl_storage); + let gas_meter = VpGasMeter::new(0); + let (vp_wasm_cache, _vp_cache_dir) = wasm_cache(); + let verifiers = BTreeSet::new(); + let ctx = Ctx::new( + &ADDRESS, + &wl_storage.storage, + &wl_storage.write_log, + &tx, + &tx_index, + gas_meter, + &keys_changed, + &verifiers, + vp_wasm_cache, + ); + + let vp = MultitokenVp { ctx }; + assert!( + !vp.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") + ); + } +} diff --git a/shared/src/ledger/protocol/mod.rs b/shared/src/ledger/protocol/mod.rs index 8d360a3bd5..6cab156d7a 100644 --- a/shared/src/ledger/protocol/mod.rs +++ b/shared/src/ledger/protocol/mod.rs @@ -8,10 +8,11 @@ use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use thiserror::Error; use crate::ledger::gas::{self, BlockGasMeter, VpGasMeter}; -use crate::ledger::ibc::vp::{Ibc, IbcToken}; +use crate::ledger::ibc::vp::Ibc; use crate::ledger::native_vp::ethereum_bridge::bridge_pool_vp::BridgePoolVp; use crate::ledger::native_vp::ethereum_bridge::vp::EthBridge; use crate::ledger::native_vp::governance::GovernanceVp; +use crate::ledger::native_vp::multitoken::MultitokenVp; use crate::ledger::native_vp::parameters::{self, ParametersVp}; use crate::ledger::native_vp::replay_protection::ReplayProtectionVp; use crate::ledger::native_vp::slash_fund::SlashFundVp; @@ -58,7 +59,7 @@ pub enum Error { #[error("Parameters native VP: {0}")] ParametersNativeVpError(parameters::Error), #[error("IBC Token native VP: {0}")] - IbcTokenNativeVpError(crate::ledger::ibc::vp::IbcTokenError), + MultitokenNativeVpError(crate::ledger::native_vp::multitoken::Error), #[error("Governance native VP error: {0}")] GovernanceNativeVpError(crate::ledger::native_vp::governance::Error), #[error("SlashFund native VP error: {0}")] @@ -550,16 +551,12 @@ where gas_meter = slash_fund.ctx.gas_meter.into_inner(); result } - InternalAddress::IbcToken(_) - | InternalAddress::IbcEscrow - | InternalAddress::IbcBurn - | InternalAddress::IbcMint => { - // validate the transfer - let ibc_token = IbcToken { ctx }; - let result = ibc_token + InternalAddress::Multitoken => { + let multitoken = MultitokenVp { ctx }; + let result = multitoken .validate_tx(tx, &keys_changed, &verifiers) - .map_err(Error::IbcTokenNativeVpError); - gas_meter = ibc_token.ctx.gas_meter.into_inner(); + .map_err(Error::MultitokenNativeVpError); + gas_meter = multitoken.ctx.gas_meter.into_inner(); result } InternalAddress::EthBridge => { @@ -588,6 +585,14 @@ where replay_protection_vp.ctx.gas_meter.into_inner(); result } + InternalAddress::IbcToken(_) + | InternalAddress::Erc20(_) => { + // The address should be a part of a multitoken key + gas_meter = ctx.gas_meter.into_inner(); + Ok(verifiers.contains(&Address::Internal( + InternalAddress::Multitoken, + ))) + } }; accepted diff --git a/shared/src/ledger/queries/shell.rs b/shared/src/ledger/queries/shell.rs index 6cb42ce0c9..38fb7ca61a 100644 --- a/shared/src/ledger/queries/shell.rs +++ b/shared/src/ledger/queries/shell.rs @@ -8,7 +8,7 @@ use namada_core::ledger::storage::LastBlock; use namada_core::types::account::{Account, AccountPublicKeysMap}; use namada_core::types::address::Address; use namada_core::types::hash::Hash; -use namada_core::types::storage::{BlockHeight, BlockResults, Key, KeySeg}; +use namada_core::types::storage::{BlockHeight, BlockResults, KeySeg}; use namada_core::types::token::MaspDenom; use self::eth_bridge::{EthBridge, ETH_BRIDGE}; @@ -28,7 +28,6 @@ use crate::types::transaction::TxResult; type Conversion = ( Address, - Option, MaspDenom, Epoch, masp_primitives::transaction::components::Amount, @@ -170,7 +169,7 @@ where H: 'static + StorageHasher + Sync, { // Conversion values are constructed on request - if let Some(((addr, sub_prefix, denom), epoch, conv, pos)) = ctx + if let Some(((addr, denom), epoch, conv, pos)) = ctx .wl_storage .storage .conversion_state @@ -179,7 +178,6 @@ where { Ok(( addr.clone(), - sub_prefix.clone(), *denom, *epoch, Into::::into( diff --git a/shared/src/ledger/queries/shell/eth_bridge.rs b/shared/src/ledger/queries/shell/eth_bridge.rs index 42d0207d5b..d7d0ed249e 100644 --- a/shared/src/ledger/queries/shell/eth_bridge.rs +++ b/shared/src/ledger/queries/shell/eth_bridge.rs @@ -17,7 +17,7 @@ use namada_core::types::ethereum_events::{ }; use namada_core::types::ethereum_structs::RelayProof; use namada_core::types::storage::{BlockHeight, DbKeySeg, Key}; -use namada_core::types::token::Amount; +use namada_core::types::token::{minted_balance_key, Amount}; use namada_core::types::vote_extensions::validator_set_update::{ ValidatorSetArgs, VotingPowersMap, }; @@ -123,13 +123,12 @@ where "The Ethereum bridge storage is not initialized", )); }; - if asset == native_erc20 { - return Err(storage_api::Error::SimpleMessage( - "Wrapped NAM's supply is not kept track of", - )); - } - let keys: wrapped_erc20s::Keys = (&asset).into(); - ctx.wl_storage.read(&keys.supply()) + let token = if asset == native_erc20 { + ctx.wl_storage.storage.native_token.clone() + } else { + wrapped_erc20s::token(&asset) + }; + ctx.wl_storage.read(&minted_balance_key(&token)) } /// Helper function to read a smart contract from storage. @@ -1365,15 +1364,26 @@ mod test_ethbridge_router { assert!(resp.is_err()); } - /// Test that reading the wrapped NAM supply fails. + /// Test reading the wrapped NAM supply #[tokio::test] - async fn test_read_wnam_supply_fails() { + async fn test_read_wnam_supply() { let mut client = TestClient::new(RPC); assert_eq!(client.wl_storage.storage.last_epoch.0, 0); // initialize storage test_utils::init_default_storage(&mut client.wl_storage); + let native_erc20 = + read_native_erc20_address(&client.wl_storage).expect("Test failed"); + + // write tokens to storage + let amount = Amount::native_whole(12345); + let token = &client.wl_storage.storage.native_token; + client + .wl_storage + .write(&minted_balance_key(token), amount) + .expect("Test failed"); + // commit the changes client .wl_storage @@ -1382,21 +1392,12 @@ mod test_ethbridge_router { .expect("Test failed"); // check that reading wrapped NAM fails - let native_erc20 = - read_native_erc20_address(&client.wl_storage).expect("Test failed"); let result = RPC .shell() .eth_bridge() .read_erc20_supply(&client, &native_erc20) .await; - let Err(err) = result else { - panic!("Test failed"); - }; - - assert_eq!( - err.to_string(), - "Wrapped NAM's supply is not kept track of" - ); + assert_matches!(result, Ok(Some(a)) if a == amount); } /// Test reading the supply of an ERC20 token. @@ -1420,10 +1421,10 @@ mod test_ethbridge_router { // write tokens to storage let amount = Amount::native_whole(12345); - let keys: wrapped_erc20s::Keys = (&ERC20_TOKEN).into(); + let token = wrapped_erc20s::token(&ERC20_TOKEN); client .wl_storage - .write(&keys.supply(), amount) + .write(&minted_balance_key(&token), amount) .expect("Test failed"); // check that the supply was updated diff --git a/shared/src/ledger/queries/types.rs b/shared/src/ledger/queries/types.rs index 720b1ce7d6..78f136cad2 100644 --- a/shared/src/ledger/queries/types.rs +++ b/shared/src/ledger/queries/types.rs @@ -108,7 +108,7 @@ pub trait Client { ) -> Result; /// `/abci_info`: get information about the ABCI application. - async fn abci_info(&self) -> Result { + async fn abci_info(&self) -> Result { Ok(self.perform(abci_info::Request).await?.response) } @@ -271,7 +271,7 @@ pub trait Client { /// /// Returns empty result (200 OK) on success, no response in case of an /// error. - async fn health(&self) -> Result<(), Error> { + async fn health(&self) -> Result<(), RpcError> { self.perform(health::Request).await?; Ok(()) } diff --git a/shared/src/ledger/queries/vp/pos.rs b/shared/src/ledger/queries/vp/pos.rs index d872aa5002..075b936e25 100644 --- a/shared/src/ledger/queries/vp/pos.rs +++ b/shared/src/ledger/queries/vp/pos.rs @@ -1,21 +1,24 @@ //! Queries router and handlers for PoS validity predicate -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use namada_core::ledger::storage_api::collections::lazy_map; use namada_core::ledger::storage_api::OptionExt; +use namada_proof_of_stake::parameters::PosParams; use namada_proof_of_stake::types::{ BondId, BondsAndUnbondsDetail, BondsAndUnbondsDetails, CommissionPair, - Slash, WeightedValidator, + Slash, ValidatorState, WeightedValidator, }; use namada_proof_of_stake::{ - self, below_capacity_validator_set_handle, bond_amount, bond_handle, - consensus_validator_set_handle, find_all_slashes, - find_delegation_validators, find_delegations, read_all_validator_addresses, - read_pos_params, read_total_stake, - read_validator_max_commission_rate_change, read_validator_stake, - unbond_handle, validator_commission_rate_handle, validator_slashes_handle, + self, bond_amount, bond_handle, find_all_enqueued_slashes, + find_all_slashes, find_delegation_validators, find_delegations, + read_all_validator_addresses, + read_below_capacity_validator_set_addresses_with_stake, + read_consensus_validator_set_addresses_with_stake, read_pos_params, + read_total_stake, read_validator_max_commission_rate_change, + read_validator_stake, unbond_handle, validator_commission_rate_handle, + validator_slashes_handle, validator_state_handle, }; use crate::ledger::queries::types::RequestCtx; @@ -43,18 +46,23 @@ router! {POS, ( "commission" / [validator: Address] / [epoch: opt Epoch] ) -> Option = validator_commission, + + ( "state" / [validator: Address] / [epoch: opt Epoch] ) + -> Option = validator_state, }, ( "validator_set" ) = { ( "consensus" / [epoch: opt Epoch] ) - -> HashSet = consensus_validator_set, + -> BTreeSet = consensus_validator_set, ( "below_capacity" / [epoch: opt Epoch] ) - -> HashSet = below_capacity_validator_set, + -> BTreeSet = below_capacity_validator_set, // TODO: add "below_threshold" }, + ( "pos_params") -> PosParams = pos_params, + ( "total_stake" / [epoch: opt Epoch] ) -> token::Amount = total_stake, @@ -82,6 +90,9 @@ router! {POS, ( "bonds_and_unbonds" / [source: opt Address] / [validator: opt Address] ) -> BondsAndUnbondsDetails = bonds_and_unbonds, + ( "enqueued_slashes" ) + -> HashMap>> = enqueued_slashes, + ( "all_slashes" ) -> HashMap> = slashes, ( "is_delegator" / [addr: Address ] / [epoch: opt Epoch] ) -> bool = is_delegator, @@ -133,6 +144,15 @@ impl Enriched { // Handlers that implement the functions via `trait StorageRead`: +/// Get the PoS parameters +fn pos_params(ctx: RequestCtx<'_, D, H>) -> storage_api::Result +where + D: 'static + DB + for<'iter> DBIter<'iter> + Sync, + H: 'static + StorageHasher + Sync, +{ + read_pos_params(ctx.wl_storage) +} + /// Find if the given address belongs to a validator account. fn is_validator( ctx: RequestCtx<'_, D, H>, @@ -203,6 +223,26 @@ where } } +/// Get the validator state +fn validator_state( + ctx: RequestCtx<'_, D, H>, + validator: Address, + epoch: Option, +) -> storage_api::Result> +where + D: 'static + DB + for<'iter> DBIter<'iter> + Sync, + H: 'static + StorageHasher + Sync, +{ + let epoch = epoch.unwrap_or(ctx.wl_storage.storage.last_epoch); + let params = read_pos_params(ctx.wl_storage)?; + let state = validator_state_handle(&validator).get( + ctx.wl_storage, + epoch, + ¶ms, + )?; + Ok(state) +} + /// Get the total stake of a validator at the given epoch or current when /// `None`. The total stake is a sum of validator's self-bonds and delegations /// to their address. @@ -226,64 +266,29 @@ where fn consensus_validator_set( ctx: RequestCtx<'_, D, H>, epoch: Option, -) -> storage_api::Result> +) -> storage_api::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { let epoch = epoch.unwrap_or(ctx.wl_storage.storage.last_epoch); - consensus_validator_set_handle() - .at(&epoch) - .iter(ctx.wl_storage)? - .map(|next_result| { - next_result.map( - |( - lazy_map::NestedSubKey::Data { - key: bonded_stake, - nested_sub_key: _position, - }, - address, - )| { - WeightedValidator { - bonded_stake, - address, - } - }, - ) - }) - .collect() + read_consensus_validator_set_addresses_with_stake(ctx.wl_storage, epoch) } /// Get all the validator in the below-capacity set with their bonded stake. fn below_capacity_validator_set( ctx: RequestCtx<'_, D, H>, epoch: Option, -) -> storage_api::Result> +) -> storage_api::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { let epoch = epoch.unwrap_or(ctx.wl_storage.storage.last_epoch); - below_capacity_validator_set_handle() - .at(&epoch) - .iter(ctx.wl_storage)? - .map(|next_result| { - next_result.map( - |( - lazy_map::NestedSubKey::Data { - key: bonded_stake, - nested_sub_key: _position, - }, - address, - )| { - WeightedValidator { - bonded_stake: bonded_stake.into(), - address, - } - }, - ) - }) - .collect() + read_below_capacity_validator_set_addresses_with_stake( + ctx.wl_storage, + epoch, + ) } /// Get the total stake in PoS system at the given epoch or current when `None`. @@ -496,7 +501,19 @@ where find_all_slashes(ctx.wl_storage) } -/// All slashes +/// Enqueued slashes +fn enqueued_slashes( + ctx: RequestCtx<'_, D, H>, +) -> storage_api::Result>>> +where + D: 'static + DB + for<'iter> DBIter<'iter> + Sync, + H: 'static + StorageHasher + Sync, +{ + let current_epoch = ctx.wl_storage.storage.last_epoch; + find_all_enqueued_slashes(ctx.wl_storage, current_epoch) +} + +/// Native validator address by looking up the Tendermint address fn validator_by_tm_addr( ctx: RequestCtx<'_, D, H>, tm_addr: String, diff --git a/shared/src/ledger/queries/vp/token.rs b/shared/src/ledger/queries/vp/token.rs index cbad27005f..ffb1117c91 100644 --- a/shared/src/ledger/queries/vp/token.rs +++ b/shared/src/ledger/queries/vp/token.rs @@ -1,15 +1,15 @@ +//! Token validity predicate queries + use namada_core::ledger::storage::{DBIter, StorageHasher, DB}; use namada_core::ledger::storage_api; use namada_core::ledger::storage_api::token::read_denom; use namada_core::types::address::Address; -use namada_core::types::storage::Key; use namada_core::types::token; use crate::ledger::queries::RequestCtx; router! {TOKEN, - ( "denomination" / [addr: Address] / [sub_prefix: opt Key] ) -> Option = denomination, - ( "denomination" / [addr: Address] / "ibc" / [_ibc_junk: String] ) -> Option = denomination_ibc, + ( "denomination" / [addr: Address] ) -> Option = denomination, } /// Get the number of decimal places (in base 10) for a @@ -17,27 +17,47 @@ router! {TOKEN, fn denomination( ctx: RequestCtx<'_, D, H>, addr: Address, - sub_prefix: Option, ) -> storage_api::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - read_denom(ctx.wl_storage, &addr, sub_prefix.as_ref()) + read_denom(ctx.wl_storage, &addr) } -// TODO Please fix this +#[cfg(any(test, feature = "async-client"))] +pub mod client_only_methods { + use borsh::BorshDeserialize; -/// Get the number of decimal places (in base 10) for a -/// token specified by `addr`. -fn denomination_ibc( - ctx: RequestCtx<'_, D, H>, - addr: Address, - _ibc_junk: String, -) -> storage_api::Result> -where - D: 'static + DB + for<'iter> DBIter<'iter> + Sync, - H: 'static + StorageHasher + Sync, -{ - read_denom(ctx.wl_storage, &addr, None) + use super::Token; + use crate::ledger::queries::{Client, RPC}; + use crate::types::address::Address; + use crate::types::token; + + impl Token { + /// Get the balance of the given `token` belonging to the given `owner`. + pub async fn balance( + &self, + client: &CLIENT, + token: &Address, + owner: &Address, + ) -> Result::Error> + where + CLIENT: Client + Sync, + { + let balance_key = token::balance_key(token, owner); + let response = RPC + .shell() + .storage_value(client, None, None, false, &balance_key) + .await?; + + let balance = if response.data.is_empty() { + token::Amount::default() + } else { + token::Amount::try_from_slice(&response.data) + .unwrap_or_default() + }; + Ok(balance) + } + } } diff --git a/shared/src/ledger/rpc.rs b/shared/src/ledger/rpc.rs index 5a809d23b8..c9e458692a 100644 --- a/shared/src/ledger/rpc.rs +++ b/shared/src/ledger/rpc.rs @@ -15,9 +15,12 @@ use namada_core::types::account::Account; use namada_core::types::address::Address; use namada_core::types::storage::Key; use namada_core::types::token::{ - Amount, DenominatedAmount, Denomination, MaspDenom, TokenAddress, + Amount, DenominatedAmount, Denomination, MaspDenom, +}; +use namada_proof_of_stake::parameters::PosParams; +use namada_proof_of_stake::types::{ + BondsAndUnbondsDetails, CommissionPair, ValidatorState, }; -use namada_proof_of_stake::types::{BondsAndUnbondsDetails, CommissionPair}; use serde::Serialize; use crate::ledger::args::InputAmount; @@ -38,7 +41,6 @@ use crate::types::governance::{ProposalVote, VotePower}; use crate::types::hash::Hash; use crate::types::key::common; use crate::types::storage::{BlockHeight, BlockResults, Epoch, PrefixValue}; -use crate::types::token::balance_key; use crate::types::{storage, token}; /// Query the status of a given transaction. @@ -139,9 +141,10 @@ pub async fn get_token_balance( client: &C, token: &Address, owner: &Address, -) -> Option { - let balance_key = balance_key(token, owner); - query_storage_value(client, &balance_key).await +) -> token::Amount { + unwrap_client_response::( + RPC.vp().token().balance(client, token, owner).await, + ) } /// Check if the given address is a known validator. @@ -234,7 +237,6 @@ pub async fn query_conversion( asset_type: AssetType, ) -> Option<( Address, - Option, MaspDenom, Epoch, masp_primitives::transaction::components::Amount, @@ -689,6 +691,13 @@ pub async fn get_proposal_votes( } } +/// Get the PoS parameters +pub async fn get_pos_params( + client: &C, +) -> PosParams { + unwrap_client_response::(RPC.vp().pos().pos_params(client).await) +} + /// Get all validators in the given epoch pub async fn get_all_validators( client: &C, @@ -729,6 +738,20 @@ pub async fn get_validator_stake( .unwrap_or_default() } +/// Query and return a validator's state +pub async fn get_validator_state( + client: &C, + validator: &Address, + epoch: Option, +) -> Option { + unwrap_client_response::>( + RPC.vp() + .pos() + .validator_state(client, validator, &epoch) + .await, + ) +} + /// Get the delegator's delegation pub async fn get_delegators_delegation< C: crate::ledger::queries::Client + Sync, @@ -983,7 +1006,6 @@ pub async fn validate_amount( client: &C, amount: InputAmount, token: &Address, - sub_prefix: &Option, force: bool, ) -> Option { let input_amount = match amount { @@ -991,10 +1013,7 @@ pub async fn validate_amount( InputAmount::Validated(amt) => return Some(amt), }; let denom = unwrap_client_response::>( - RPC.vp() - .token() - .denomination(client, token, sub_prefix) - .await, + RPC.vp().token().denomination(client, token).await, ) .or_else(|| { if force { @@ -1090,14 +1109,11 @@ pub async fn format_denominated_amount< C: crate::ledger::queries::Client + Sync, >( client: &C, - token: &TokenAddress, + token: &Address, amount: token::Amount, ) -> String { let denom = unwrap_client_response::>( - RPC.vp() - .token() - .denomination(client, &token.address, &token.sub_prefix) - .await, + RPC.vp().token().denomination(client, token).await, ) .unwrap_or_else(|| { println!( diff --git a/shared/src/ledger/signing.rs b/shared/src/ledger/signing.rs index a5af08fbc3..f7252bf621 100644 --- a/shared/src/ledger/signing.rs +++ b/shared/src/ledger/signing.rs @@ -13,10 +13,8 @@ use namada_core::types::account::AccountPublicKeysMap; use namada_core::types::address::{ masp, masp_tx_key, Address, ImplicitAddress, }; -use namada_core::types::storage::Key; -use namada_core::types::token::{ - self, Amount, DenominatedAmount, MaspDenom, TokenAddress, -}; +// use namada_core::types::storage::Key; +use namada_core::types::token::{self, Amount, DenominatedAmount, MaspDenom}; use namada_core::types::transaction::pos; use prost::Message; use serde::{Deserialize, Serialize}; @@ -314,10 +312,7 @@ pub async fn solve_pow_challenge( .unwrap_or_default(); let is_bal_sufficient = fee_amount <= balance; if !is_bal_sufficient { - let token_addr = TokenAddress { - address: args.fee_token.clone(), - sub_prefix: None, - }; + let token_addr = args.fee_token.clone(); let err_msg = format!( "The wrapper transaction source doesn't have enough balance to \ pay fee {}, got {}.", @@ -412,23 +407,13 @@ fn make_ledger_amount_addr( output: &mut Vec, amount: DenominatedAmount, token: &Address, - sub_prefix: &Option, prefix: &str, ) { - let token_address = TokenAddress { - address: token.clone(), - sub_prefix: sub_prefix.clone(), - }; if let Some(token) = tokens.get(token) { - output.push(format!( - "{}Amount {}: {}", - prefix, - token_address.format_with_alias(token), - amount - )); + output.push(format!("{}Amount {}: {}", prefix, token, amount)); } else { output.extend(vec![ - format!("{}Token: {}", prefix, token_address), + format!("{}Token: {}", prefix, token), format!("{}Amount: {}", prefix, amount), ]); } @@ -442,27 +427,21 @@ async fn make_ledger_amount_asset( output: &mut Vec, amount: u64, token: &AssetType, - assets: &HashMap, MaspDenom, Epoch)>, + assets: &HashMap, prefix: &str, ) { - if let Some((token, sub_prefix, _, _epoch)) = assets.get(token) { + if let Some((token, _, _epoch)) = assets.get(token) { // If the AssetType can be decoded, then at least display Addressees - let token_addr = TokenAddress { - address: token.clone(), - sub_prefix: sub_prefix.clone(), - }; let formatted_amt = - format_denominated_amount(client, &token_addr, amount.into()).await; + format_denominated_amount(client, token, amount.into()).await; if let Some(token) = tokens.get(token) { - output.push(format!( - "{}Amount: {} {}", - prefix, - token_addr.format_with_alias(token), - formatted_amt, - )); + output + .push( + format!("{}Amount: {} {}", prefix, token, formatted_amt,), + ); } else { output.extend(vec![ - format!("{}Token: {}", prefix, token_addr), + format!("{}Token: {}", prefix, token), format!("{}Amount: {}", prefix, formatted_amt), ]); } @@ -546,7 +525,7 @@ pub async fn make_ledger_masp_endpoints< output: &mut Vec, transfer: &Transfer, builder: Option<&MaspBuilder>, - assets: &HashMap, MaspDenom, Epoch)>, + assets: &HashMap, ) { if transfer.source != masp() { output.push(format!("Sender : {}", transfer.source)); @@ -556,7 +535,6 @@ pub async fn make_ledger_masp_endpoints< output, transfer.amount, &transfer.token, - &transfer.sub_prefix, "Sending ", ); } @@ -584,7 +562,6 @@ pub async fn make_ledger_masp_endpoints< output, transfer.amount, &transfer.token, - &transfer.sub_prefix, "Receiving ", ); } @@ -610,7 +587,6 @@ pub async fn make_ledger_masp_endpoints< output, transfer.amount, &transfer.token, - &transfer.sub_prefix, "", ); } @@ -951,16 +927,10 @@ pub async fn to_ledger_vector< Section::MaspBuilder(builder) if builder.target == shielded_hash => { - for (addr, sub_prefix, denom, epoch) in &builder.asset_types - { + for (addr, denom, epoch) in &builder.asset_types { asset_types.insert( - make_asset_type( - Some(*epoch), - addr, - sub_prefix, - *denom, - ), - (addr.clone(), sub_prefix.clone(), *denom, *epoch), + make_asset_type(Some(*epoch), addr, *denom), + (addr.clone(), *denom, *epoch), ); } Some(builder) @@ -1145,10 +1115,7 @@ pub async fn to_ledger_vector< } if let Some(wrapper) = tx.header.wrapper() { - let gas_token = TokenAddress { - address: wrapper.fee.token.clone(), - sub_prefix: None, - }; + let gas_token = wrapper.fee.token.clone(); let gas_limit = format_denominated_amount( client, &gas_token, diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 3f00409c78..0281c7f5a5 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -18,15 +18,14 @@ use masp_primitives::transaction::components::transparent::fees::{ use masp_primitives::transaction::components::Amount; use namada_core::types::address::{masp, Address}; use namada_core::types::dec::Dec; -use namada_core::types::storage::Key; use namada_core::types::token::MaspDenom; use namada_proof_of_stake::parameters::PosParams; -use namada_proof_of_stake::types::CommissionPair; +use namada_proof_of_stake::types::{CommissionPair, ValidatorState}; use prost::EncodeError; use thiserror::Error; use super::rpc::query_wasm_code_hash; -use super::signing::wrap_tx; +use super::signing; use crate::ibc::applications::transfer::msgs::transfer::MsgTransfer; use crate::ibc::core::ics04_channel::timeout::TimeoutHeight; use crate::ibc::signer::Signer; @@ -37,8 +36,10 @@ use crate::ibc_proto::cosmos::base::v1beta1::Coin; use crate::ledger::args::{self, InputAmount}; use crate::ledger::governance::storage as gov_storage; use crate::ledger::masp::{ShieldedContext, ShieldedUtils}; -use crate::ledger::rpc::{self, validate_amount, TxBroadcastData, TxResponse}; -use crate::ledger::signing::TxSigningKey; +use crate::ledger::rpc::{ + self, format_denominated_amount, validate_amount, TxBroadcastData, + TxResponse, +}; use crate::ledger::wallet::{Wallet, WalletUtils}; use crate::proto::{MaspBuilder, Tx}; use crate::tendermint_rpc::endpoint::broadcast::tx_sync::Response; @@ -46,7 +47,7 @@ use crate::tendermint_rpc::error::Error as RpcError; use crate::types::control_flow::{time, ProceedOrElse}; use crate::types::key::*; use crate::types::masp::TransferTarget; -use crate::types::storage::{Epoch, RESERVED_ADDRESS_PREFIX}; +use crate::types::storage::Epoch; use crate::types::time::DateTimeUtc; use crate::types::transaction::account::{InitAccount, UpdateAccount}; use crate::types::transaction::{pos, TxType}; @@ -114,6 +115,18 @@ pub enum Error { /// Invalid validator address #[error("The address {0} doesn't belong to any known validator account.")] InvalidValidatorAddress(Address), + /// Not jailed at pipeline epoch + #[error( + "The validator address {0} is not jailed at epoch when it would be \ + restored." + )] + ValidatorNotCurrentlyJailed(Address), + /// Validator still frozen and ineligible to be unjailed + #[error( + "The validator address {0} is currently frozen and ineligible to be \ + unjailed." + )] + ValidatorFrozenFromUnjailing(Address), /// Rate of epoch change too large for current epoch #[error( "New rate, {0}, is too large of a change with respect to the \ @@ -278,7 +291,7 @@ pub async fn prepare_tx( tx_builder } else { let epoch = rpc::query_epoch(client).await; - wrap_tx( + signing::wrap_tx( client, tx_builder, args, @@ -317,8 +330,7 @@ pub async fn process_tx< #[cfg(feature = "std")] { - use super::signing; - signing::generate_test_vector(client, wallet, &tx).await; + super::signing::generate_test_vector(client, wallet, &tx).await; } if args.dry_run { @@ -544,18 +556,18 @@ where /// decode components of a masp note pub fn decode_component( - (addr, sub, denom, epoch): (Address, Option, MaspDenom, Epoch), + (addr, denom, epoch): (Address, MaspDenom, Epoch), val: i128, res: &mut HashMap, mk_key: F, ) where - F: FnOnce(Address, Option, Epoch) -> K, + F: FnOnce(Address, Epoch) -> K, K: Eq + std::hash::Hash, { let decoded_change = token::Change::from_masp_denominated(val, denom) .expect("expected this to fit"); - res.entry(mk_key(addr, sub, epoch)) + res.entry(mk_key(addr, epoch)) .and_modify(|val| *val += decoded_change) .or_insert(decoded_change); } @@ -631,11 +643,7 @@ pub async fn build_validator_commission_change< .await .unwrap(); - // TODO: put following two let statements in its own function - let params_key = crate::ledger::pos::params_key(); - let params = rpc::query_storage_value::(client, ¶ms_key) - .await - .expect("Parameter should be defined."); + let params: PosParams = rpc::get_pos_params(client).await; let validator = validator.clone(); if rpc::is_validator(client, &validator).await { @@ -724,9 +732,51 @@ pub async fn build_unjail_validator< } } - let tx_code_path = String::from_utf8(tx_code_path).unwrap(); + let params: PosParams = rpc::get_pos_params(client).await; + let current_epoch = rpc::query_epoch(client).await; + let pipeline_epoch = current_epoch + params.pipeline_len; + + let validator_state_at_pipeline = + rpc::get_validator_state(client, &validator, Some(pipeline_epoch)) + .await + .expect("Validator state should be defined."); + if validator_state_at_pipeline != ValidatorState::Jailed { + eprintln!( + "The given validator address {} is not jailed at the pipeline \ + epoch when it would be restored to one of the validator sets.", + &validator + ); + if !tx.force { + return Err(Error::ValidatorNotCurrentlyJailed(validator.clone())); + } + } + + let last_slash_epoch_key = + crate::ledger::pos::validator_last_slash_key(&validator); + let last_slash_epoch = + rpc::query_storage_value::(client, &last_slash_epoch_key) + .await; + if let Some(last_slash_epoch) = last_slash_epoch { + let eligible_epoch = + last_slash_epoch + params.slash_processing_epoch_offset(); + if current_epoch < eligible_epoch { + eprintln!( + "The given validator address {} is currently frozen and not \ + yet eligible to be unjailed.", + &validator + ); + if !tx.force { + return Err(Error::ValidatorNotCurrentlyJailed( + validator.clone(), + )); + } + } + } + let tx_code_hash = - query_wasm_code_hash(client, tx_code_path).await.unwrap(); + query_wasm_code_hash(client, tx_code_path.to_str().unwrap()) + .await + .unwrap(); let _data = validator .clone() @@ -895,7 +945,6 @@ pub async fn build_unbond< let tx_builder = tx_builder.add_code_from_hash(tx_code_hash).add_data(data); - let _default_signer = source.unwrap_or_else(|| validator.clone()); let tx_builder = prepare_tx::( client, &tx, @@ -1076,7 +1125,6 @@ pub async fn build_ibc_transfer( source, receiver, token, - sub_prefix, amount, port_id, channel_id, @@ -1090,20 +1138,8 @@ pub async fn build_ibc_transfer( let source = source_exists_or_err(source.clone(), tx.force, client).await?; // We cannot check the receiver - let token = token_exists_or_err(token, tx.force, client).await?; - // Check source balance - let (sub_prefix, balance_key) = match sub_prefix { - Some(sub_prefix) => { - let sub_prefix = storage::Key::parse(sub_prefix).unwrap(); - let prefix = token::multitoken_balance_prefix(&token, &sub_prefix); - ( - Some(sub_prefix), - token::multitoken_balance_key(&prefix, &source), - ) - } - None => (None, token::balance_key(&token, &source)), - }; + let balance_key = token::balance_key(&token, &source); check_balance_too_low_err( &token, @@ -1120,18 +1156,16 @@ pub async fn build_ibc_transfer( .await .unwrap(); - let denom = match sub_prefix { - // To parse IbcToken address, remove the address prefix - Some(sp) => sp.to_string().replace(RESERVED_ADDRESS_PREFIX, ""), - None => token.to_string(), - }; let amount = amount .to_string_native() .split('.') .next() .expect("invalid amount") .to_string(); - let token = Coin { denom, amount }; + let token = Coin { + denom: token.to_string(), + amount, + }; // this height should be that of the destination chain, not this chain let timeout_height = match timeout_height { @@ -1189,9 +1223,9 @@ pub async fn build_ibc_transfer( /// Returns true only if a new decoding has been added to the given set. async fn add_asset_type< C: crate::ledger::queries::Client + Sync, - U: ShieldedUtils, + U: ShieldedUtils, >( - asset_types: &mut HashSet<(Address, Option, MaspDenom, Epoch)>, + asset_types: &mut HashSet<(Address, MaspDenom, Epoch)>, shielded: &mut ShieldedContext, client: &C, asset_type: AssetType, @@ -1210,7 +1244,7 @@ async fn add_asset_type< /// type information. async fn used_asset_types< C: crate::ledger::queries::Client + Sync, - U: ShieldedUtils, + U: ShieldedUtils, P, R, K, @@ -1219,7 +1253,7 @@ async fn used_asset_types< shielded: &mut ShieldedContext, client: &C, builder: &Builder, -) -> Result, MaspDenom, Epoch)>, RpcError> { +) -> Result, RpcError> { let mut asset_types = HashSet::new(); // Collect all the asset types used in the Sapling inputs for input in builder.sapling_inputs() { @@ -1261,7 +1295,7 @@ async fn used_asset_types< /// Submit an ordinary transfer pub async fn build_transfer< C: crate::ledger::queries::Client + Sync, - U: ShieldedUtils, + U: ShieldedUtils, >( client: &C, shielded: &mut ShieldedContext, @@ -1276,37 +1310,18 @@ pub async fn build_transfer< source_exists_or_err(source.clone(), args.tx.force, client).await?; // Check that the target address exists on chain target_exists_or_err(target.clone(), args.tx.force, client).await?; - // Check that the token address exists on chain - token_exists_or_err(token.clone(), args.tx.force, client).await?; // Check source balance - let (sub_prefix, balance_key) = match &args.sub_prefix { - Some(ref sub_prefix) => { - let sub_prefix = storage::Key::parse(sub_prefix).unwrap(); - let prefix = token::multitoken_balance_prefix(&token, &sub_prefix); - ( - Some(sub_prefix), - token::multitoken_balance_key(&prefix, &source), - ) - } - None => (None, token::balance_key(&token, &source)), - }; + let balance_key = token::balance_key(&token, &source); // validate the amount given - let validated_amount = validate_amount( - client, - args.amount, - &token, - &sub_prefix, - args.tx.force, - ) - .await - .expect("expected to validate amount"); + let validated_amount = + validate_amount(client, args.amount, &token, args.tx.force) + .await + .expect("expected to validate amount"); let validate_fee = validate_amount( client, args.tx.fee_amount, &args.tx.fee_token, - // TODO: Currently multi-tokens cannot be used to pay fees - &None, args.tx.force, ) .await @@ -1314,7 +1329,6 @@ pub async fn build_transfer< args.amount = InputAmount::Validated(validated_amount); args.tx.fee_amount = InputAmount::Validated(validate_fee); - check_balance_too_low_err::( &token, &source, @@ -1337,8 +1351,6 @@ pub async fn build_transfer< } else { (validated_amount.amount, token, false) }; - let _default_signer = - TxSigningKey::WalletAddress(args.source.effective_address()); // Determine whether to pin this transaction to a storage key let key = match &args.target { @@ -1412,7 +1424,6 @@ pub async fn build_transfer< source: source.clone(), target: target.clone(), token: token.clone(), - sub_prefix: sub_prefix.clone(), amount: validated_amount, key: key.clone(), // Link the Transfer to the MASP Transaction by hash code @@ -1671,26 +1682,6 @@ where } } -/// Returns the given token if the given address exists on chain -/// otherwise returns an error, force forces the address through even -/// if it isn't on chain -pub async fn token_exists_or_err( - token: Address, - force: bool, - client: &C, -) -> Result { - let message = - format!("The token address {} doesn't exist on chain.", token); - address_exists_or_err( - token, - force, - client, - message, - Error::TokenDoesNotExist, - ) - .await -} - /// Returns the given source address if the given address exists on chain /// otherwise returns an error, force forces the address through even /// if it isn't on chain @@ -1754,8 +1745,8 @@ async fn check_balance_too_low_err( transfer is {} and the balance is {}.", source, token, - amount.to_string_native(), - balance.to_string_native() + format_denominated_amount(client, token, amount).await, + format_denominated_amount(client, token, balance).await, ); Ok(()) } else { diff --git a/shared/src/vm/host_env.rs b/shared/src/vm/host_env.rs index 56cbc7d680..f290e484ff 100644 --- a/shared/src/vm/host_env.rs +++ b/shared/src/vm/host_env.rs @@ -23,6 +23,9 @@ use crate::types::hash::Hash; use crate::types::ibc::IbcEvent; use crate::types::internal::HostEnvResult; use crate::types::storage::{BlockHeight, Key, TxIndex}; +use crate::types::token::{ + is_any_minted_balance_key, is_any_minter_key, is_any_token_balance_key, +}; use crate::vm::memory::VmMemory; use crate::vm::prefix_iter::{PrefixIteratorId, PrefixIterators}; use crate::vm::{HostRef, MutHostRef}; @@ -902,9 +905,20 @@ where H: StorageHasher, CA: WasmCacheAccess, { + // Get the token if the key is a balance or minter key + let token = if let Some([token, _]) = is_any_token_balance_key(key) { + Some(token) + } else { + is_any_minted_balance_key(key).or_else(|| is_any_minter_key(key)) + }; + let write_log = unsafe { env.ctx.write_log.get() }; let storage = unsafe { env.ctx.storage.get() }; for addr in key.find_addresses() { + // skip if the address is a token address + if Some(&addr) == token { + continue; + } // skip the check for implicit and internal addresses if let Address::Implicit(_) | Address::Internal(_) = &addr { continue; diff --git a/test_utils/src/tx_data.rs b/test_utils/src/tx_data.rs index 878217bc2c..a985479237 100644 --- a/test_utils/src/tx_data.rs +++ b/test_utils/src/tx_data.rs @@ -2,7 +2,9 @@ //! Namada transaction. use borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::types::address::Address; use namada_core::types::storage; +use namada_core::types::token::Amount; /// Represents an arbitrary write to storage at the specified key. This should /// be used alongside the test `tx_write.wasm`. @@ -23,3 +25,27 @@ pub struct TxWriteData { /// The bytes to be written. pub value: Vec, } + +/// Represents minting of the specified token. This should +/// be used alongside the test `tx_mint_tokens.wasm`. +#[derive( + Clone, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + BorshSerialize, + BorshDeserialize, +)] +pub struct TxMintData { + /// The minter to mint the token + pub minter: Address, + /// The minted target + pub target: Address, + /// The minted token + pub token: Address, + /// The minted amount + pub amount: Amount, +} diff --git a/tests/Cargo.toml b/tests/Cargo.toml index b36bfcabb6..65c0d306f3 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -25,6 +25,7 @@ abciplus = [ "namada_tx_prelude/abciplus", "namada_apps/abciplus" ] + wasm-runtime = ["namada/wasm-runtime"] [dependencies] @@ -33,12 +34,16 @@ namada_core = {path = "../core", features = ["testing"]} namada_test_utils = {path = "../test_utils"} namada_vp_prelude = {path = "../vp_prelude"} namada_tx_prelude = {path = "../tx_prelude"} +async-trait.workspace = true chrono.workspace = true +clap.workspace = true concat-idents.workspace = true derivative.workspace = true hyper = {version = "0.14.20", features = ["full"]} ibc-relayer-types.workspace = true ibc-relayer.workspace = true +lazy_static.workspace = true +num-traits.workspace = true prost.workspace = true regex.workspace = true serde_json.workspace = true diff --git a/tests/src/e2e.rs b/tests/src/e2e.rs index ffe2ec0d86..10a6f69d6e 100644 --- a/tests/src/e2e.rs +++ b/tests/src/e2e.rs @@ -16,6 +16,5 @@ pub mod eth_bridge_tests; pub mod helpers; pub mod ibc_tests; pub mod ledger_tests; -pub mod multitoken_tests; pub mod setup; pub mod wallet_tests; diff --git a/tests/src/e2e/eth_bridge_tests/helpers.rs b/tests/src/e2e/eth_bridge_tests/helpers.rs index 9fc7acd781..09df129e2b 100644 --- a/tests/src/e2e/eth_bridge_tests/helpers.rs +++ b/tests/src/e2e/eth_bridge_tests/helpers.rs @@ -14,7 +14,6 @@ use namada::ledger::eth_bridge::{ use namada::types::address::{wnam, Address}; use namada::types::ethereum_events::{EthAddress, Uint}; use namada_apps::config::ethereum_bridge; -use namada_core::ledger::eth_bridge; use namada_core::types::ethereum_events::{EthereumEvent, TransferToNamada}; use namada_core::types::token; @@ -173,16 +172,13 @@ pub fn attempt_wrapped_erc20_transfer( ) -> Result { let ledger_address = get_actor_rpc(test, node); - let eth_bridge_addr = eth_bridge::ADDRESS.to_string(); - let sub_prefix = wrapped_erc20s::sub_prefix(asset).to_string(); + let token = wrapped_erc20s::token(asset).to_string(); let amount = amount.to_string(); let transfer_args = vec![ "transfer", "--token", - ð_bridge_addr, - "--sub-prefix", - &sub_prefix, + &token, "--source", from, "--target", @@ -208,10 +204,8 @@ pub fn find_wrapped_erc20_balance( ) -> Result { let ledger_address = get_actor_rpc(test, node); - let sub_prefix = wrapped_erc20s::sub_prefix(asset); - let prefix = - token::multitoken_balance_prefix(ð_bridge::ADDRESS, &sub_prefix); - let balance_key = token::multitoken_balance_key(&prefix, owner); + let token = wrapped_erc20s::token(asset); + let balance_key = token::balance_key(&token, owner); let mut bytes = run!( test, Bin::Client, diff --git a/tests/src/e2e/helpers.rs b/tests/src/e2e/helpers.rs index 7c79fdfdb6..1fde997ef6 100644 --- a/tests/src/e2e/helpers.rs +++ b/tests/src/e2e/helpers.rs @@ -53,6 +53,7 @@ where /// and returns the [`Test`] handle and [`NamadaBgCmd`] for the validator node. /// It blocks until the node is ready to receive RPC requests from /// `namadac`. +#[allow(dead_code)] pub fn setup_single_node_test() -> Result<(Test, NamadaBgCmd)> { let test = setup::single_node_net()?; run_single_node_test_from(test) @@ -71,6 +72,7 @@ pub fn run_single_node_test_from(test: Test) -> Result<(Test, NamadaBgCmd)> { } /// Initialize an established account. +#[allow(dead_code)] pub fn init_established_account( test: &Test, rpc_addr: &str, @@ -424,20 +426,29 @@ pub fn epoch_sleep( ledger_address: &str, timeout_secs: u64, ) -> Result { - let old_epoch = get_epoch(test, ledger_address)?; - let start = Instant::now(); - let loop_timeout = Duration::new(timeout_secs, 0); - loop { - if Instant::now().duration_since(start) > loop_timeout { - panic!("Timed out waiting for the next epoch"); - } - let epoch = get_epoch(test, ledger_address)?; - if epoch > old_epoch { - break Ok(epoch); - } else { - sleep(10); - } - } + let mut find = run!( + test, + Bin::Client, + &["utils", "epoch-sleep", "--node", ledger_address], + Some(timeout_secs) + )?; + parse_reached_epoch(&mut find) +} + +pub fn parse_reached_epoch(find: &mut NamadaCmd) -> Result { + let (unread, matched) = find.exp_regex("Reached epoch .*")?; + let epoch_str = strip_trailing_newline(&matched) + .trim() + .rsplit_once(' ') + .unwrap() + .1; + let epoch = u64::from_str(epoch_str).map_err(|e| { + eyre!(format!( + "Epoch: {} parsed from {}, Error: {}\n\nOutput: {}", + epoch_str, matched, e, unread + )) + })?; + Ok(Epoch(epoch)) } /// Wait for txs and VPs WASM compilations to finish. This is useful to avoid a diff --git a/tests/src/e2e/ibc_tests.rs b/tests/src/e2e/ibc_tests.rs index 26947eb2ec..52cb6ca9c0 100644 --- a/tests/src/e2e/ibc_tests.rs +++ b/tests/src/e2e/ibc_tests.rs @@ -72,7 +72,7 @@ use namada::ledger::storage::ics23_specs::ibc_proof_specs; use namada::ledger::storage::traits::Sha256Hasher; use namada::types::address::{Address, InternalAddress}; use namada::types::key::PublicKey; -use namada::types::storage::{BlockHeight, Key, RESERVED_ADDRESS_PREFIX}; +use namada::types::storage::{BlockHeight, Key}; use namada::types::token::Amount; use namada_apps::client::rpc::{ query_storage_value, query_storage_value_bytes, @@ -723,7 +723,6 @@ fn transfer_token( ALBERT_KEY, port_channel_id_a, None, - None, false, )?; let packet = match get_event(test_a, height)? { @@ -782,11 +781,7 @@ fn transfer_received_token( "{}/{}/{}", port_channel_id.port_id, port_channel_id.channel_id, xan ); - let sub_prefix = ibc_token_prefix(denom) - .unwrap() - .sub_key() - .unwrap() - .to_string(); + let ibc_token = ibc_token(denom).to_string(); let rpc = get_actor_rpc(test, &Who::Validator(0)); let amount = Amount::native_whole(50000).to_string_native(); @@ -797,9 +792,7 @@ fn transfer_received_token( "--target", ALBERT, "--token", - NAM, - "--sub-prefix", - &sub_prefix, + &ibc_token, "--amount", &amount, "--gas-amount", @@ -834,23 +827,16 @@ fn transfer_back( "{}/{}/{}", port_channel_id_b.port_id, port_channel_id_b.channel_id, xan ); - let hash = calc_hash(denom_raw); - let ibc_token = Address::Internal(InternalAddress::IbcToken(hash)); - // Need the address prefix for ibc-transfer command - let sub_prefix = format!( - "{}/{}{}", - MULTITOKEN_STORAGE_KEY, RESERVED_ADDRESS_PREFIX, ibc_token - ); + let ibc_token = ibc_token(denom_raw).to_string(); // Send a token from Chain B let height = transfer( test_b, BERTHA, &receiver, - NAM, + ibc_token, &Amount::native_whole(50000), BERTHA_KEY, port_channel_id_b, - Some(sub_prefix), None, false, )?; @@ -911,7 +897,6 @@ fn transfer_timeout( &Amount::native_whole(100000), ALBERT_KEY, port_channel_id_a, - None, Some(Duration::new(5, 0)), false, )?; @@ -1054,7 +1039,6 @@ fn transfer( amount: &Amount, signer: impl AsRef, port_channel_id: &PortChannelId, - sub_prefix: Option, timeout_sec: Option, wait_reveal_pk: bool, ) -> Result { @@ -1083,11 +1067,7 @@ fn transfer( "--node", &rpc, ]; - let sp = sub_prefix.clone().unwrap_or_default(); - if sub_prefix.is_some() { - tx_args.push("--sub-prefix"); - tx_args.push(&sp); - } + let timeout = timeout_sec.unwrap_or_default().as_secs().to_string(); if timeout_sec.is_some() { tx_args.push("--timeout-sec-offset"); @@ -1294,7 +1274,7 @@ fn check_balances( // Check the escrowed balance let expected = format!( ": 100000, owned by {}", - Address::Internal(InternalAddress::IbcEscrow) + Address::Internal(InternalAddress::Ibc) ); client.exp_string(&expected)?; // Check the source balance @@ -1307,21 +1287,12 @@ fn check_balances( "{}/{}/{}", &dest_port_channel_id.port_id, &dest_port_channel_id.channel_id, &token, ); - let key_prefix = ibc_token_prefix(denom)?; - let sub_prefix = key_prefix.sub_key().unwrap().to_string(); + let ibc_token = ibc_token(denom).to_string(); let rpc_b = get_actor_rpc(test_b, &Who::Validator(0)); let query_args = vec![ - "balance", - "--owner", - BERTHA, - "--token", - NAM, - "--sub-prefix", - &sub_prefix, - "--node", - &rpc_b, + "balance", "--owner", BERTHA, "--token", &ibc_token, "--node", &rpc_b, ]; - let expected = format!("nam with {}: 100000", sub_prefix); + let expected = format!("{}: 100000", ibc_token); let mut client = run!(test_b, Bin::Client, query_args, Some(40))?; client.exp_string(&expected)?; client.assert_success(); @@ -1339,40 +1310,23 @@ fn check_balances_after_non_ibc( "{}/{}/{}", port_channel_id.port_id, port_channel_id.channel_id, token ); - let key_prefix = ibc_token_prefix(denom)?; - let sub_prefix = key_prefix.sub_key().unwrap().to_string(); + let ibc_token = ibc_token(denom).to_string(); // Check the source let rpc = get_actor_rpc(test, &Who::Validator(0)); let query_args = vec![ - "balance", - "--owner", - BERTHA, - "--token", - NAM, - "--sub-prefix", - &sub_prefix, - "--node", - &rpc, + "balance", "--owner", BERTHA, "--token", &ibc_token, "--node", &rpc, ]; - let expected = format!("nam with {}: 50000", sub_prefix); + let expected = format!("{}: 50000", ibc_token); let mut client = run!(test, Bin::Client, query_args, Some(40))?; client.exp_string(&expected)?; client.assert_success(); // Check the traget let query_args = vec![ - "balance", - "--owner", - ALBERT, - "--token", - NAM, - "--sub-prefix", - &sub_prefix, - "--node", - &rpc, + "balance", "--owner", ALBERT, "--token", &ibc_token, "--node", &rpc, ]; - let expected = format!("nam with {}: 50000", sub_prefix); + let expected = format!("{}: 50000", ibc_token); let mut client = run!(test, Bin::Client, query_args, Some(40))?; client.exp_string(&expected)?; client.assert_success(); @@ -1395,7 +1349,7 @@ fn check_balances_after_back( // Check the escrowed balance let expected = format!( ": 50000, owned by {}", - Address::Internal(InternalAddress::IbcEscrow) + Address::Internal(InternalAddress::Ibc) ); client.exp_string(&expected)?; // Check the source balance @@ -1408,21 +1362,12 @@ fn check_balances_after_back( "{}/{}/{}", &dest_port_channel_id.port_id, &dest_port_channel_id.channel_id, &token, ); - let key_prefix = ibc_token_prefix(denom)?; - let sub_prefix = key_prefix.sub_key().unwrap().to_string(); + let ibc_token = ibc_token(denom).to_string(); let rpc_b = get_actor_rpc(test_b, &Who::Validator(0)); let query_args = vec![ - "balance", - "--owner", - BERTHA, - "--token", - NAM, - "--sub-prefix", - &sub_prefix, - "--node", - &rpc_b, + "balance", "--owner", BERTHA, "--token", &ibc_token, "--node", &rpc_b, ]; - let expected = format!("nam with {}: 0", sub_prefix); + let expected = format!("{}: 0", ibc_token); let mut client = run!(test_b, Bin::Client, query_args, Some(40))?; client.exp_string(&expected)?; client.assert_success(); diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index fe8e25294a..80c83a2a54 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -19,32 +19,62 @@ use std::time::{Duration, Instant}; use borsh::BorshSerialize; use color_eyre::eyre::Result; use data_encoding::HEXLOWER; -use namada::types::address::{btc, eth, masp_rewards, Address}; +use namada::types::address::Address; use namada::types::governance::ProposalType; use namada::types::storage::Epoch; use namada::types::token; -use namada_apps::client::tx::CLIShieldedUtils; use namada_apps::config::ethereum_bridge; use namada_apps::config::genesis::genesis_config::{ GenesisConfig, ParametersConfig, PosParamsConfig, }; use namada_apps::config::utils::convert_tm_addr_to_socket_addr; use namada_apps::facade::tendermint_config::net::Address as TendermintAddress; -use namada_core::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; use namada_test_utils::TestWasms; use serde_json::json; use setup::constants::*; +use setup::Test; use super::helpers::{ - get_height, is_debug_mode, wait_for_block_height, wait_for_wasm_pre_compile, + get_height, wait_for_block_height, wait_for_wasm_pre_compile, }; -use super::setup::{get_all_wasms_hashes, set_ethereum_bridge_mode}; +use super::setup::{get_all_wasms_hashes, set_ethereum_bridge_mode, NamadaCmd}; use crate::e2e::helpers::{ epoch_sleep, find_address, find_bonded_stake, get_actor_rpc, get_epoch, + is_debug_mode, parse_reached_epoch, }; use crate::e2e::setup::{self, default_port_offset, sleep, Bin, Who}; use crate::{run, run_as}; +fn start_namada_ledger_node( + test: &Test, + idx: Option, + timeout_sec: Option, +) -> Result { + let who = match idx { + Some(idx) => Who::Validator(idx), + _ => Who::NonValidator, + }; + let mut node = + run_as!(test, who.clone(), Bin::Node, &["ledger"], timeout_sec)?; + node.exp_string("Namada ledger node started")?; + if let Who::Validator(_) = who { + node.exp_string("This node is a validator")?; + } else { + node.exp_string("This node is not a validator")?; + } + Ok(node) +} + +fn start_namada_ledger_node_wait_wasm( + test: &Test, + idx: Option, + timeout_sec: Option, +) -> Result { + let mut node = start_namada_ledger_node(test, idx, timeout_sec)?; + wait_for_wasm_pre_compile(&mut node)?; + Ok(node) +} + /// Test that when we "run-ledger" with all the possible command /// combinations from fresh state, the node starts-up successfully for both a /// validator and non-validator user. @@ -110,28 +140,14 @@ fn test_node_connectivity_and_consensus() -> Result<()> { ); // 1. Run 2 genesis validator ledger nodes and 1 non-validator node - let args = ["ledger"]; - let mut validator_0 = - run_as!(test, Who::Validator(0), Bin::Node, args, Some(40))?; - validator_0.exp_string("Namada ledger node started")?; - validator_0.exp_string("This node is a validator")?; - let mut validator_1 = - run_as!(test, Who::Validator(1), Bin::Node, args, Some(40))?; - validator_1.exp_string("Namada ledger node started")?; - validator_1.exp_string("This node is a validator")?; - let mut non_validator = - run_as!(test, Who::NonValidator, Bin::Node, args, Some(40))?; - non_validator.exp_string("Namada ledger node started")?; - non_validator.exp_string("This node is not a validator")?; - - wait_for_wasm_pre_compile(&mut validator_0)?; - let bg_validator_0 = validator_0.background(); - - wait_for_wasm_pre_compile(&mut validator_1)?; - let bg_validator_1 = validator_1.background(); - - wait_for_wasm_pre_compile(&mut non_validator)?; - let _bg_non_validator = non_validator.background(); + let bg_validator_0 = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); + let bg_validator_1 = + start_namada_ledger_node_wait_wasm(&test, Some(1), Some(40))? + .background(); + let _bg_non_validator = + start_namada_ledger_node_wait_wasm(&test, None, Some(40))?.background(); // 2. Cross over epoch to check for consensus with multiple nodes let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); @@ -219,9 +235,7 @@ fn test_namada_shuts_down_if_tendermint_dies() -> Result<()> { // 1. Run the ledger node let mut ledger = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - - ledger.exp_string("Namada ledger node started")?; + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))?; // 2. Kill the tendermint node sleep(1); @@ -262,10 +276,8 @@ fn run_ledger_load_state_and_reset() -> Result<()> { ); // 1. Run the ledger node - let mut ledger = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; + let mut ledger = start_namada_ledger_node(&test, Some(0), Some(40))?; - ledger.exp_string("Namada ledger node started")?; // There should be no previous state ledger.exp_string("No state could be found")?; // Wait to commit a block @@ -285,10 +297,7 @@ fn run_ledger_load_state_and_reset() -> Result<()> { drop(ledger); // 3. Run the ledger again, it should load its previous state - let mut ledger = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - - ledger.exp_string("Namada ledger node started")?; + let mut ledger = start_namada_ledger_node(&test, Some(0), Some(40))?; // There should be previous state now ledger.exp_string("Last state root hash:")?; @@ -310,10 +319,7 @@ fn run_ledger_load_state_and_reset() -> Result<()> { session.exp_eof()?; // 6. Run the ledger again, it should start from fresh state - let mut session = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - - session.exp_string("Namada ledger node started")?; + let mut session = start_namada_ledger_node(&test, Some(0), Some(40))?; // There should be no previous state session.exp_string("No state could be found")?; @@ -413,18 +419,15 @@ fn ledger_txs_and_queries() -> Result<()> { ); // 1. Run the ledger node - let mut ledger = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - - wait_for_wasm_pre_compile(&mut ledger)?; - let _bg_ledger = ledger.background(); + let _bg_ledger = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); // for a custom tx let transfer = token::Transfer { source: find_address(&test, BERTHA).unwrap(), target: find_address(&test, ALBERT).unwrap(), token: find_address(&test, NAM).unwrap(), - sub_prefix: None, amount: token::DenominatedAmount { amount: token::Amount::native_whole(10), denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), @@ -625,1279 +628,33 @@ fn ledger_txs_and_queries() -> Result<()> { let mut client = run!(test, Bin::Client, query_args, Some(40))?; client.exp_regex(expected)?; - client.assert_success(); - } - let christel = find_address(&test, CHRISTEL)?; - // as setup in `genesis/e2e-tests-single-node.toml` - let christel_balance = token::Amount::native_whole(1000000); - let nam = find_address(&test, NAM)?; - let storage_key = token::balance_key(&nam, &christel).to_string(); - let query_args_and_expected_response = vec![ - // 8. Query storage key and get hex-encoded raw bytes - ( - vec![ - "query-bytes", - "--storage-key", - &storage_key, - "--node", - &validator_one_rpc, - ], - // expect hex encoded of borsh encoded bytes - HEXLOWER.encode(&christel_balance.try_to_vec().unwrap()), - ), - ]; - for (query_args, expected) in &query_args_and_expected_response { - let mut client = run!(test, Bin::Client, query_args, Some(40))?; - client.exp_string(expected)?; - - client.assert_success(); - } - - Ok(()) -} - -/// In this test we: -/// 1. Run the ledger node -/// 2. Attempt to spend 10 BTC at SK(A) to PA(B) -/// 3. Attempt to spend 15 BTC at SK(A) to Bertha -/// 4. Send 20 BTC from Albert to PA(A) -/// 5. Attempt to spend 10 ETH at SK(A) to PA(B) -/// 6. Spend 7 BTC at SK(A) to PA(B) -/// 7. Spend 7 BTC at SK(A) to PA(B) -/// 8. Attempt to spend 7 BTC at SK(A) to PA(B) -/// 9. Spend 6 BTC at SK(A) to PA(B) -/// 10. Assert BTC balance at VK(A) is 0 -/// 11. Assert ETH balance at VK(A) is 0 -/// 12. Assert balance at VK(B) is 10 BTC -/// 13. Send 10 BTC from SK(B) to Bertha - -#[test] -fn masp_txs_and_queries() -> Result<()> { - // Download the shielded pool parameters before starting node - let _ = CLIShieldedUtils::new(PathBuf::new()); - // Lengthen epoch to ensure that a transaction can be constructed and - // submitted within the same block. Necessary to ensure that conversion is - // not invalidated. - let test = setup::network( - |genesis| { - let parameters = ParametersConfig { - epochs_per_year: epochs_per_year_from_min_duration( - if is_debug_mode() { 1440 } else { 360 }, - ), - min_num_of_blocks: 1, - ..genesis.parameters - }; - GenesisConfig { - parameters, - ..genesis - } - }, - None, - )?; - set_ethereum_bridge_mode( - &test, - &test.net.chain_id, - &Who::Validator(0), - ethereum_bridge::ledger::Mode::Off, - None, - ); - - // 1. Run the ledger node - let mut ledger = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - - wait_for_wasm_pre_compile(&mut ledger)?; - - let _bg_ledger = ledger.background(); - - let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); - - // the extra argument in the tuple is for flagging extra checks below - let txs_args = vec![ - // 2. Attempt to spend 10 BTC at SK(A) to PA(B) - ( - vec![ - "transfer", - "--source", - A_SPENDING_KEY, - "--target", - AB_PAYMENT_ADDRESS, - "--token", - BTC, - "--amount", - "10", - "--signing-keys", - ALBERT_KEY, - "--node", - &validator_one_rpc, - ], - ("No balance found", false), - ), - // 3. Attempt to spend 15 BTC at SK(A) to Bertha - ( - vec![ - "transfer", - "--source", - A_SPENDING_KEY, - "--target", - BERTHA, - "--token", - BTC, - "--amount", - "15", - "--signing-keys", - ALBERT_KEY, - "--node", - &validator_one_rpc, - ], - ("No balance found", false), - ), - // 4. Send 20 BTC from Albert to PA(A) - ( - vec![ - "transfer", - "--source", - ALBERT, - "--target", - AA_PAYMENT_ADDRESS, - "--token", - BTC, - "--amount", - "20", - "--signing-keys", - ALBERT_KEY, - "--node", - &validator_one_rpc, - ], - ("Transaction is valid", false), - ), - // 5. Attempt to spend 10 ETH at SK(A) to PA(B) - ( - vec![ - "transfer", - "--source", - A_SPENDING_KEY, - "--target", - AB_PAYMENT_ADDRESS, - "--token", - ETH, - "--amount", - "10", - "--signing-keys", - ALBERT_KEY, - "--node", - &validator_one_rpc, - ], - ("No balance found", false), - ), - // 10. Assert BTC balance at VK(A) is 0 - ( - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - BTC, - "--node", - &validator_one_rpc, - ], - ("btc: 20", false), - ), - // 6. Spend 7 BTC at SK(A) to PA(B) - ( - vec![ - "transfer", - "--source", - A_SPENDING_KEY, - "--target", - AB_PAYMENT_ADDRESS, - "--token", - BTC, - "--amount", - "7", - "--signing-keys", - ALBERT_KEY, - "--node", - &validator_one_rpc, - ], - ("Transaction is valid", false), - ), - // 7. Spend 7 BTC at SK(A) to PA(B) - ( - vec![ - "transfer", - "--source", - A_SPENDING_KEY, - "--target", - BB_PAYMENT_ADDRESS, - "--token", - BTC, - "--amount", - "7", - "--node", - &validator_one_rpc, - ], - ("Transaction is valid", false), - ), - // 8. Attempt to spend 7 BTC at SK(A) to PA(B) - ( - vec![ - "transfer", - "--source", - A_SPENDING_KEY, - "--target", - BB_PAYMENT_ADDRESS, - "--token", - BTC, - "--amount", - "7", - "--node", - &validator_one_rpc, - ], - ("is lower than the amount to be transferred and fees", false), - ), - // 9. Spend 6 BTC at SK(A) to PA(B) - ( - vec![ - "transfer", - "--source", - A_SPENDING_KEY, - "--target", - BB_PAYMENT_ADDRESS, - "--token", - BTC, - "--amount", - "6", - "--node", - &validator_one_rpc, - ], - ("Transaction is valid", false), - ), - // 10. Assert BTC balance at VK(A) is 0 - ( - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - BTC, - "--node", - &validator_one_rpc, - ], - ("No shielded btc balance found", false), - ), - // 11. Assert ETH balance at VK(A) is 0 - ( - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - ETH, - "--node", - &validator_one_rpc, - ], - ("No shielded eth balance found", false), - ), - // 12. Assert balance at VK(B) is 10 BTC - ( - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--node", - &validator_one_rpc, - ], - ("btc : 20", false), - ), - // 13. Send 10 BTC from SK(B) to Bertha - ( - vec![ - "transfer", - "--source", - B_SPENDING_KEY, - "--target", - BERTHA, - "--token", - BTC, - "--amount", - "20", - "--node", - &validator_one_rpc, - ], - ("Transaction is valid", false), - ), - ]; - - // Wait till epoch boundary - let _ep0 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - for (tx_args, (tx_result, wait_reveal_pk)) in &txs_args { - for &dry_run in &[true, false] { - let tx_args = if dry_run && tx_args[0] == "transfer" { - vec![tx_args.clone(), vec!["--dry-run"]].concat() - } else { - tx_args.clone() - }; - let mut client = run!(test, Bin::Client, tx_args, Some(720))?; - - if *tx_result == "Transaction is valid" && !dry_run { - client.exp_string("Transaction accepted")?; - client.exp_string("Transaction applied")?; - if *wait_reveal_pk { - client.exp_string("Transaction applied")?; - } - } - client.exp_string(tx_result)?; - } - } - - Ok(()) -} - -/// In this test we: -/// 1. Run the ledger node -/// 2. Assert PPA(C) cannot be recognized by incorrect viewing key -/// 3. Assert PPA(C) has not transaction pinned to it -/// 4. Send 20 BTC from Albert to PPA(C) -/// 5. Assert PPA(C) has the 20 BTC transaction pinned to it - -#[test] -fn masp_pinned_txs() -> Result<()> { - // Download the shielded pool parameters before starting node - let _ = CLIShieldedUtils::new(PathBuf::new()); - // Lengthen epoch to ensure that a transaction can be constructed and - // submitted within the same block. Necessary to ensure that conversion is - // not invalidated. - let test = setup::network( - |genesis| { - let parameters = ParametersConfig { - epochs_per_year: epochs_per_year_from_min_duration(120), - ..genesis.parameters - }; - GenesisConfig { - parameters, - ..genesis - } - }, - None, - )?; - set_ethereum_bridge_mode( - &test, - &test.net.chain_id, - &Who::Validator(0), - ethereum_bridge::ledger::Mode::Off, - None, - ); - - // 1. Run the ledger node - let mut ledger = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - - wait_for_wasm_pre_compile(&mut ledger)?; - - let _bg_ledger = ledger.background(); - - let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); - - // Wait till epoch boundary - let _ep0 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Assert PPA(C) cannot be recognized by incorrect viewing key - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AC_PAYMENT_ADDRESS, - "--token", - BTC, - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.send_line(AB_VIEWING_KEY)?; - client.exp_string("Supplied viewing key cannot decode transactions to")?; - client.assert_success(); - - // Assert PPA(C) has no transaction pinned to it - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AC_PAYMENT_ADDRESS, - "--token", - BTC, - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.send_line(AC_VIEWING_KEY)?; - client.exp_string("has not yet been consumed")?; - client.assert_success(); - - // Wait till epoch boundary - let _ep1 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Send 20 BTC from Albert to PPA(C) - let mut client = run!( - test, - Bin::Client, - vec![ - "transfer", - "--source", - ALBERT, - "--target", - AC_PAYMENT_ADDRESS, - "--token", - BTC, - "--amount", - "20", - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("Transaction accepted")?; - client.exp_string("Transaction applied")?; - client.exp_string("Transaction is valid")?; - client.assert_success(); - - // Wait till epoch boundary - // This makes it more consistent for some reason? - let _ep2 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Assert PPA(C) has the 20 BTC transaction pinned to it - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AC_PAYMENT_ADDRESS, - "--token", - BTC, - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.send_line(AC_VIEWING_KEY)?; - client.exp_string("Received 20 btc")?; - client.assert_success(); - - // Assert PPA(C) has no NAM pinned to it - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AC_PAYMENT_ADDRESS, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.send_line(AC_VIEWING_KEY)?; - client.exp_string("Received no shielded nam")?; - client.assert_success(); - - // Wait till epoch boundary - let _ep1 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Assert PPA(C) does not NAM pinned to it on epoch boundary - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AC_PAYMENT_ADDRESS, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.send_line(AC_VIEWING_KEY)?; - client.exp_string("Received no shielded nam")?; - client.assert_success(); - - Ok(()) -} - -/// In this test we verify that users of the MASP receive the correct rewards -/// for leaving their assets in the pool for varying periods of time. - -#[test] -fn masp_incentives() -> Result<()> { - // The number of decimal places used by BTC amounts. - const BTC_DENOMINATION: u8 = 8; - // The number of decimal places used by ETH amounts. - const ETH_DENOMINATION: u8 = 18; - // Download the shielded pool parameters before starting node - let _ = CLIShieldedUtils::new(PathBuf::new()); - // Lengthen epoch to ensure that a transaction can be constructed and - // submitted within the same block. Necessary to ensure that conversion is - // not invalidated. - let test = setup::network( - |genesis| { - let parameters = ParametersConfig { - epochs_per_year: epochs_per_year_from_min_duration( - if is_debug_mode() { 340 } else { 85 }, - ), - min_num_of_blocks: 1, - ..genesis.parameters - }; - GenesisConfig { - parameters, - ..genesis - } - }, - None, - )?; - set_ethereum_bridge_mode( - &test, - &test.net.chain_id, - &Who::Validator(0), - ethereum_bridge::ledger::Mode::Off, - None, - ); - - // 1. Run the ledger node - let mut ledger = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - - wait_for_wasm_pre_compile(&mut ledger)?; - - let _bg_ledger = ledger.background(); - - let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); - - // Wait till epoch boundary - let ep0 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Send 20 BTC from Albert to PA(A) - let mut client = run!( - test, - Bin::Client, - vec![ - "transfer", - "--source", - ALBERT, - "--target", - AA_PAYMENT_ADDRESS, - "--token", - BTC, - "--amount", - "20", - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("Transaction accepted")?; - client.exp_string("Transaction applied")?; - client.exp_string("Transaction is valid")?; - client.assert_success(); - - // Assert BTC balance at VK(A) is 20 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - BTC, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("btc: 20")?; - client.assert_success(); - - // Assert NAM balance at VK(A) is 0 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("No shielded nam balance found")?; - client.assert_success(); - - let masp_rewards = masp_rewards(); - - // Wait till epoch boundary - let ep1 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Assert BTC balance at VK(A) is 20 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - BTC, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("btc: 20")?; - client.assert_success(); - - let amt20 = token::Amount::from_uint(20, BTC_DENOMINATION).unwrap(); - let amt10 = token::Amount::from_uint(10, ETH_DENOMINATION).unwrap(); - - // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_1-epoch_0) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep1.0 - ep0.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); - - // Assert NAM balance at MASP pool is 20*BTC_reward*(epoch_1-epoch_0) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - MASP, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep1.0 - ep0.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); - - // Wait till epoch boundary - let ep2 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Assert BTC balance at VK(A) is 20 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - BTC, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("btc: 20")?; - client.assert_success(); - - // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_2-epoch_0) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep2.0 - ep0.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); - - // Assert NAM balance at MASP pool is 20*BTC_reward*(epoch_2-epoch_0) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - MASP, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep2.0 - ep0.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); - - // Wait till epoch boundary - let ep3 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Send 10 ETH from Albert to PA(B) - let mut client = run!( - test, - Bin::Client, - vec![ - "transfer", - "--source", - ALBERT, - "--target", - AB_PAYMENT_ADDRESS, - "--token", - ETH, - "--amount", - "10", - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("Transaction accepted")?; - client.exp_string("Transaction applied")?; - client.exp_string("Transaction is valid")?; - client.assert_success(); - - // Assert ETH balance at VK(B) is 10 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - ETH, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("eth: 10")?; - client.assert_success(); - - // Assert NAM balance at VK(B) is 0 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("No shielded nam balance found")?; - client.assert_success(); - - // Wait till epoch boundary - let ep4 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Assert ETH balance at VK(B) is 10 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - ETH, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("eth: 10")?; - client.assert_success(); - - // Assert NAM balance at VK(B) is 10*ETH_reward*(epoch_4-epoch_3) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = (amt10 * masp_rewards[&(eth(), None)]).0 * (ep4.0 - ep3.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); - - // Assert NAM balance at MASP pool is - // 20*BTC_reward*(epoch_4-epoch_0)+10*ETH_reward*(epoch_4-epoch_3) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - MASP, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = ((amt20 * masp_rewards[&(btc(), None)]).0 * (ep4.0 - ep0.0)) - + ((amt10 * masp_rewards[&(eth(), None)]).0 * (ep4.0 - ep3.0)); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated))?; - client.assert_success(); - - // Wait till epoch boundary - let ep5 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Send 10 ETH from SK(B) to Christel - let mut client = run!( - test, - Bin::Client, - vec![ - "transfer", - "--source", - B_SPENDING_KEY, - "--target", - CHRISTEL, - "--token", - ETH, - "--amount", - "10", - "--signing-keys", - BERTHA_KEY, - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("Transaction accepted")?; - client.exp_string("Transaction applied")?; - client.exp_string("Transaction is valid")?; - client.assert_success(); - - // Assert ETH balance at VK(B) is 0 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - ETH, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("No shielded eth balance found")?; - client.assert_success(); - - // let mut ep = get_epoch(&test, &validator_one_rpc)?; - - // Assert NAM balance at VK(B) is 10*ETH_reward*(ep-epoch_3) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = (amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); - - // ep = get_epoch(&test, &validator_one_rpc)?; - // Assert NAM balance at MASP pool is - // 20*BTC_reward*(epoch_5-epoch_0)+10*ETH_reward*(epoch_5-epoch_3) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - MASP, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = ((amt20 * masp_rewards[&(btc(), None)]).0 * (ep5.0 - ep0.0)) - + ((amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0)); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated))?; - client.assert_success(); - - // Wait till epoch boundary - let ep6 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Send 20 BTC from SK(A) to Christel - let mut client = run!( - test, - Bin::Client, - vec![ - "transfer", - "--source", - A_SPENDING_KEY, - "--target", - CHRISTEL, - "--token", - BTC, - "--amount", - "20", - "--signing-keys", - ALBERT_KEY, - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("Transaction accepted")?; - client.exp_string("Transaction applied")?; - client.exp_string("Transaction is valid")?; - client.assert_success(); - - // Assert BTC balance at VK(A) is 0 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - BTC, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("No shielded btc balance found")?; - client.assert_success(); - - // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_6-epoch_0) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); - - // Assert NAM balance at MASP pool is - // 20*BTC_reward*(epoch_6-epoch_0)+20*ETH_reward*(epoch_5-epoch_3) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - MASP, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = ((amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0)) - + ((amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0)); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); - - // Wait till epoch boundary - let _ep7 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_6-epoch_0) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated))?; - client.assert_success(); - - // Assert NAM balance at VK(B) is 10*ETH_reward*(epoch_5-epoch_3) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = (amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); - - // Assert NAM balance at MASP pool is - // 20*BTC_reward*(epoch_6-epoch_0)+10*ETH_reward*(epoch_5-epoch_3) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - MASP, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = ((amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0)) - + ((amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0)); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); - - // Wait till epoch boundary to prevent conversion expiry during transaction - // construction - let _ep8 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Send 10*ETH_reward*(epoch_5-epoch_3) NAM from SK(B) to Christel - let mut client = run!( - test, - Bin::Client, - vec![ - "transfer", - "--source", - B_SPENDING_KEY, - "--target", - CHRISTEL, - "--token", - NAM, - "--amount", - &((amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0)) - .to_string_native(), - "--signing-keys", - BERTHA_KEY, - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("Transaction accepted")?; - client.exp_string("Transaction applied")?; - client.exp_string("Transaction is valid")?; - client.assert_success(); - - // Wait till epoch boundary - let _ep9 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Send 20*BTC_reward*(epoch_6-epoch_0) NAM from SK(A) to Bertha - let mut client = run!( - test, - Bin::Client, - vec![ - "transfer", - "--source", - A_SPENDING_KEY, - "--target", - BERTHA, - "--token", - NAM, - "--amount", - &((amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0)) - .to_string_native(), - "--signing-keys", - ALBERT_KEY, - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("Transaction accepted")?; - client.exp_string("Transaction applied")?; - client.exp_string("Transaction is valid")?; - client.assert_success(); - - // Assert NAM balance at VK(A) is 0 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("No shielded nam balance found")?; - client.assert_success(); - - // Assert NAM balance at VK(B) is 0 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("No shielded nam balance found")?; - client.assert_success(); + client.assert_success(); + } + let christel = find_address(&test, CHRISTEL)?; + // as setup in `genesis/e2e-tests-single-node.toml` + let christel_balance = token::Amount::native_whole(1000000); + let nam = find_address(&test, NAM)?; + let storage_key = token::balance_key(&nam, &christel).to_string(); + let query_args_and_expected_response = vec![ + // 8. Query storage key and get hex-encoded raw bytes + ( + vec![ + "query-bytes", + "--storage-key", + &storage_key, + "--node", + &validator_one_rpc, + ], + // expect hex encoded of borsh encoded bytes + HEXLOWER.encode(&christel_balance.try_to_vec().unwrap()), + ), + ]; + for (query_args, expected) in &query_args_and_expected_response { + let mut client = run!(test, Bin::Client, query_args, Some(40))?; + client.exp_string(expected)?; - // Assert NAM balance at MASP pool is 0 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - MASP, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("nam: 0")?; - client.assert_success(); + client.assert_success(); + } Ok(()) } @@ -1921,12 +678,9 @@ fn invalid_transactions() -> Result<()> { ); // 1. Run the ledger node - let mut ledger = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - - wait_for_wasm_pre_compile(&mut ledger)?; - - let bg_ledger = ledger.background(); + let bg_ledger = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); // 2. Submit a an invalid transaction (trying to transfer tokens should fail // in the user's VP due to the wrong signer) @@ -1977,10 +731,7 @@ fn invalid_transactions() -> Result<()> { drop(ledger); // 4. Restart the ledger - let mut ledger = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - - ledger.exp_string("Namada ledger node started")?; + let mut ledger = start_namada_ledger_node(&test, Some(0), Some(40))?; // There should be previous state now ledger.exp_string("Last state root hash:")?; @@ -2075,11 +826,9 @@ fn pos_bonds() -> Result<()> { ); // 1. Run the ledger node - let mut ledger = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - - wait_for_wasm_pre_compile(&mut ledger)?; - let _bg_ledger = ledger.background(); + let _bg_ledger = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); @@ -2200,7 +949,7 @@ fn pos_bonds() -> Result<()> { delegation_withdrawable_epoch ); } - let epoch = get_epoch(&test, &validator_one_rpc)?; + let epoch = epoch_sleep(&test, &validator_one_rpc, 40)?; if epoch >= delegation_withdrawable_epoch { break; } @@ -2290,27 +1039,15 @@ fn pos_rewards() -> Result<()> { } // 1. Run 3 genesis validator ledger nodes - let mut validator_0 = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - validator_0.exp_string("Namada ledger node started")?; - validator_0.exp_string("This node is a validator")?; - - let mut validator_1 = - run_as!(test, Who::Validator(1), Bin::Node, &["ledger"], Some(40))?; - validator_1.exp_string("Namada ledger node started")?; - validator_1.exp_string("This node is a validator")?; - - let mut validator_2 = - run_as!(test, Who::Validator(2), Bin::Node, &["ledger"], Some(40))?; - validator_2.exp_string("Namada ledger node started")?; - validator_2.exp_string("This node is a validator")?; - - wait_for_wasm_pre_compile(&mut validator_0)?; - let bg_validator_0 = validator_0.background(); - wait_for_wasm_pre_compile(&mut validator_1)?; - let bg_validator_1 = validator_1.background(); - wait_for_wasm_pre_compile(&mut validator_2)?; - let bg_validator_2 = validator_2.background(); + let bg_validator_0 = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); + let bg_validator_1 = + start_namada_ledger_node_wait_wasm(&test, Some(1), Some(40))? + .background(); + let bg_validator_2 = + start_namada_ledger_node_wait_wasm(&test, Some(2), Some(40))? + .background(); let validator_zero_rpc = get_actor_rpc(&test, &Who::Validator(0)); let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(1)); @@ -2417,7 +1154,7 @@ fn pos_rewards() -> Result<()> { if Instant::now().duration_since(start) > loop_timeout { panic!("Timed out waiting for epoch: {}", wait_epoch); } - let epoch = get_epoch(&test, &validator_zero_rpc)?; + let epoch = epoch_sleep(&test, &validator_zero_rpc, 40)?; if dbg!(epoch) >= wait_epoch { break; } @@ -2461,11 +1198,9 @@ fn test_bond_queries() -> Result<()> { )?; // 1. Run the ledger node - let mut ledger = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - - wait_for_wasm_pre_compile(&mut ledger)?; - let _bg_ledger = ledger.background(); + let _bg_ledger = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); let validator_alias = "validator-0"; @@ -2502,7 +1237,7 @@ fn test_bond_queries() -> Result<()> { if Instant::now().duration_since(start) > loop_timeout { panic!("Timed out waiting for epoch: {}", 1); } - let epoch = get_epoch(&test, &validator_one_rpc)?; + let epoch = epoch_sleep(&test, &validator_one_rpc, 40)?; if epoch >= Epoch(4) { break; } @@ -2628,17 +1363,10 @@ fn pos_init_validator() -> Result<()> { )?; // 1. Run a validator and non-validator ledger node - let args = ["ledger"]; let mut validator_0 = - run_as!(test, Who::Validator(0), Bin::Node, args, Some(60))?; + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(60))?; let mut non_validator = - run_as!(test, Who::NonValidator, Bin::Node, args, Some(60))?; - - wait_for_wasm_pre_compile(&mut validator_0)?; - // let _bg_ledger = validator_0.background(); - - wait_for_wasm_pre_compile(&mut non_validator)?; - // let _bg_ledger = non_validator.background(); + start_namada_ledger_node_wait_wasm(&test, None, Some(60))?; // Wait for a first block validator_0.exp_string("Committed block hash")?; @@ -2779,6 +1507,7 @@ fn pos_init_validator() -> Result<()> { // Stop the non-validator node and run it as the new validator let mut non_validator = bg_non_validator.foreground(); non_validator.interrupt()?; + non_validator.exp_eof()?; // it takes a bit before the node is shutdown. We dont want flasky test. if is_debug_mode() { @@ -2791,7 +1520,7 @@ fn pos_init_validator() -> Result<()> { let validator_1_base_dir = test.get_base_dir(&Who::NonValidator); let mut validator_1 = setup::run_cmd( Bin::Node, - args, + ["ledger"], Some(60), &test.working_dir, validator_1_base_dir, @@ -2817,7 +1546,7 @@ fn pos_init_validator() -> Result<()> { if Instant::now().duration_since(start) > loop_timeout { panic!("Timed out waiting for epoch: {}", earliest_update_epoch); } - let epoch = get_epoch(&test, &non_validator_rpc)?; + let epoch = epoch_sleep(&test, &non_validator_rpc, 40)?; if epoch >= earliest_update_epoch { break; } @@ -2855,11 +1584,9 @@ fn ledger_many_txs_in_a_block() -> Result<()> { ); // 1. Run the ledger node - let mut ledger = - run_as!(*test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - - wait_for_wasm_pre_compile(&mut ledger)?; - let bg_ledger = ledger.background(); + let bg_ledger = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); let validator_one_rpc = Arc::new(get_actor_rpc(&test, &Who::Validator(0))); @@ -2969,13 +1696,11 @@ fn proposal_submission() -> Result<()> { client.assert_success(); // 1. Run the ledger node - let mut ledger = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - - wait_for_wasm_pre_compile(&mut ledger)?; - let _bg_ledger = ledger.background(); + let bg_ledger = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); - let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); + let validator_0_rpc = get_actor_rpc(&test, &Who::Validator(0)); // 1.1 Delegate some token let tx_args = vec![ @@ -2993,7 +1718,7 @@ fn proposal_submission() -> Result<()> { "--gas-token", NAM, "--node", - &validator_one_rpc, + &validator_0_rpc, ]; let mut client = run!(test, Bin::Client, tx_args, Some(40))?; client.exp_string("Transaction is valid.")?; @@ -3024,6 +1749,11 @@ fn proposal_submission() -> Result<()> { client.exp_string("Transaction is valid.")?; client.assert_success(); + // Wait for the proposal to be committed + let mut ledger = bg_ledger.foreground(); + ledger.exp_string("Committed block hash")?; + let _bg_ledger = ledger.background(); + // 3. Query the proposal let proposal_query_args = vec![ "query-proposal", @@ -3322,12 +2052,9 @@ fn eth_governance_proposal() -> Result<()> { client.assert_success(); // Run the ledger node - let mut ledger = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - - ledger.exp_string("Namada ledger node started")?; - wait_for_wasm_pre_compile(&mut ledger)?; - let _bg_ledger = ledger.background(); + let _bg_ledger = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); @@ -3556,12 +2283,9 @@ fn pgf_governance_proposal() -> Result<()> { client.assert_success(); // Run the ledger node - let mut ledger = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - - ledger.exp_string("Namada ledger node started")?; - wait_for_wasm_pre_compile(&mut ledger)?; - let _bg_ledger = ledger.background(); + let _bg_ledger = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); @@ -3876,11 +2600,9 @@ fn proposal_offline() -> Result<()> { ); // 1. Run the ledger node - let mut ledger = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(20))?; - - wait_for_wasm_pre_compile(&mut ledger)?; - let _bg_ledger = ledger.background(); + let _bg_ledger = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); @@ -4333,30 +3055,12 @@ fn test_genesis_validators() -> Result<()> { test.genesis.validator.keys(), ); - let args = ["ledger"]; let mut validator_0 = - run_as!(test, Who::Validator(0), Bin::Node, args, Some(40))?; - validator_0.exp_string("Namada ledger node started")?; - validator_0.exp_string("This node is a validator")?; - + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))?; let mut validator_1 = - run_as!(test, Who::Validator(1), Bin::Node, args, Some(40))?; - validator_1.exp_string("Namada ledger node started")?; - validator_1.exp_string("This node is a validator")?; - + start_namada_ledger_node_wait_wasm(&test, Some(1), Some(40))?; let mut non_validator = - run_as!(test, Who::NonValidator, Bin::Node, args, Some(40))?; - non_validator.exp_string("Namada ledger node started")?; - non_validator.exp_string("This node is not a validator")?; - - // Wait for a first block - validator_0.exp_string("Committed block hash")?; - validator_1.exp_string("Committed block hash")?; - non_validator.exp_string("Committed block hash")?; - - wait_for_wasm_pre_compile(&mut validator_0)?; - wait_for_wasm_pre_compile(&mut validator_1)?; - wait_for_wasm_pre_compile(&mut non_validator)?; + start_namada_ledger_node_wait_wasm(&test, None, Some(40))?; // Wait for a first block validator_0.exp_string("Committed block hash")?; @@ -4448,6 +3152,7 @@ fn test_genesis_validators() -> Result<()> { /// 4. Run it to get it to double vote and sign blocks /// 5. Submit a valid token transfer tx to validator 0 /// 6. Wait for double signing evidence +/// 7. Make sure the the first validator can proceed to the next epoch #[test] fn double_signing_gets_slashed() -> Result<()> { use std::net::SocketAddr; @@ -4457,9 +3162,24 @@ fn double_signing_gets_slashed() -> Result<()> { use namada_apps::client; use namada_apps::config::Config; + let mut pipeline_len = 0; + let mut unbonding_len = 0; + let mut cubic_offset = 0; + // Setup 2 genesis validator nodes let test = setup::network( - |genesis| setup::set_validators(2, genesis, default_port_offset), + |genesis| { + (pipeline_len, unbonding_len, cubic_offset) = ( + genesis.pos_params.pipeline_len, + genesis.pos_params.unbonding_len, + genesis.pos_params.cubic_slashing_window_length, + ); + let mut genesis = + setup::set_validators(4, genesis, default_port_offset); + // Make faster epochs to be more likely to discover boundary issues + genesis.parameters.min_num_of_blocks = 2; + genesis + }, None, )?; @@ -4477,24 +3197,37 @@ fn double_signing_gets_slashed() -> Result<()> { ethereum_bridge::ledger::Mode::Off, None, ); + println!("pipeline_len: {}", pipeline_len); // 1. Run 2 genesis validator ledger nodes - let args = ["ledger"]; - let mut validator_0 = - run_as!(test, Who::Validator(0), Bin::Node, args, Some(40))?; - validator_0.exp_string("Namada ledger node started")?; - validator_0.exp_string("This node is a validator")?; - let _bg_validator_0 = validator_0.background(); - let mut validator_1 = - run_as!(test, Who::Validator(1), Bin::Node, args, Some(40))?; - validator_1.exp_string("Namada ledger node started")?; - validator_1.exp_string("This node is a validator")?; - let bg_validator_1 = validator_1.background(); + let _bg_validator_0 = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); + let bg_validator_1 = + start_namada_ledger_node_wait_wasm(&test, Some(1), Some(40))? + .background(); + + let mut validator_2 = + run_as!(test, Who::Validator(2), Bin::Node, &["ledger"], Some(40))?; + validator_2.exp_string("Namada ledger node started")?; + validator_2.exp_string("This node is a validator")?; + let _bg_validator_2 = validator_2.background(); + + let mut validator_3 = + run_as!(test, Who::Validator(3), Bin::Node, &["ledger"], Some(40))?; + validator_3.exp_string("Namada ledger node started")?; + validator_3.exp_string("This node is a validator")?; + let _bg_validator_3 = validator_3.background(); // 2. Copy the first genesis validator base-dir let validator_0_base_dir = test.get_base_dir(&Who::Validator(0)); - let validator_0_base_dir_copy = - test.test_dir.path().join("validator-0-copy"); + let validator_0_base_dir_copy = test + .test_dir + .path() + .join(test.net.chain_id.as_str()) + .join(client::utils::NET_ACCOUNTS_DIR) + .join("validator-0-copy") + .join(namada_apps::config::DEFAULT_BASE_DIR); fs_extra::dir::copy( validator_0_base_dir, &validator_0_base_dir_copy, @@ -4512,7 +3245,7 @@ fn double_signing_gets_slashed() -> Result<()> { let net_address_port_0 = net_address_0.port(); let update_config = |ix: u8, mut config: Config| { - let first_port = net_address_port_0 + 6 * (ix as u16 + 1); + let first_port = net_address_port_0 + 26 * (ix as u16 + 1); let p2p_addr = convert_tm_addr_to_socket_addr(&config.ledger.cometbft.p2p.laddr) .ip() @@ -4568,7 +3301,7 @@ fn double_signing_gets_slashed() -> Result<()> { // `validator_0` and `validator_0_copy` should start double signing let mut validator_0_copy = setup::run_cmd( Bin::Node, - args, + ["ledger"], Some(40), &test.working_dir, validator_0_base_dir_copy, @@ -4576,7 +3309,6 @@ fn double_signing_gets_slashed() -> Result<()> { )?; validator_0_copy.exp_string("Namada ledger node started")?; validator_0_copy.exp_string("This node is a validator")?; - wait_for_wasm_pre_compile(&mut validator_0_copy)?; let _bg_validator_0_copy = validator_0_copy.background(); // 5. Submit a valid token transfer tx to validator 0 @@ -4608,7 +3340,128 @@ fn double_signing_gets_slashed() -> Result<()> { // 6. Wait for double signing evidence let mut validator_1 = bg_validator_1.foreground(); validator_1.exp_string("Processing evidence")?; - validator_1.exp_string("Slashing")?; + + println!("\nPARSING SLASH MESSAGE\n"); + let (_, res) = validator_1 + .exp_regex(r"Slashing [a-z0-9]+ for Duplicate vote in epoch [0-9]+") + .unwrap(); + println!("\n{res}\n"); + let bg_validator_1 = validator_1.background(); + + let exp_processing_epoch = Epoch::from_str(res.split(' ').last().unwrap()) + .unwrap() + + unbonding_len + + cubic_offset + + 1u64; + + // Query slashes + // let tx_args = ["slashes", "--node", &validator_one_rpc]; + // let client = run!(test, Bin::Client, tx_args, Some(40))?; + + let mut client = run!( + test, + Bin::Client, + &["slashes", "--node", &validator_one_rpc], + Some(40) + )?; + client.exp_string("No processed slashes found")?; + client.exp_string("Enqueued slashes for future processing")?; + let (_, res) = client + .exp_regex(r"To be processed in epoch [0-9]+") + .unwrap(); + let processing_epoch = + Epoch::from_str(res.split(' ').last().unwrap()).unwrap(); + + assert_eq!(processing_epoch, exp_processing_epoch); + + println!("\n{processing_epoch}\n"); + + // 6. Wait for processing epoch + loop { + let epoch = epoch_sleep(&test, &validator_one_rpc, 240)?; + println!("\nCurrent epoch: {}", epoch); + if epoch > processing_epoch { + break; + } + } + + let mut client = run!( + test, + Bin::Client, + &[ + "validator-state", + "--validator", + "validator-0", + "--node", + &validator_one_rpc + ], + Some(40) + )?; + let _ = client.exp_regex(r"Validator [a-z0-9]+ is jailed").unwrap(); + + let mut client = run!( + test, + Bin::Client, + &["slashes", "--node", &validator_one_rpc], + Some(40) + )?; + client.exp_string("Processed slashes:")?; + client.exp_string("No enqueued slashes found")?; + + let tx_args = vec![ + "unjail-validator", + "--validator", + "validator-0", + "--gas-amount", + "0", + "--gas-limit", + "0", + "--gas-token", + NAM, + "--node", + &validator_one_rpc, + ]; + let mut client = + run_as!(test, Who::Validator(0), Bin::Client, tx_args, Some(40))?; + client.exp_string("Transaction applied with result:")?; + client.exp_string("Transaction is valid.")?; + client.assert_success(); + + // Wait until pipeline epoch to see if the validator is back in consensus + let cur_epoch = epoch_sleep(&test, &validator_one_rpc, 240)?; + loop { + let epoch = epoch_sleep(&test, &validator_one_rpc, 240)?; + println!("\nCurrent epoch: {}", epoch); + if epoch > cur_epoch + pipeline_len + 1u64 { + break; + } + } + let mut client = run!( + test, + Bin::Client, + &[ + "validator-state", + "--validator", + "validator-0", + "--node", + &validator_one_rpc + ], + Some(40) + )?; + let _ = client + .exp_regex(r"Validator [a-z0-9]+ is in the .* set") + .unwrap(); + + // 7. Make sure the the first validator can proceed to the next epoch + epoch_sleep(&test, &validator_one_rpc, 120)?; + + // Make sure there are no errors + let mut validator_1 = bg_validator_1.foreground(); + validator_1.interrupt()?; + // Wait for the node to stop running to finish writing the state and tx + // queue + validator_1.exp_string("Namada ledger node has shut down.")?; + validator_1.assert_success(); Ok(()) } @@ -4627,15 +3480,12 @@ fn implicit_account_reveal_pk() -> Result<()> { let test = setup::network(|genesis| genesis, None)?; // 1. Run the ledger node - let mut ledger = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - - wait_for_wasm_pre_compile(&mut ledger)?; - let _bg_ledger = ledger.background(); - - let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); + let _bg_ledger = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); // 2. Some transactions that need signature authorization: + let validator_0_rpc = get_actor_rpc(&test, &Who::Validator(0)); let txs_args: Vec Vec>> = vec![ // A token transfer tx Box::new(|source| { @@ -4652,7 +3502,7 @@ fn implicit_account_reveal_pk() -> Result<()> { "--signing-keys", source, "--node", - &validator_one_rpc, + &validator_0_rpc, ] .into_iter() .map(|x| x.to_owned()) @@ -4671,7 +3521,7 @@ fn implicit_account_reveal_pk() -> Result<()> { "--signing-keys", source, "--node", - &validator_one_rpc, + &validator_0_rpc, ] .into_iter() .map(|x| x.to_owned()) @@ -4693,7 +3543,7 @@ fn implicit_account_reveal_pk() -> Result<()> { "--signing-keys", source, "--node", - &validator_one_rpc, + &validator_0_rpc, ] .into_iter() .map(|x| x.to_owned()) @@ -4730,7 +3580,7 @@ fn implicit_account_reveal_pk() -> Result<()> { "--signing-keys", BERTHA_KEY, "--node", - &validator_one_rpc, + &validator_0_rpc, ]; let mut client = run!(test, Bin::Client, credit_args, Some(40))?; client.assert_success(); @@ -4751,6 +3601,52 @@ fn implicit_account_reveal_pk() -> Result<()> { Ok(()) } +#[test] +fn test_epoch_sleep() -> Result<()> { + // Use slightly longer epochs to give us time to sleep + let test = setup::network( + |genesis| { + let parameters = ParametersConfig { + epochs_per_year: epochs_per_year_from_min_duration(30), + min_num_of_blocks: 1, + ..genesis.parameters + }; + GenesisConfig { + parameters, + ..genesis + } + }, + None, + )?; + + // 1. Run the ledger node + let mut ledger = + run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; + wait_for_wasm_pre_compile(&mut ledger)?; + + let _bg_ledger = ledger.background(); + + let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); + + // 2. Query the current epoch + let start_epoch = get_epoch(&test, &validator_one_rpc).unwrap(); + + // 3. Use epoch-sleep to sleep for an epoch + let args = ["utils", "epoch-sleep", "--node", &validator_one_rpc]; + let mut client = run!(test, Bin::Client, &args, None)?; + let reached_epoch = parse_reached_epoch(&mut client)?; + client.assert_success(); + + // 4. Confirm the current epoch is larger + // possibly badly, we assume we get here within 30 seconds of the last step + // should be fine haha (future debuggers: sorry) + let current_epoch = get_epoch(&test, &validator_one_rpc).unwrap(); + assert!(current_epoch > start_epoch); + assert_eq!(current_epoch, reached_epoch); + + Ok(()) +} + /// Prepare proposal data in the test's temp dir from the given source address. /// This can be submitted with "init-proposal" command. fn prepare_proposal_data( diff --git a/tests/src/e2e/setup.rs b/tests/src/e2e/setup.rs index 4c39c8e6ef..eecc60588f 100644 --- a/tests/src/e2e/setup.rs +++ b/tests/src/e2e/setup.rs @@ -21,7 +21,6 @@ use eyre::{eyre, Context}; use itertools::{Either, Itertools}; use namada::types::chain::ChainId; use namada_apps::client::utils; -use namada_apps::client::utils::REDUCED_CLI_PRINTING; use namada_apps::config::genesis::genesis_config::{self, GenesisConfig}; use namada_apps::config::{ethereum_bridge, Config}; use namada_apps::{config, wallet}; @@ -42,7 +41,7 @@ pub const APPS_PACKAGE: &str = "namada_apps"; pub const ENV_VAR_DEBUG: &str = "NAMADA_E2E_DEBUG"; /// Env. var for keeping temporary files created by the E2E tests -const ENV_VAR_KEEP_TEMP: &str = "NAMADA_E2E_KEEP_TEMP"; +pub const ENV_VAR_KEEP_TEMP: &str = "NAMADA_E2E_KEEP_TEMP"; /// Env. var for temporary path const ENV_VAR_TEMP_PATH: &str = "NAMADA_E2E_TEMP_PATH"; @@ -155,7 +154,6 @@ pub fn network( eprintln!("Failed setting up colorful error reports {}", err); } }); - env::set_var(REDUCED_CLI_PRINTING, "true"); let working_dir = working_dir(); let test_dir = TestDir::new(); @@ -401,6 +399,7 @@ mod macros { } } +#[derive(Clone)] pub enum Who { // A non-validator NonValidator, diff --git a/tests/src/integration.rs b/tests/src/integration.rs new file mode 100644 index 0000000000..8642e0e03c --- /dev/null +++ b/tests/src/integration.rs @@ -0,0 +1,3 @@ +mod masp; +mod setup; +mod utils; diff --git a/tests/src/integration/masp.rs b/tests/src/integration/masp.rs new file mode 100644 index 0000000000..6716769c83 --- /dev/null +++ b/tests/src/integration/masp.rs @@ -0,0 +1,1206 @@ +use std::path::PathBuf; + +use color_eyre::eyre::Result; +use color_eyre::owo_colors::OwoColorize; +use namada_apps::client::tx::CLIShieldedUtils; +use namada_apps::node::ledger::shell::testing::client::run; +use namada_apps::node::ledger::shell::testing::utils::Bin; +use namada_core::types::address::{btc, eth, masp_rewards}; +use namada_core::types::token; +use namada_core::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; +use test_log::test; + +use super::setup; +use crate::e2e::setup::constants::{ + AA_PAYMENT_ADDRESS, AA_VIEWING_KEY, AB_PAYMENT_ADDRESS, AB_VIEWING_KEY, + AC_PAYMENT_ADDRESS, AC_VIEWING_KEY, ALBERT, A_SPENDING_KEY, + BB_PAYMENT_ADDRESS, BERTHA, BTC, B_SPENDING_KEY, CHRISTEL, ETH, MASP, NAM, +}; +use crate::integration::utils::CapturedOutput; + +/// In this test we verify that users of the MASP receive the correct rewards +/// for leaving their assets in the pool for varying periods of time. +#[test] +fn masp_incentives() -> Result<()> { + // The number of decimal places used by BTC amounts. + const BTC_DENOMINATION: u8 = 8; + // The number of decimal places used by ETH amounts. + const ETH_DENOMINATION: u8 = 18; + // This address doesn't matter for tests. But an argument is required. + let validator_one_rpc = "127.0.0.1:26567"; + // Download the shielded pool parameters before starting node + let _ = CLIShieldedUtils::new(PathBuf::new()); + // Lengthen epoch to ensure that a transaction can be constructed and + // submitted within the same block. Necessary to ensure that conversion is + // not invalidated. + let mut node = setup::setup()?; + // Wait till epoch boundary + let ep0 = node.next_epoch(); + // Send 20 BTC from Albert to PA + run( + &node, + Bin::Client, + vec![ + "transfer", + "--source", + ALBERT, + "--target", + AA_PAYMENT_ADDRESS, + "--token", + BTC, + "--amount", + "20", + "--node", + validator_one_rpc, + ], + )?; + node.assert_success(); + + // Assert BTC balance at VK(A) is 20 + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + BTC, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("btc: 20")); + + // Assert NAM balance at VK(A) is 0 + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("No shielded nam balance found")); + + let masp_rewards = masp_rewards(); + + // Wait till epoch boundary + let ep1 = node.next_epoch(); + + // Assert BTC balance at VK(A) is 20 + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + BTC, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("btc: 20")); + + let amt20 = token::Amount::from_uint(20, BTC_DENOMINATION).unwrap(); + let amt10 = token::Amount::from_uint(10, ETH_DENOMINATION).unwrap(); + + // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_1-epoch_0) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + + let amt = (amt20 * masp_rewards[&btc()]).0 * (ep1.0 - ep0.0); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated))); + + // Assert NAM balance at MASP pool is 20*BTC_reward*(epoch_1-epoch_0) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + MASP, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + let amt = (amt20 * masp_rewards[&btc()]).0 * (ep1.0 - ep0.0); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated))); + + // Wait till epoch boundary + let ep2 = node.next_epoch(); + + // Assert BTC balance at VK(A) is 20 + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + BTC, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("btc: 20")); + + // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_2-epoch_0) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + let amt = (amt20 * masp_rewards[&btc()]).0 * (ep2.0 - ep0.0); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated))); + + // Assert NAM balance at MASP pool is 20*BTC_reward*(epoch_2-epoch_0) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + MASP, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + let amt = (amt20 * masp_rewards[&btc()]).0 * (ep2.0 - ep0.0); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated))); + + // Wait till epoch boundary + let ep3 = node.next_epoch(); + + // Send 10 ETH from Albert to PA(B) + run( + &node, + Bin::Client, + vec![ + "transfer", + "--source", + ALBERT, + "--target", + AB_PAYMENT_ADDRESS, + "--token", + ETH, + "--amount", + "10", + "--node", + validator_one_rpc, + ], + )?; + node.assert_success(); + + // Assert ETH balance at VK(B) is 10 + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AB_VIEWING_KEY, + "--token", + ETH, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("eth: 10")); + + // Assert NAM balance at VK(B) is 0 + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AB_VIEWING_KEY, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("No shielded nam balance found")); + + // Wait till epoch boundary + let ep4 = node.next_epoch(); + + // Assert ETH balance at VK(B) is 10 + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AB_VIEWING_KEY, + "--token", + ETH, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("eth: 10")); + + // Assert NAM balance at VK(B) is 10*ETH_reward*(epoch_4-epoch_3) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AB_VIEWING_KEY, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + let amt = (amt10 * masp_rewards[ð()]).0 * (ep4.0 - ep3.0); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated))); + + // Assert NAM balance at MASP pool is + // 20*BTC_reward*(epoch_4-epoch_0)+10*ETH_reward*(epoch_4-epoch_3) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + MASP, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + let amt = ((amt20 * masp_rewards[&btc()]).0 * (ep4.0 - ep0.0)) + + ((amt10 * masp_rewards[ð()]).0 * (ep4.0 - ep3.0)); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated))); + + // Wait till epoch boundary + let ep5 = node.next_epoch(); + + // Send 10 ETH from SK(B) to Christel + run( + &node, + Bin::Client, + vec![ + "transfer", + "--source", + B_SPENDING_KEY, + "--target", + CHRISTEL, + "--token", + ETH, + "--amount", + "10", + "--signer", + BERTHA, + "--node", + validator_one_rpc, + ], + )?; + node.assert_success(); + + // Assert ETH balance at VK(B) is 0 + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AB_VIEWING_KEY, + "--token", + ETH, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("No shielded eth balance found")); + + let _ep = node.next_epoch(); + + // Assert NAM balance at VK(B) is 10*ETH_reward*(ep-epoch_3) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AB_VIEWING_KEY, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + let amt = (amt10 * masp_rewards[ð()]).0 * (ep5.0 - ep3.0); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated))); + + let ep = node.next_epoch(); + // Assert NAM balance at MASP pool is + // 20*BTC_reward*(epoch_5-epoch_0)+10*ETH_reward*(epoch_5-epoch_3) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + MASP, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + let amt = ((amt20 * masp_rewards[&btc()]).0 * (ep.0 - ep0.0)) + + ((amt10 * masp_rewards[ð()]).0 * (ep5.0 - ep3.0)); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated))); + + // Wait till epoch boundary + let ep6 = node.next_epoch(); + + // Send 20 BTC from SK(A) to Christel + run( + &node, + Bin::Client, + vec![ + "transfer", + "--source", + A_SPENDING_KEY, + "--target", + CHRISTEL, + "--token", + BTC, + "--amount", + "20", + "--signer", + ALBERT, + "--node", + validator_one_rpc, + ], + )?; + node.assert_success(); + + // Assert BTC balance at VK(A) is 0 + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + BTC, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("No shielded btc balance found")); + + // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_6-epoch_0) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + let amt = (amt20 * masp_rewards[&btc()]).0 * (ep6.0 - ep0.0); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated,))); + + // Assert NAM balance at MASP pool is + // 20*BTC_reward*(epoch_6-epoch_0)+20*ETH_reward*(epoch_5-epoch_3) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + MASP, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + let amt = ((amt20 * masp_rewards[&btc()]).0 * (ep6.0 - ep0.0)) + + ((amt10 * masp_rewards[ð()]).0 * (ep5.0 - ep3.0)); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated,))); + + // Wait till epoch boundary + let _ep7 = node.next_epoch(); + + // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_6-epoch_0) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + let amt = (amt20 * masp_rewards[&btc()]).0 * (ep6.0 - ep0.0); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated))); + + // Assert NAM balance at VK(B) is 10*ETH_reward*(epoch_5-epoch_3) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AB_VIEWING_KEY, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + let amt = (amt10 * masp_rewards[ð()]).0 * (ep5.0 - ep3.0); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated,))); + + // Assert NAM balance at MASP pool is + // 20*BTC_reward*(epoch_6-epoch_0)+10*ETH_reward*(epoch_5-epoch_3) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + MASP, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + let amt = ((amt20 * masp_rewards[&btc()]).0 * (ep6.0 - ep0.0)) + + ((amt10 * masp_rewards[ð()]).0 * (ep5.0 - ep3.0)); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated,))); + + // Wait till epoch boundary to prevent conversion expiry during transaction + // construction + let _ep8 = node.next_epoch(); + + // Send 10*ETH_reward*(epoch_5-epoch_3) NAM from SK(B) to Christel + run( + &node, + Bin::Client, + vec![ + "transfer", + "--source", + B_SPENDING_KEY, + "--target", + CHRISTEL, + "--token", + NAM, + "--amount", + &((amt10 * masp_rewards[ð()]).0 * (ep5.0 - ep3.0)) + .to_string_native(), + "--signer", + BERTHA, + "--node", + validator_one_rpc, + ], + )?; + node.assert_success(); + + // Wait till epoch boundary + let _ep9 = node.next_epoch(); + + // Send 20*BTC_reward*(epoch_6-epoch_0) NAM from SK(A) to Bertha + run( + &node, + Bin::Client, + vec![ + "transfer", + "--source", + A_SPENDING_KEY, + "--target", + BERTHA, + "--token", + NAM, + "--amount", + &((amt20 * masp_rewards[&btc()]).0 * (ep6.0 - ep0.0)) + .to_string_native(), + "--signer", + ALBERT, + "--node", + validator_one_rpc, + ], + )?; + node.assert_success(); + + // Assert NAM balance at VK(A) is 0 + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("No shielded nam balance found")); + + // Assert NAM balance at VK(B) is 0 + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AB_VIEWING_KEY, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("No shielded nam balance found")); + + // Assert NAM balance at MASP pool is 0 + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + MASP, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("nam: 0")); + + Ok(()) +} + +/// In this test we: +/// 1. Run the ledger node +/// 2. Assert PPA(C) cannot be recognized by incorrect viewing key +/// 3. Assert PPA(C) has not transaction pinned to it +/// 4. Send 20 BTC from Albert to PPA(C) +/// 5. Assert PPA(C) has the 20 BTC transaction pinned to it +#[test] +fn masp_pinned_txs() -> Result<()> { + // This address doesn't matter for tests. But an argument is required. + let validator_one_rpc = "127.0.0.1:26567"; + // Download the shielded pool parameters before starting node + let _ = CLIShieldedUtils::new(PathBuf::new()); + + let mut node = setup::setup()?; + // Wait till epoch boundary + let _ep0 = node.next_epoch(); + + // Assert PPA(C) cannot be recognized by incorrect viewing key + let captured = + CapturedOutput::with_input(AB_VIEWING_KEY.into()).run(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AC_PAYMENT_ADDRESS, + "--token", + BTC, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!( + captured.contains("Supplied viewing key cannot decode transactions to") + ); + + // Assert PPA(C) has no transaction pinned to it + let captured = + CapturedOutput::with_input(AC_VIEWING_KEY.into()).run(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AC_PAYMENT_ADDRESS, + "--token", + BTC, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("has not yet been consumed")); + + // Wait till epoch boundary + let _ep1 = node.next_epoch(); + + // Send 20 BTC from Albert to PPA(C) + run( + &node, + Bin::Client, + vec![ + "transfer", + "--source", + ALBERT, + "--target", + AC_PAYMENT_ADDRESS, + "--token", + BTC, + "--amount", + "20", + "--node", + validator_one_rpc, + ], + )?; + node.assert_success(); + + // Wait till epoch boundary + // This makes it more consistent for some reason? + let _ep2 = node.next_epoch(); + + // Assert PPA(C) has the 20 BTC transaction pinned to it + let captured = + CapturedOutput::with_input(AC_VIEWING_KEY.into()).run(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AC_PAYMENT_ADDRESS, + "--token", + BTC, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("Received 20 btc")); + + // Assert PPA(C) has no NAM pinned to it + let captured = + CapturedOutput::with_input(AC_VIEWING_KEY.into()).run(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AC_PAYMENT_ADDRESS, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("Received no shielded nam")); + + // Wait till epoch boundary + let _ep1 = node.next_epoch(); + + // Assert PPA(C) does not NAM pinned to it on epoch boundary + let captured = + CapturedOutput::with_input(AC_VIEWING_KEY.into()).run(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AC_PAYMENT_ADDRESS, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("Received no shielded nam")); + Ok(()) +} + +/// In this test we: +/// 1. Run the ledger node +/// 2. Attempt to spend 10 BTC at SK(A) to PA(B) +/// 3. Attempt to spend 15 BTC at SK(A) to Bertha +/// 4. Send 20 BTC from Albert to PA(A) +/// 5. Attempt to spend 10 ETH at SK(A) to PA(B) +/// 6. Spend 7 BTC at SK(A) to PA(B) +/// 7. Spend 7 BTC at SK(A) to PA(B) +/// 8. Attempt to spend 7 BTC at SK(A) to PA(B) +/// 9. Spend 6 BTC at SK(A) to PA(B) +/// 10. Assert BTC balance at VK(A) is 0 +/// 11. Assert ETH balance at VK(A) is 0 +/// 12. Assert balance at VK(B) is 10 BTC +/// 13. Send 10 BTC from SK(B) to Bertha +#[test] +fn masp_txs_and_queries() -> Result<()> { + // Uncomment for better debugging + // let _log_guard = + // namada_apps::logging::init_from_env_or(tracing::level_filters::LevelFilter::INFO)? + // ; This address doesn't matter for tests. But an argument is required. + let validator_one_rpc = "127.0.0.1:26567"; + // Download the shielded pool parameters before starting node + let _ = CLIShieldedUtils::new(PathBuf::new()); + + enum Response { + Ok(&'static str), + Err(&'static str), + } + + let mut node = setup::setup()?; + _ = node.next_epoch(); + let txs_args = vec![ + // 0. Attempt to spend 10 BTC at SK(A) to PA(B) + ( + vec![ + "transfer", + "--source", + A_SPENDING_KEY, + "--target", + AB_PAYMENT_ADDRESS, + "--token", + BTC, + "--amount", + "10", + "--node", + validator_one_rpc, + ], + Response::Err(""), + ), + // 1. Attempt to spend 15 BTC at SK(A) to Bertha + ( + vec![ + "transfer", + "--source", + A_SPENDING_KEY, + "--target", + BERTHA, + "--token", + BTC, + "--amount", + "15", + "--node", + validator_one_rpc, + ], + Response::Err(""), + ), + // 2. Send 20 BTC from Albert to PA(A) + ( + vec![ + "transfer", + "--source", + ALBERT, + "--target", + AA_PAYMENT_ADDRESS, + "--token", + BTC, + "--amount", + "20", + "--node", + validator_one_rpc, + ], + Response::Ok("Transaction is valid"), + ), + // 3. Attempt to spend 10 ETH at SK(A) to PA(B) + ( + vec![ + "transfer", + "--source", + A_SPENDING_KEY, + "--target", + AB_PAYMENT_ADDRESS, + "--token", + ETH, + "--amount", + "10", + "--node", + validator_one_rpc, + ], + Response::Err(""), + ), + // 4. Spend 7 BTC at SK(A) to PA(B) + ( + vec![ + "transfer", + "--source", + A_SPENDING_KEY, + "--target", + AB_PAYMENT_ADDRESS, + "--token", + BTC, + "--amount", + "7", + "--node", + validator_one_rpc, + ], + Response::Ok("Transaction is valid"), + ), + // 5. Spend 7 BTC at SK(A) to PA(B) + ( + vec![ + "transfer", + "--source", + A_SPENDING_KEY, + "--target", + BB_PAYMENT_ADDRESS, + "--token", + BTC, + "--amount", + "7", + "--node", + validator_one_rpc, + ], + Response::Ok("Transaction is valid"), + ), + // 6. Attempt to spend 7 BTC at SK(A) to PA(B) + ( + vec![ + "transfer", + "--source", + A_SPENDING_KEY, + "--target", + BB_PAYMENT_ADDRESS, + "--token", + BTC, + "--amount", + "7", + "--node", + validator_one_rpc, + ], + Response::Err(""), + ), + // 7. Spend 6 BTC at SK(A) to PA(B) + ( + vec![ + "transfer", + "--source", + A_SPENDING_KEY, + "--target", + BB_PAYMENT_ADDRESS, + "--token", + BTC, + "--amount", + "6", + "--node", + validator_one_rpc, + ], + Response::Ok("Transaction is valid"), + ), + // 8. Assert BTC balance at VK(A) is 0 + ( + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + BTC, + "--node", + validator_one_rpc, + ], + Response::Ok("No shielded btc balance found"), + ), + // 9. Assert ETH balance at VK(A) is 0 + ( + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + ETH, + "--node", + validator_one_rpc, + ], + Response::Ok("No shielded eth balance found"), + ), + // 10. Assert balance at VK(B) is 20 BTC + ( + vec![ + "balance", + "--owner", + AB_VIEWING_KEY, + "--node", + validator_one_rpc, + ], + Response::Ok("btc : 20"), + ), + // 11. Send 20 BTC from SK(B) to Bertha + ( + vec![ + "transfer", + "--source", + B_SPENDING_KEY, + "--target", + BERTHA, + "--token", + BTC, + "--amount", + "20", + "--node", + validator_one_rpc, + ], + Response::Ok("Transaction is valid"), + ), + ]; + + for (tx_args, tx_result) in &txs_args { + // We ensure transfers don't cross epoch boundaries. + if tx_args[0] == "transfer" { + node.next_epoch(); + } + for &dry_run in &[true, false] { + let tx_args = if dry_run && tx_args[0] == "transfer" { + vec![tx_args.clone(), vec!["--dry-run"]].concat() + } else { + tx_args.clone() + }; + println!( + "{}: {:?}\n\n", + "Running".green().underline(), + tx_args.join(" ").yellow().underline() + ); + let captured = + CapturedOutput::of(|| run(&node, Bin::Client, tx_args.clone())); + match tx_result { + Response::Ok("Transaction is valid") => { + assert!( + captured.result.is_ok(), + "{:?} failed with result {:?}.\n Unread output: {}", + tx_args, + captured.result, + captured.output, + ); + if !dry_run { + node.assert_success(); + } else { + assert!( + captured.contains("Transaction is valid"), + "{:?} failed to contain needle 'Transaction is \ + valid',\nGot output '{}'", + tx_args, + captured.output + ); + } + } + Response::Ok(out) => { + assert!( + captured.result.is_ok(), + "{:?} failed with result {:?}.\n Unread output: {}", + tx_args, + captured.result, + captured.output, + ); + assert!( + captured.contains(out), + "{:?} failed to contain needle '{}',\nGot output '{}'", + tx_args, + out, + captured.output + ); + } + Response::Err(msg) => { + assert!( + captured.result.is_err(), + "{:?} unexpectedly succeeded", + tx_args + ); + assert!( + captured.contains(msg), + "{:?} failed to contain needle {},\nGot output {}", + tx_args, + msg, + captured.output + ); + } + } + } + } + + Ok(()) +} diff --git a/tests/src/integration/setup.rs b/tests/src/integration/setup.rs new file mode 100644 index 0000000000..df74c5f6f1 --- /dev/null +++ b/tests/src/integration/setup.rs @@ -0,0 +1,163 @@ +use std::mem::ManuallyDrop; +use std::path::Path; +use std::str::FromStr; +use std::sync::{Arc, Mutex}; + +use color_eyre::eyre::{eyre, Result}; +use namada_apps::cli::args; +use namada_apps::config; +use namada_apps::config::genesis::genesis_config; +use namada_apps::config::genesis::genesis_config::GenesisConfig; +use namada_apps::config::TendermintMode; +use namada_apps::facade::tendermint::Timeout; +use namada_apps::facade::tendermint_proto::google::protobuf::Timestamp; +use namada_apps::node::ledger::shell::testing::node::MockNode; +use namada_apps::node::ledger::shell::testing::utils::TestDir; +use namada_apps::node::ledger::shell::Shell; +use namada_core::types::address::Address; +use namada_core::types::chain::{ChainId, ChainIdPrefix}; +use toml::value::Table; + +use crate::e2e::setup::{ + copy_wasm_to_chain_dir, get_all_wasms_hashes, SINGLE_NODE_NET_GENESIS, +}; + +/// Env. var for keeping temporary files created by the integration tests +const ENV_VAR_KEEP_TEMP: &str = "NAMADA_INT_KEEP_TEMP"; + +/// Setup a network with a single genesis validator node. +pub fn setup() -> Result { + initialize_genesis(|genesis| genesis) +} + +/// Setup folders with genesis, configs, wasm, etc. +pub fn initialize_genesis( + mut update_genesis: impl FnMut(GenesisConfig) -> GenesisConfig, +) -> Result { + let working_dir = std::fs::canonicalize("..").unwrap(); + let keep_temp = match std::env::var(ENV_VAR_KEEP_TEMP) { + Ok(val) => val.to_ascii_lowercase() != "false", + _ => false, + }; + let test_dir = TestDir::new(); + + // Open the source genesis file + let mut genesis = genesis_config::open_genesis_config( + working_dir.join(SINGLE_NODE_NET_GENESIS), + )?; + + genesis.parameters.vp_whitelist = + Some(get_all_wasms_hashes(&working_dir, Some("vp_"))); + genesis.parameters.tx_whitelist = + Some(get_all_wasms_hashes(&working_dir, Some("tx_"))); + + // Run the provided function on it + let genesis = update_genesis(genesis); + + // Run `init-network` to generate the finalized genesis config, keys and + // addresses and update WASM checksums + let genesis_path = test_dir.path().join("e2e-test-genesis-src.toml"); + genesis_config::write_genesis_config(&genesis, &genesis_path); + let wasm_checksums_path = working_dir.join("wasm/checksums.json"); + + // setup genesis file + namada_apps::client::utils::init_network( + args::Global { + chain_id: None, + base_dir: test_dir.path().to_path_buf(), + wasm_dir: None, + }, + args::InitNetwork { + genesis_path, + wasm_checksums_path, + chain_id_prefix: ChainIdPrefix::from_str("integration-test") + .unwrap(), + unsafe_dont_encrypt: true, + consensus_timeout_commit: Timeout::from_str("1s").unwrap(), + localhost: true, + allow_duplicate_ip: true, + dont_archive: true, + archive_dir: None, + }, + ); + + create_node(test_dir, &genesis, keep_temp) +} + +/// Create a mock ledger node. +fn create_node( + base_dir: TestDir, + genesis: &GenesisConfig, + keep_temp: bool, +) -> Result { + // look up the chain id from the global file. + let chain_id = if let toml::Value::String(chain_id) = + toml::from_str::( + &std::fs::read_to_string( + base_dir.path().join("global-config.toml"), + ) + .unwrap(), + ) + .unwrap() + .get("default_chain_id") + .unwrap() + { + chain_id.to_string() + } else { + return Err(eyre!("Could not read chain id from global-config.toml")); + }; + + // the directory holding compiled wasm + let wasm_dir = base_dir.path().join(Path::new(&chain_id)).join("wasm"); + // copy compiled wasms into the wasm directory + let chain_id = ChainId::from_str(&chain_id).unwrap(); + copy_wasm_to_chain_dir( + &std::fs::canonicalize("..").unwrap(), + &base_dir.path().join(Path::new(&chain_id.to_string())), + &chain_id, + genesis.validator.keys(), + ); + + // instantiate and initialize the ledger node. + let (sender, recv) = tokio::sync::mpsc::unbounded_channel(); + let node = MockNode { + shell: Arc::new(Mutex::new(Shell::new( + config::Ledger::new( + base_dir.path(), + chain_id.clone(), + TendermintMode::Validator + ), + wasm_dir, + sender, + None, + None, + 50 * 1024 * 1024, // 50 kiB + 50 * 1024 * 1024, // 50 kiB + Address::from_str("atest1v4ehgw36x3prswzxggunzv6pxqmnvdj9xvcyzvpsggeyvs3cg9qnywf589qnwvfsg5erg3fkl09rg5").unwrap(), + ))), + test_dir: ManuallyDrop::new(base_dir), + keep_temp, + _broadcast_recv: recv, + results: Arc::new(Mutex::new(vec![])), + }; + let init_req = namada_apps::facade::tower_abci::request::InitChain { + time: Some(Timestamp { + seconds: 0, + nanos: 0, + }), + chain_id: chain_id.to_string(), + consensus_params: None, + validators: vec![], + app_state_bytes: vec![], + initial_height: 0, + }; + { + let mut locked = node.shell.lock().unwrap(); + locked + .init_chain(init_req, 1) + .map_err(|e| eyre!("Failed to initialize ledger: {:?}", e))?; + locked.commit(); + } + + Ok(node) +} diff --git a/tests/src/integration/utils.rs b/tests/src/integration/utils.rs new file mode 100644 index 0000000000..f626a001ee --- /dev/null +++ b/tests/src/integration/utils.rs @@ -0,0 +1,83 @@ +use std::fs::File; +use std::path::PathBuf; +use std::sync::Arc; + +struct TempFile(PathBuf); +impl TempFile { + fn new(path: PathBuf) -> (Self, File) { + let f = File::create(&path).unwrap(); + (Self(path), f) + } +} + +impl Drop for TempFile { + fn drop(&mut self) { + _ = std::fs::remove_file(&self.0); + } +} + +/// Test helper that captures stdout of +/// a process. +pub struct CapturedOutput { + pub output: String, + pub result: T, + input: String, +} + +impl CapturedOutput { + pub fn with_input(input: String) -> Self { + Self { + output: "".to_string(), + result: (), + input, + } + } +} + +impl CapturedOutput { + /// Run a client command and capture + /// the output to the mocked stdout. + pub(crate) fn of(func: F) -> Self + where + F: FnOnce() -> T, + { + std::io::set_output_capture(Some(Default::default())); + let mut capture = Self { + output: Default::default(), + result: func(), + input: Default::default(), + }; + let captured = std::io::set_output_capture(None); + let captured = captured.unwrap(); + let captured = Arc::try_unwrap(captured).unwrap(); + let captured = captured.into_inner().unwrap(); + capture.output = String::from_utf8(captured).unwrap(); + capture + } + + /// Run a client command with input to the mocked stdin and capture + /// the output to the mocked stdout + pub fn run(&self, func: F) -> CapturedOutput + where + F: FnOnce() -> U, + { + { + // write the input to the mocked stdin + let mut buf = namada_apps::cli::TESTIN.lock().unwrap(); + buf.clear(); + buf.extend_from_slice(self.input.as_bytes()); + } + CapturedOutput::of(func) + } + + /// Check if the captured output contains the regex. + pub fn matches(&self, needle: regex::Regex) -> bool { + needle.captures(&self.output).is_some() + } + + /// Check if the captured output contains the string. + pub fn contains(&self, needle: &str) -> bool { + let needle = regex::Regex::new(needle).unwrap(); + self.matches(needle) + } +} diff --git a/tests/src/lib.rs b/tests/src/lib.rs index db37039878..7e35947477 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -1,3 +1,4 @@ +#![cfg_attr(test, feature(internal_output_capture))] //! Namada integrations and WASM tests and testing helpers. #![doc(html_favicon_url = "https://dev.namada.net/master/favicon.png")] @@ -11,6 +12,9 @@ mod vm_host_env; pub use vm_host_env::{ibc, tx, vp}; #[cfg(test)] mod e2e; +#[cfg(test)] +#[allow(dead_code)] +mod integration; pub mod native_vp; pub mod storage; #[cfg(test)] diff --git a/tests/src/native_vp/eth_bridge_pool.rs b/tests/src/native_vp/eth_bridge_pool.rs index a794057058..289efc2d7e 100644 --- a/tests/src/native_vp/eth_bridge_pool.rs +++ b/tests/src/native_vp/eth_bridge_pool.rs @@ -6,7 +6,6 @@ mod test_bridge_pool_vp { use namada::core::ledger::eth_bridge::storage::bridge_pool::BRIDGE_POOL_ADDRESS; use namada::ledger::eth_bridge::{ wrapped_erc20s, Contracts, EthereumBridgeConfig, UpgradeableContract, - ADDRESS, }; use namada::ledger::native_vp::ethereum_bridge::bridge_pool_vp::BridgePoolVp; use namada::proto::Tx; @@ -84,27 +83,12 @@ mod test_bridge_pool_vp { // initialize Bertha's account env.spawn_accounts([&albert_address(), &bertha_address(), &nam()]); // enrich Albert - env.credit_tokens( - &albert_address(), - &nam(), - None, - BERTHA_WEALTH.into(), - ); + env.credit_tokens(&albert_address(), &nam(), BERTHA_WEALTH.into()); // enrich Bertha - env.credit_tokens( - &bertha_address(), - &nam(), - None, - BERTHA_WEALTH.into(), - ); + env.credit_tokens(&bertha_address(), &nam(), BERTHA_WEALTH.into()); // Bertha has ERC20 tokens too. - let sub_prefix = wrapped_erc20s::sub_prefix(&ASSET); - env.credit_tokens( - &bertha_address(), - &ADDRESS, - Some(sub_prefix), - BERTHA_TOKENS.into(), - ); + let token = wrapped_erc20s::token(&ASSET); + env.credit_tokens(&bertha_address(), &token, BERTHA_TOKENS.into()); env } diff --git a/tests/src/storage_api/testnet_pow.rs b/tests/src/storage_api/testnet_pow.rs index 5e54188c1b..ab45bc99c8 100644 --- a/tests/src/storage_api/testnet_pow.rs +++ b/tests/src/storage_api/testnet_pow.rs @@ -11,7 +11,7 @@ use crate::vp; fn test_challenge_and_solution() -> storage_api::Result<()> { let faucet_address = address::testing::established_address_1(); let difficulty = Difficulty::try_new(1).unwrap(); - let withdrawal_limit = token::Amount::native_whole(1_000); + let withdrawal_limit = token::Amount::native_whole(1_000).into(); let mut tx_env = TestTxEnv::default(); diff --git a/tests/src/vm_host_env/ibc.rs b/tests/src/vm_host_env/ibc.rs index b73fef0a3d..5858abe7d3 100644 --- a/tests/src/vm_host_env/ibc.rs +++ b/tests/src/vm_host_env/ibc.rs @@ -60,13 +60,14 @@ pub use namada::ledger::ibc::storage::{ ack_key, channel_counter_key, channel_key, client_counter_key, client_state_key, client_type_key, client_update_height_key, client_update_timestamp_key, commitment_key, connection_counter_key, - connection_key, consensus_state_key, ibc_token_prefix, - next_sequence_ack_key, next_sequence_recv_key, next_sequence_send_key, - port_key, receipt_key, + connection_key, consensus_state_key, ibc_token, next_sequence_ack_key, + next_sequence_recv_key, next_sequence_send_key, port_key, receipt_key, }; use namada::ledger::ibc::vp::{ get_dummy_genesis_validator, get_dummy_header as tm_dummy_header, Ibc, - IbcToken, +}; +use namada::ledger::native_vp::multitoken::{ + Error as MultitokenVpError, MultitokenVp, }; use namada::ledger::native_vp::{Ctx, NativeVp}; use namada::ledger::parameters::storage::{ @@ -92,7 +93,7 @@ use namada::vm::{wasm, WasmCacheRwAccess}; use namada_test_utils::TestWasms; use namada_tx_prelude::BorshSerialize; -use crate::tx::{self, *}; +use crate::tx::*; const ADDRESS: Address = Address::Internal(InternalAddress::Ibc); @@ -115,19 +116,20 @@ impl<'a> TestIbcVp<'a> { } } -pub struct TestIbcTokenVp<'a> { - pub token: IbcToken<'a, MockDB, Sha256Hasher, WasmCacheRwAccess>, +pub struct TestMultitokenVp<'a> { + pub multitoken_vp: + MultitokenVp<'a, MockDB, Sha256Hasher, WasmCacheRwAccess>, } -impl<'a> TestIbcTokenVp<'a> { +impl<'a> TestMultitokenVp<'a> { pub fn validate( &self, - tx_data: &Tx, - ) -> std::result::Result { - self.token.validate_tx( - tx_data, - self.token.ctx.keys_changed, - self.token.ctx.verifiers, + tx: &Tx, + ) -> std::result::Result { + self.multitoken_vp.validate_tx( + tx, + self.multitoken_vp.ctx.keys_changed, + self.multitoken_vp.ctx.verifiers, ) } } @@ -168,11 +170,11 @@ pub fn validate_ibc_vp_from_tx<'a>( } /// Validate the native token VP for the given address -pub fn validate_token_vp_from_tx<'a>( +pub fn validate_multitoken_vp_from_tx<'a>( tx_env: &'a TestTxEnv, tx: &'a Tx, target: &Key, -) -> std::result::Result { +) -> std::result::Result { let (verifiers, keys_changed) = tx_env .wl_storage .write_log @@ -198,9 +200,9 @@ pub fn validate_token_vp_from_tx<'a>( &verifiers, vp_wasm_cache, ); - let token = IbcToken { ctx }; + let multitoken_vp = MultitokenVp { ctx }; - TestIbcTokenVp { token }.validate(tx) + TestMultitokenVp { multitoken_vp }.validate(tx) } /// Initialize the test storage. Requires initialized [`tx_host_env::ENV`]. @@ -233,10 +235,10 @@ pub fn init_storage() -> (Address, Address) { }); // initialize a token - let token = tx::ctx().init_account(code_hash).unwrap(); + let token = tx_host_env::ctx().init_account(code_hash).unwrap(); // initialize an account - let account = tx::ctx().init_account(code_hash).unwrap(); + let account = tx_host_env::ctx().init_account(code_hash).unwrap(); let key = token::balance_key(&token, &account); let init_bal = Amount::native_whole(100); let bytes = init_bal.try_to_vec().expect("encoding failed"); @@ -263,6 +265,22 @@ pub fn init_storage() -> (Address, Address) { env.wl_storage.storage.write(&key, &bytes).unwrap(); }); + // commit the initialized token and account + tx_host_env::with(|env| { + env.wl_storage.commit_tx(); + env.wl_storage.commit_block().unwrap(); + + // block header to check timeout timestamp + env.wl_storage + .storage + .set_header(tm_dummy_header()) + .unwrap(); + env.wl_storage + .storage + .begin_block(BlockHash::default(), BlockHeight(2)) + .unwrap(); + }); + (token, account) } @@ -762,6 +780,6 @@ pub fn packet_from_message( } pub fn balance_key_with_ibc_prefix(denom: String, owner: &Address) -> Key { - let prefix = ibc_token_prefix(denom).expect("invalid denom"); - token::multitoken_balance_key(&prefix, owner) + let ibc_token = ibc_token(denom); + token::balance_key(&ibc_token, owner) } diff --git a/tests/src/vm_host_env/mod.rs b/tests/src/vm_host_env/mod.rs index 40e549c8f7..f86d5800d7 100644 --- a/tests/src/vm_host_env/mod.rs +++ b/tests/src/vm_host_env/mod.rs @@ -37,8 +37,11 @@ mod tests { use namada_core::ledger::ibc::context::transfer_mod::testing::DummyTransferModule; use namada_core::ledger::ibc::Error as IbcActionError; use namada_test_utils::TestWasms; + use namada_tx_prelude::address::InternalAddress; use namada_tx_prelude::chain::ChainId; - use namada_tx_prelude::{BorshSerialize, StorageRead, StorageWrite}; + use namada_tx_prelude::{ + Address, BorshSerialize, StorageRead, StorageWrite, + }; use namada_vp_prelude::account::AccountPublicKeysMap; use namada_vp_prelude::VpEnv; use prost::Message; @@ -1169,10 +1172,10 @@ mod tests { // Check if the token was escrowed let escrow = token::balance_key( &token, - &address::Address::Internal(address::InternalAddress::IbcEscrow), + &address::Address::Internal(address::InternalAddress::Ibc), ); let token_vp_result = - ibc::validate_token_vp_from_tx(&env, &tx, &escrow); + ibc::validate_multitoken_vp_from_tx(&env, &tx, &escrow); assert!(token_vp_result.expect("token validation failed unexpectedly")); // Commit @@ -1224,7 +1227,7 @@ mod tests { assert_eq!(balance, Some(Amount::native_whole(0))); let escrow_key = token::balance_key( &token, - &address::Address::Internal(address::InternalAddress::IbcEscrow), + &address::Address::Internal(address::InternalAddress::Ibc), ); let escrow: Option = tx_host_env::with(|env| { env.wl_storage.read(&escrow_key).expect("read error") @@ -1253,14 +1256,23 @@ mod tests { writes.extend(channel_writes); // the origin-specific token let denom = format!("{}/{}/{}", port_id, channel_id, token); - let key_prefix = ibc_storage::ibc_token_prefix(&denom).unwrap(); - let balance_key = token::multitoken_balance_key(&key_prefix, &sender); + let ibc_token = ibc_storage::ibc_token(&denom); + let balance_key = token::balance_key(&ibc_token, &sender); let init_bal = Amount::native_whole(100); writes.insert(balance_key.clone(), init_bal.try_to_vec().unwrap()); + let minted_key = token::minted_balance_key(&ibc_token); + writes.insert(minted_key.clone(), init_bal.try_to_vec().unwrap()); + let minter_key = token::minter_key(&ibc_token); + writes.insert( + minter_key, + Address::Internal(InternalAddress::Ibc) + .try_to_vec() + .unwrap(), + ); // original denom let hash = ibc_storage::calc_hash(&denom); - let denom_key = ibc_storage::ibc_denom_key(&hash); - writes.insert(denom_key, denom.as_bytes().to_vec()); + let denom_key = ibc_storage::ibc_denom_key(hash); + writes.insert(denom_key, denom.try_to_vec().unwrap()); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { env.wl_storage @@ -1272,11 +1284,7 @@ mod tests { // Start a transaction to send a packet // Set this chain is the sink zone - let ibc_token = address::Address::Internal( - address::InternalAddress::IbcToken(hash), - ); - let hashed_denom = - format!("{}/{}", ibc_storage::MULTITOKEN_STORAGE_KEY, ibc_token); + let hashed_denom = ibc_token.to_string(); let msg = ibc::msg_transfer(port_id, channel_id, hashed_denom, &sender); let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); @@ -1298,11 +1306,8 @@ mod tests { let result = ibc::validate_ibc_vp_from_tx(&env, &tx); assert!(result.expect("validation failed unexpectedly")); // Check if the token was burned - let burn = token::balance_key( - &token, - &address::Address::Internal(address::InternalAddress::IbcBurn), - ); - let result = ibc::validate_token_vp_from_tx(&env, &tx, &burn); + let result = + ibc::validate_multitoken_vp_from_tx(&env, &tx, &minted_key); assert!(result.expect("token validation failed unexpectedly")); // Check the balance tx_host_env::set(env); @@ -1310,14 +1315,10 @@ mod tests { env.wl_storage.read(&balance_key).expect("read error") }); assert_eq!(balance, Some(Amount::native_whole(0))); - let burn_key = token::balance_key( - &token, - &address::Address::Internal(address::InternalAddress::IbcBurn), - ); - let burn: Option = tx_host_env::with(|env| { - env.wl_storage.read(&burn_key).expect("read error") + let minted: Option = tx_host_env::with(|env| { + env.wl_storage.read(&minted_key).expect("read error") }); - assert_eq!(burn, Some(Amount::native_whole(100))); + assert_eq!(minted, Some(Amount::native_whole(0))); } #[test] @@ -1380,20 +1381,23 @@ mod tests { let result = ibc::validate_ibc_vp_from_tx(&env, &tx); assert!(result.expect("validation failed unexpectedly")); // Check if the token was minted - let mint = token::balance_key( - &token, - &address::Address::Internal(address::InternalAddress::IbcMint), - ); - let result = ibc::validate_token_vp_from_tx(&env, &tx, &mint); + let denom = format!("{}/{}/{}", port_id, channel_id, token); + let ibc_token = ibc::ibc_token(&denom); + let minted_key = token::minted_balance_key(&ibc_token); + let result = + ibc::validate_multitoken_vp_from_tx(&env, &tx, &minted_key); assert!(result.expect("token validation failed unexpectedly")); // Check the balance tx_host_env::set(env); - let denom = format!("{}/{}/{}", port_id, channel_id, token); let key = ibc::balance_key_with_ibc_prefix(denom, &receiver); let balance: Option = tx_host_env::with(|env| { env.wl_storage.read(&key).expect("read error") }); assert_eq!(balance, Some(Amount::native_whole(100))); + let minted: Option = tx_host_env::with(|env| { + env.wl_storage.read(&minted_key).expect("read error") + }); + assert_eq!(minted, Some(Amount::native_whole(100))); } #[test] @@ -1426,7 +1430,7 @@ mod tests { // escrow in advance let escrow_key = token::balance_key( &token, - &address::Address::Internal(address::InternalAddress::IbcEscrow), + &address::Address::Internal(address::InternalAddress::Ibc), ); let val = Amount::native_whole(100).try_to_vec().unwrap(); tx_host_env::with(|env| { @@ -1474,7 +1478,8 @@ mod tests { let result = ibc::validate_ibc_vp_from_tx(&env, &tx); assert!(result.expect("validation failed unexpectedly")); // Check if the token was unescrowed - let result = ibc::validate_token_vp_from_tx(&env, &tx, &escrow_key); + let result = + ibc::validate_multitoken_vp_from_tx(&env, &tx, &escrow_key); assert!(result.expect("token validation failed unexpectedly")); // Check the balance tx_host_env::set(env); @@ -1517,9 +1522,13 @@ mod tests { }); }); // escrow in advance - let escrow_key = token::balance_key( - &token, - &address::Address::Internal(address::InternalAddress::IbcEscrow), + let dummy_src_port = "dummy_transfer"; + let dummy_src_channel = "channel_42"; + let denom = + format!("{}/{}/{}", dummy_src_port, dummy_src_channel, token); + let escrow_key = ibc::balance_key_with_ibc_prefix( + denom, + &address::Address::Internal(address::InternalAddress::Ibc), ); let val = Amount::native_whole(100).try_to_vec().unwrap(); tx_host_env::with(|env| { @@ -1531,8 +1540,6 @@ mod tests { // Set this chain as the source zone let counterparty = ibc::dummy_channel_counterparty(); - let dummy_src_port = "dummy_transfer"; - let dummy_src_channel = "channel_42"; let denom = format!( "{}/{}/{}/{}/{}", counterparty.port_id().clone(), @@ -1572,7 +1579,8 @@ mod tests { let result = ibc::validate_ibc_vp_from_tx(&env, &tx); assert!(result.expect("validation failed unexpectedly")); // Check if the token was unescrowed - let result = ibc::validate_token_vp_from_tx(&env, &tx, &escrow_key); + let result = + ibc::validate_multitoken_vp_from_tx(&env, &tx, &escrow_key); assert!(result.expect("token validation failed unexpectedly")); // Check the balance tx_host_env::set(env); @@ -1676,9 +1684,9 @@ mod tests { // Check if the token was refunded let escrow = token::balance_key( &token, - &address::Address::Internal(address::InternalAddress::IbcEscrow), + &address::Address::Internal(address::InternalAddress::Ibc), ); - let result = ibc::validate_token_vp_from_tx(&env, &tx, &escrow); + let result = ibc::validate_multitoken_vp_from_tx(&env, &tx, &escrow); assert!(result.expect("token validation failed unexpectedly")); } @@ -1767,9 +1775,9 @@ mod tests { // Check if the token was refunded let escrow = token::balance_key( &token, - &address::Address::Internal(address::InternalAddress::IbcEscrow), + &address::Address::Internal(address::InternalAddress::Ibc), ); - let result = ibc::validate_token_vp_from_tx(&env, &tx, &escrow); + let result = ibc::validate_multitoken_vp_from_tx(&env, &tx, &escrow); assert!(result.expect("token validation failed unexpectedly")); } } diff --git a/tests/src/vm_host_env/tx.rs b/tests/src/vm_host_env/tx.rs index 3f16d3554d..aad6510c89 100644 --- a/tests/src/vm_host_env/tx.rs +++ b/tests/src/vm_host_env/tx.rs @@ -212,17 +212,9 @@ impl TestTxEnv { &mut self, target: &Address, token: &Address, - sub_prefix: Option, amount: token::Amount, ) { - let storage_key = match &sub_prefix { - Some(sub_prefix) => { - let prefix = - token::multitoken_balance_prefix(token, sub_prefix); - token::multitoken_balance_key(&prefix, target) - } - None => token::balance_key(token, target), - }; + let storage_key = token::balance_key(token, target); self.wl_storage .storage .write(&storage_key, amount.try_to_vec().unwrap()) diff --git a/tx_prelude/src/ibc.rs b/tx_prelude/src/ibc.rs index 12654df964..d15a60a5af 100644 --- a/tx_prelude/src/ibc.rs +++ b/tx_prelude/src/ibc.rs @@ -9,11 +9,12 @@ pub use namada_core::ledger::ibc::{ }; use namada_core::ledger::storage_api::{StorageRead, StorageWrite}; use namada_core::ledger::tx_env::TxEnv; +use namada_core::types::address::{Address, InternalAddress}; pub use namada_core::types::ibc::IbcEvent; use namada_core::types::storage::{BlockHeight, Header, Key}; use namada_core::types::token::Amount; -use crate::token::transfer_with_keys; +use crate::token::{burn, mint, transfer}; use crate::{Ctx, KeyValIterator}; /// IBC actions to handle an IBC message @@ -72,11 +73,37 @@ impl IbcStorageContext for Ctx { fn transfer_token( &mut self, - src: &Key, - dest: &Key, + src: &Address, + dest: &Address, + token: &Address, amount: Amount, ) -> std::result::Result<(), Self::Error> { - transfer_with_keys(self, src, dest, amount) + let amount = amount.denominated(token, self)?; + transfer(self, src, dest, token, amount, &None, &None, &None) + } + + fn mint_token( + &mut self, + target: &Address, + token: &Address, + amount: Amount, + ) -> Result<(), Self::Error> { + mint( + self, + &Address::Internal(InternalAddress::Ibc), + target, + token, + amount, + ) + } + + fn burn_token( + &mut self, + target: &Address, + token: &Address, + amount: Amount, + ) -> Result<(), Self::Error> { + burn(self, target, token, amount) } fn get_height(&self) -> std::result::Result { diff --git a/tx_prelude/src/token.rs b/tx_prelude/src/token.rs index 685a2e51a6..e950668d2b 100644 --- a/tx_prelude/src/token.rs +++ b/tx_prelude/src/token.rs @@ -1,5 +1,5 @@ use masp_primitives::transaction::Transaction; -use namada_core::types::address::{Address, InternalAddress}; +use namada_core::types::address::Address; use namada_core::types::hash::Hash; use namada_core::types::storage::KeySeg; use namada_core::types::token; @@ -14,79 +14,24 @@ pub fn transfer( src: &Address, dest: &Address, token: &Address, - sub_prefix: Option, amount: DenominatedAmount, key: &Option, shielded_hash: &Option, shielded: &Option, ) -> TxResult { - if amount.amount != Amount::default() { - let src_key = match &sub_prefix { - Some(sub_prefix) => { - let prefix = - token::multitoken_balance_prefix(token, sub_prefix); - token::multitoken_balance_key(&prefix, src) - } - None => token::balance_key(token, src), - }; - let dest_key = match &sub_prefix { - Some(sub_prefix) => { - let prefix = - token::multitoken_balance_prefix(token, sub_prefix); - token::multitoken_balance_key(&prefix, dest) - } - None => token::balance_key(token, dest), - }; - if src != dest { - let src_bal: Option = match src { - Address::Internal(InternalAddress::IbcMint) => { - Some(Amount::max_signed()) - } - Address::Internal(InternalAddress::IbcBurn) => { - log_string("invalid transfer from the burn address"); - unreachable!() - } - _ => ctx.read(&src_key)?, - }; - let mut src_bal = src_bal.unwrap_or_else(|| { - log_string(format!("src {} has no balance", src_key)); - unreachable!() - }); - src_bal.spend(&amount.amount); - let mut dest_bal: Amount = match dest { - Address::Internal(InternalAddress::IbcMint) => { - log_string("invalid transfer to the mint address"); - unreachable!() - } - _ => ctx.read(&dest_key)?.unwrap_or_default(), - }; - dest_bal.receive(&amount.amount); - - match src { - Address::Internal(InternalAddress::IbcMint) => { - ctx.write_temp(&src_key, src_bal)?; - } - Address::Internal(InternalAddress::IbcBurn) => { - log_string("invalid transfer from the burn address"); - unreachable!() - } - _ => { - ctx.write(&src_key, src_bal)?; - } - } - match dest { - Address::Internal(InternalAddress::IbcMint) => { - log_string("invalid transfer to the mint address"); - unreachable!() - } - Address::Internal(InternalAddress::IbcBurn) => { - ctx.write_temp(&dest_key, dest_bal)?; - } - _ => { - ctx.write(&dest_key, dest_bal)?; - } - } - } + if amount.amount != Amount::default() && src != dest { + let src_key = token::balance_key(token, src); + let dest_key = token::balance_key(token, dest); + let src_bal: Option = ctx.read(&src_key)?; + let mut src_bal = src_bal.unwrap_or_else(|| { + log_string(format!("src {} has no balance", src_key)); + unreachable!() + }); + src_bal.spend(&amount.amount); + let mut dest_bal: Amount = ctx.read(&dest_key)?.unwrap_or_default(); + dest_bal.receive(&amount.amount); + ctx.write(&src_key, src_bal)?; + ctx.write(&dest_key, dest_bal)?; } // If this transaction has a shielded component, then handle it @@ -109,8 +54,6 @@ pub fn transfer( source: src.clone(), target: dest.clone(), token: token.clone(), - // todo: build asset types for multitokens - sub_prefix: None, amount, key: key.clone(), shielded: *shielded_hash, @@ -135,49 +78,49 @@ pub fn transfer( Ok(()) } -/// A token transfer with storage keys that can be used in a transaction. -pub fn transfer_with_keys( +/// Mint that can be used in a transaction. +pub fn mint( ctx: &mut Ctx, - src_key: &storage::Key, - dest_key: &storage::Key, + minter: &Address, + target: &Address, + token: &Address, amount: Amount, ) -> TxResult { - let src_owner = is_any_token_or_multitoken_balance_key(src_key); - let src_bal: Option = match src_owner { - Some([_, Address::Internal(InternalAddress::IbcMint)]) => { - Some(Amount::max_signed()) - } - Some([_, Address::Internal(InternalAddress::IbcBurn)]) => { - log_string("invalid transfer from the burn address"); - unreachable!() - } - _ => ctx.read(src_key)?, - }; - let mut src_bal = src_bal.unwrap_or_else(|| { - log_string(format!("src {} has no balance", src_key)); - unreachable!() - }); - src_bal.spend(&amount); - let dest_owner = is_any_token_balance_key(dest_key); - let mut dest_bal: Amount = match dest_owner { - Some([_, Address::Internal(InternalAddress::IbcMint)]) => { - log_string("invalid transfer to the mint address"); - unreachable!() - } - _ => ctx.read(dest_key)?.unwrap_or_default(), - }; - dest_bal.receive(&amount); - match src_owner { - Some([_, Address::Internal(InternalAddress::IbcMint)]) => { - ctx.write_temp(src_key, src_bal)?; - } - _ => ctx.write(src_key, src_bal)?, - } - match dest_owner { - Some([_, Address::Internal(InternalAddress::IbcBurn)]) => { - ctx.write_temp(dest_key, dest_bal)?; - } - _ => ctx.write(dest_key, dest_bal)?, - } + let target_key = token::balance_key(token, target); + let mut target_bal: Amount = ctx.read(&target_key)?.unwrap_or_default(); + target_bal.receive(&amount); + + let minted_key = token::minted_balance_key(token); + let mut minted_bal: Amount = ctx.read(&minted_key)?.unwrap_or_default(); + minted_bal.receive(&amount); + + ctx.write(&target_key, target_bal)?; + ctx.write(&minted_key, minted_bal)?; + + let minter_key = token::minter_key(token); + ctx.write(&minter_key, minter)?; + + Ok(()) +} + +/// Burn that can be used in a transaction. +pub fn burn( + ctx: &mut Ctx, + target: &Address, + token: &Address, + amount: Amount, +) -> TxResult { + let target_key = token::balance_key(token, target); + let mut target_bal: Amount = ctx.read(&target_key)?.unwrap_or_default(); + target_bal.spend(&amount); + + // burn the minted amount + let minted_key = token::minted_balance_key(token); + let mut minted_bal: Amount = ctx.read(&minted_key)?.unwrap_or_default(); + minted_bal.spend(&amount); + + ctx.write(&target_key, target_bal)?; + ctx.write(&minted_key, minted_bal)?; + Ok(()) } diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index 05ea297b9d..f6d14e2c2b 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -89,6 +89,55 @@ 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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +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.71" @@ -315,7 +364,7 @@ checksum = "f8175979259124331c1d7bf6586ee7e0da434155e4b2d48ec2c8386281d8df39" dependencies = [ "async-trait", "axum-core", - "bitflags", + "bitflags 1.3.2", "bytes", "futures-util", "http", @@ -511,6 +560,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "bitvec" version = "0.17.4" @@ -896,6 +951,33 @@ dependencies = [ "version_check", ] +[[package]] +name = "clap" +version = "4.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + [[package]] name = "clru" version = "0.5.0" @@ -957,6 +1039,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "concat-idents" version = "1.1.4" @@ -1467,7 +1555,7 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" dependencies = [ - "bitflags", + "bitflags 1.3.2", "byteorder", "lazy_static", "proc-macro-error", @@ -2567,7 +2655,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" dependencies = [ "base64 0.13.1", - "bitflags", + "bitflags 1.3.2", "bytes", "headers-core", "http", @@ -3142,6 +3230,17 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi 0.3.1", + "rustix 0.38.4", + "windows-sys 0.48.0", +] + [[package]] name = "itertools" version = "0.10.5" @@ -3229,9 +3328,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.144" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libloading" @@ -3315,6 +3414,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" +[[package]] +name = "linux-raw-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" + [[package]] name = "lock_api" version = "0.4.9" @@ -3602,7 +3707,7 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "namada" -version = "0.19.0" +version = "0.20.0" dependencies = [ "async-trait", "bimap", @@ -3662,7 +3767,7 @@ dependencies = [ [[package]] name = "namada_core" -version = "0.19.0" +version = "0.20.0" dependencies = [ "ark-bls12-381", "ark-ec", @@ -3714,7 +3819,7 @@ dependencies = [ [[package]] name = "namada_ethereum_bridge" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "ethers", @@ -3734,7 +3839,7 @@ dependencies = [ [[package]] name = "namada_macros" -version = "0.19.0" +version = "0.20.0" dependencies = [ "proc-macro2", "quote", @@ -3743,7 +3848,7 @@ dependencies = [ [[package]] name = "namada_proof_of_stake" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "data-encoding", @@ -3757,7 +3862,7 @@ dependencies = [ [[package]] name = "namada_test_utils" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "namada_core", @@ -3766,19 +3871,23 @@ dependencies = [ [[package]] name = "namada_tests" -version = "0.19.0" +version = "0.20.0" dependencies = [ + "async-trait", "chrono", + "clap", "concat-idents", "derivative", "hyper", "ibc-relayer", "ibc-relayer-types", + "lazy_static", "namada", "namada_core", "namada_test_utils", "namada_tx_prelude", "namada_vp_prelude", + "num-traits", "prost", "regex", "serde_json", @@ -3792,7 +3901,7 @@ dependencies = [ [[package]] name = "namada_tx_prelude" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "masp_primitives", @@ -3806,7 +3915,7 @@ dependencies = [ [[package]] name = "namada_vm_env" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "masp_primitives", @@ -3815,7 +3924,7 @@ dependencies = [ [[package]] name = "namada_vp_prelude" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "namada_core", @@ -3828,7 +3937,7 @@ dependencies = [ [[package]] name = "namada_wasm" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "getrandom 0.2.9", @@ -4467,7 +4576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" dependencies = [ "bit-set", - "bitflags", + "bitflags 1.3.2", "byteorder", "lazy_static", "num-traits", @@ -4560,7 +4669,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63" dependencies = [ - "bitflags", + "bitflags 1.3.2", "memchr", "unicase", ] @@ -4694,7 +4803,7 @@ version = "10.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -4743,7 +4852,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -4752,7 +4861,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -4815,7 +4924,7 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", "mach", "winapi", @@ -5015,11 +5124,24 @@ version = "0.37.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.3.7", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" +dependencies = [ + "bitflags 2.3.3", + "errno", + "libc", + "linux-raw-sys 0.4.3", "windows-sys 0.48.0", ] @@ -5310,7 +5432,7 @@ version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -5656,6 +5778,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "strum" version = "0.24.1" @@ -5773,7 +5901,7 @@ dependencies = [ "cfg-if 1.0.0", "fastrand", "redox_syscall 0.3.5", - "rustix", + "rustix 0.37.19", "windows-sys 0.45.0", ] @@ -6360,7 +6488,7 @@ dependencies = [ [[package]] name = "tx_template" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "getrandom 0.2.9", @@ -6474,6 +6602,12 @@ 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" @@ -6501,7 +6635,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vp_template" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "getrandom 0.2.9", diff --git a/wasm/checksums.json b/wasm/checksums.json index 1a77178f1b..f9ad3b8657 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,22 +1,21 @@ { - "tx_bond.wasm": "tx_bond.d1aea330d22cedce7f2c4c15a8da5c84e323f743e3932cfb3e9f5cc04067dc0d.wasm", - "tx_bridge_pool.wasm": "tx_bridge_pool.13fbe60b21c37a7122dec49f46d99b383e1eaf2c26a516fdc5b1db1072ce38a7.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.ddf35f2f9806d84fb3c9997f4760d67ad07b67b61946523960817a3576320e11.wasm", - "tx_ibc.wasm": "tx_ibc.1268bb1eba712d3bf6c90836cc94d0ea31d433441f32d42783df0ae1cca445a7.wasm", - "tx_init_account.wasm": "tx_init_account.bf607cb042cb82b747505d86b6d4367520b0cb3ef78224c2d7664755c1f2728b.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.65e92ec3a841e16a59ee85afe21c0786e94c73ee978f91148a0a0baccd6a73ac.wasm", - "tx_init_validator.wasm": "tx_init_validator.d84ed0638264a5c7a677484f2bc51aa577e44f1a0d5e08fd0ae2e6d11bbabf06.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.6f5f2a88ec50fc9bcf77e6127f69ad96fcb95e95c27f2dde7070e0493b589e73.wasm", - "tx_transfer.wasm": "tx_transfer.f4928ca3b4f55d08fee1387e717fabe498c5137af30b51e9f58e84751943ec3d.wasm", - "tx_unbond.wasm": "tx_unbond.e368b03d2e00450c8a5e4f6fcf181d6b53d48b88c1376ecf4d41889e0dd6f267.wasm", - "tx_unjail_validator.wasm": "tx_unjail_validator.0d7a8e680491322f7696cdb48ac00e3b4c1ba936441d5b6eafa73c6ec4a42459.wasm", - "tx_update_account.wasm": "tx_update_account.5d1d852a1ee627f9d493a81947d79c44b420fb27728e8bb6073839eb39099668.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.cf63831b4bac30f59fb65c79e6a0d71ed9b69a4c3331cb2cfd6d9cfae3c6f950.wasm", - "tx_withdraw.wasm": "tx_withdraw.820210f1dfcf8de320df453825baaea59eae5f02122808ee60598d8d8ba95633.wasm", - "vp_implicit.wasm": "vp_implicit.3741f7fa68bbc04ecd0521bc9b5915ceccbe48fda899a5fadec8b5315b13fde8.wasm", - "vp_masp.wasm": "vp_masp.8b89b3d4e7da89b96e1b74b5e594988bea9672e47e347bb1b3a2971fb958da6b.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.fc41c619b6b6fcb96c9b79947e5fc068babb4ec3e29b2dba6ef9a8df835ba58b.wasm", - "vp_token.wasm": "vp_token.142e25ef0f3dc2537a57d3240ca3cc4d58396ae883ebc01ddd15eb4463c11354.wasm", - "vp_user.wasm": "vp_user.a2c7c8d436c5bdfaf4a016fff297c7e574b03ccde75405668525b39a64968df3.wasm", - "vp_validator.wasm": "vp_validator.29dfa6a1e1715e2f96fcfd5462007ab2131b1c0febc0bfe432ae83b944361a71.wasm" + "tx_bond.wasm": "tx_bond.d4b01dca70c93b3b156eb968c400acfe4e876c38bf09c7493287f937970232f1.wasm", + "tx_bridge_pool.wasm": "tx_bridge_pool.f5af0e845d0c5a3a9996b6b6e7498be01495d9795c2df0b43524b678d161f898.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.7e62ab1927fad7009aaf16afe3d224c4a77b75115ff6816a83fdcbf7a7dc8aca.wasm", + "tx_ibc.wasm": "tx_ibc.4ca037cb00767e030705cfc0f9458481d0dbf8865f4af4812ae214a5cdc7405c.wasm", + "tx_init_account.wasm": "tx_init_account.259f69164305c505cf3b83eefe3fe659ca20e1788cc641ed0edf52862697aa2b.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.3c1ad25855e1a812b97aef13546ea2274e9d7ce1acb2b3aa26f8b329dd921184.wasm", + "tx_init_validator.wasm": "tx_init_validator.94a294f0823c850d5a50f2bc371a90d3afee816bf4e5cbc4306954c19f050893.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.e2b843c63ba1d9e32de556e54b9bfa2ba396290db8be316a046e4edaab49d25b.wasm", + "tx_transfer.wasm": "tx_transfer.a428d1bcc86b84b5cb5833c17eb58a46c8f6ca1e880a5a8a9f67e2a05e1a2719.wasm", + "tx_unbond.wasm": "tx_unbond.1c058939a0c9cfe153af8551af597e906521d61a3d10a48fb0df3b798358a379.wasm", + "tx_unjail_validator.wasm": "tx_unjail_validator.e692dedfbe209e5e4a08eabfe6e090118859c757480e305e10b427c5b95a58dd.wasm", + "tx_update_account.wasm": "tx_update_account.331d191581746d97d091791aa86a1f4b234299d27599d81b693655afcca5e3b3.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.68a81a99f5ee26c00c67b36d8e1f53c265e77ad3f1424a1dee196517f349851c.wasm", + "tx_withdraw.wasm": "tx_withdraw.c047d42440e926950eb80dd202682be9edd789d63cd35b7af218ae7f9127d7bf.wasm", + "vp_implicit.wasm": "vp_implicit.8ea0d256c2a63b5dd33366006db7cc3c30d692ade54781aafb7f8c3b8bab2cb9.wasm", + "vp_masp.wasm": "vp_masp.8f73a9b24bcbfee925da78cb62172a56fff0d921f8deeea5b868bf342e2886ac.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.7d371f0b22e095057b4fdde2716b1d71de9944c711585e36e770a5b3913c0b65.wasm", + "vp_user.wasm": "vp_user.745cb048ea5919250497fcebea716576ca8ca843367d91c68969abe733355db7.wasm", + "vp_validator.wasm": "vp_validator.d770bbcdc6b2c5701fa07462d2b153c1122b110bedf9f436952c6328331bbcea.wasm" } \ No newline at end of file diff --git a/wasm/tx_template/Cargo.toml b/wasm/tx_template/Cargo.toml index 385b45d4de..15aacd618c 100644 --- a/wasm/tx_template/Cargo.toml +++ b/wasm/tx_template/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "tx_template" resolver = "2" -version = "0.19.0" +version = "0.20.0" [lib] crate-type = ["cdylib"] diff --git a/wasm/vp_template/Cargo.toml b/wasm/vp_template/Cargo.toml index b98b74b7c6..634ad3ee1d 100644 --- a/wasm/vp_template/Cargo.toml +++ b/wasm/vp_template/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "vp_template" resolver = "2" -version = "0.19.0" +version = "0.20.0" [lib] crate-type = ["cdylib"] diff --git a/wasm/wasm_source/Cargo.toml b/wasm/wasm_source/Cargo.toml index 45f6ce13f4..8fd5f1ba82 100644 --- a/wasm/wasm_source/Cargo.toml +++ b/wasm/wasm_source/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "namada_wasm" resolver = "2" -version = "0.19.0" +version = "0.20.0" [lib] crate-type = ["cdylib"] diff --git a/wasm/wasm_source/Makefile b/wasm/wasm_source/Makefile index 9e43f77f9c..a88065355f 100644 --- a/wasm/wasm_source/Makefile +++ b/wasm/wasm_source/Makefile @@ -22,7 +22,6 @@ wasms += tx_withdraw wasms += vp_implicit wasms += vp_masp wasms += vp_testnet_faucet -wasms += vp_token wasms += vp_user wasms += vp_validator diff --git a/wasm/wasm_source/src/lib.rs b/wasm/wasm_source/src/lib.rs index b59b7adf23..ce5b1b1561 100644 --- a/wasm/wasm_source/src/lib.rs +++ b/wasm/wasm_source/src/lib.rs @@ -33,8 +33,6 @@ pub mod vp_implicit; pub mod vp_masp; #[cfg(feature = "vp_testnet_faucet")] pub mod vp_testnet_faucet; -#[cfg(feature = "vp_token")] -pub mod vp_token; #[cfg(feature = "vp_user")] pub mod vp_user; #[cfg(feature = "vp_validator")] diff --git a/wasm/wasm_source/src/tx_bond.rs b/wasm/wasm_source/src/tx_bond.rs index 1004609744..ec104e3051 100644 --- a/wasm/wasm_source/src/tx_bond.rs +++ b/wasm/wasm_source/src/tx_bond.rs @@ -15,7 +15,7 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { #[cfg(test)] mod tests { - use std::collections::HashSet; + use std::collections::BTreeSet; use namada::ledger::pos::{GenesisValidator, PosParams, PosVP}; use namada::proof_of_stake::{ @@ -102,7 +102,7 @@ mod tests { // Ensure that the bond's source has enough tokens for the bond let target = bond.source.as_ref().unwrap_or(&bond.validator); let native_token = tx_env.wl_storage.storage.native_token.clone(); - tx_env.credit_tokens(target, &native_token, None, bond.amount); + tx_env.credit_tokens(target, &native_token, bond.amount); native_token }); @@ -132,7 +132,7 @@ mod tests { // Read some data before the tx is executed let mut epoched_total_stake_pre: Vec = Vec::new(); let mut epoched_validator_stake_pre: Vec = Vec::new(); - let mut epoched_validator_set_pre: Vec> = + let mut epoched_validator_set_pre: Vec> = Vec::new(); for epoch in 0..=pos_params.unbonding_len { @@ -163,7 +163,7 @@ mod tests { // Read the data after the tx is executed. let mut epoched_total_stake_post: Vec = Vec::new(); let mut epoched_validator_stake_post: Vec = Vec::new(); - let mut epoched_validator_set_post: Vec> = + let mut epoched_validator_set_post: Vec> = Vec::new(); println!("\nFILLING POST STATE\n"); diff --git a/wasm/wasm_source/src/tx_bridge_pool.rs b/wasm/wasm_source/src/tx_bridge_pool.rs index 001fdcffcf..bf73e83f7d 100644 --- a/wasm/wasm_source/src/tx_bridge_pool.rs +++ b/wasm/wasm_source/src/tx_bridge_pool.rs @@ -19,7 +19,6 @@ fn apply_tx(ctx: &mut Ctx, signed: Tx) -> TxResult { payer, &bridge_pool::BRIDGE_POOL_ADDRESS, &nam_addr, - None, amount.native_denominated(), &None, &None, @@ -39,7 +38,6 @@ fn apply_tx(ctx: &mut Ctx, signed: Tx) -> TxResult { sender, ð_bridge::ADDRESS, &nam_addr, - None, amount.native_denominated(), &None, &None, @@ -47,19 +45,13 @@ fn apply_tx(ctx: &mut Ctx, signed: Tx) -> TxResult { )?; } else { // Otherwise we escrow ERC20 tokens. - let sub_prefix = Some(wrapped_erc20s::sub_prefix(&asset)); - let amount = amount.denominated( - ð_bridge::ADDRESS, - sub_prefix.as_ref(), - ctx, - )?; + let token = wrapped_erc20s::token(&asset); token::transfer( ctx, sender, &bridge_pool::BRIDGE_POOL_ADDRESS, - ð_bridge::ADDRESS, - sub_prefix, - amount, + &token, + amount.native_denominated(), &None, &None, &None, diff --git a/wasm/wasm_source/src/tx_transfer.rs b/wasm/wasm_source/src/tx_transfer.rs index d2e3dc314f..33899640c4 100644 --- a/wasm/wasm_source/src/tx_transfer.rs +++ b/wasm/wasm_source/src/tx_transfer.rs @@ -15,7 +15,6 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { source, target, token, - sub_prefix, amount, key, shielded: shielded_hash, @@ -34,7 +33,6 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { &source, &target, &token, - sub_prefix, amount, &key, &shielded_hash, diff --git a/wasm/wasm_source/src/tx_unbond.rs b/wasm/wasm_source/src/tx_unbond.rs index e41fce38f8..efe21cf736 100644 --- a/wasm/wasm_source/src/tx_unbond.rs +++ b/wasm/wasm_source/src/tx_unbond.rs @@ -15,7 +15,7 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { #[cfg(test)] mod tests { - use std::collections::HashSet; + use std::collections::BTreeSet; use namada::ledger::pos::{GenesisValidator, PosParams, PosVP}; use namada::proof_of_stake::types::WeightedValidator; @@ -109,12 +109,7 @@ mod tests { // bond first. // First, credit the bond's source with the initial stake, // before we initialize the bond below - tx_env.credit_tokens( - source, - &native_token, - None, - initial_stake, - ); + tx_env.credit_tokens(source, &native_token, initial_stake); } native_token }); @@ -163,7 +158,7 @@ mod tests { let mut epoched_total_stake_pre: Vec = Vec::new(); let mut epoched_validator_stake_pre: Vec = Vec::new(); let mut epoched_bonds_pre: Vec> = Vec::new(); - let mut epoched_validator_set_pre: Vec> = + let mut epoched_validator_set_pre: Vec> = Vec::new(); for epoch in 0..=pos_params.unbonding_len { diff --git a/wasm/wasm_source/src/tx_withdraw.rs b/wasm/wasm_source/src/tx_withdraw.rs index 5622d25c4a..7325e81473 100644 --- a/wasm/wasm_source/src/tx_withdraw.rs +++ b/wasm/wasm_source/src/tx_withdraw.rs @@ -113,12 +113,7 @@ mod tests { // bond first. // First, credit the bond's source with the initial stake, // before we initialize the bond below - tx_env.credit_tokens( - source, - &native_token, - None, - initial_stake, - ); + tx_env.credit_tokens(source, &native_token, initial_stake); } native_token }); diff --git a/wasm/wasm_source/src/vp_implicit.rs b/wasm/wasm_source/src/vp_implicit.rs index f4c8e959e2..3eaa7ad056 100644 --- a/wasm/wasm_source/src/vp_implicit.rs +++ b/wasm/wasm_source/src/vp_implicit.rs @@ -32,10 +32,6 @@ impl<'a> From<&'a storage::Key> for KeyType<'a> { Self::Pk(address) } else if let Some([_, owner]) = token::is_any_token_balance_key(key) { Self::Token { owner } - } else if let Some((_, [_, owner])) = - token::is_any_multitoken_balance_key(key) - { - Self::Token { owner } } else if proof_of_stake::is_pos_key(key) { Self::PoS } else if gov_storage::is_vote_key(key) { @@ -344,14 +340,13 @@ mod tests { storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); // Credit the tokens to the source before running the transaction to be // able to transfer from it - tx_env.credit_tokens(&source, &token, None, amount); + tx_env.credit_tokens(&source, &token, amount); let amount = token::DenominatedAmount { amount, @@ -365,7 +360,6 @@ mod tests { &source, address, &token, - None, amount, &None, &None, @@ -433,15 +427,15 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); + // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |_address| { // Bond the tokens, then unbond some of them @@ -514,12 +508,11 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); @@ -578,12 +571,11 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); @@ -601,7 +593,6 @@ mod tests { address, &target, &token, - None, amount, &None, &None, @@ -644,12 +635,11 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); @@ -666,7 +656,6 @@ mod tests { address, &target, &token, - None, amount, &None, &None, @@ -720,12 +709,11 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&source, &token, None, amount); + tx_env.credit_tokens(&source, &token, amount); // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); @@ -744,7 +732,6 @@ mod tests { &source, &target, &token, - None, amount, &None, &None, diff --git a/wasm/wasm_source/src/vp_masp.rs b/wasm/wasm_source/src/vp_masp.rs index 39952339ea..1e43d93a25 100644 --- a/wasm/wasm_source/src/vp_masp.rs +++ b/wasm/wasm_source/src/vp_masp.rs @@ -13,11 +13,10 @@ use ripemd::{Digest, Ripemd160}; fn asset_type_from_epoched_address( epoch: Epoch, token: &Address, - sub_prefix: String, denom: token::MaspDenom, ) -> AssetType { // Timestamp the chosen token with the current epoch - let token_bytes = (token, sub_prefix, denom, epoch.0) + let token_bytes = (token, denom, epoch.0) .try_to_vec() .expect("token should serialize"); // Generate the unique asset identifier from the unique token address @@ -63,19 +62,10 @@ fn valid_transfer_amount( fn convert_amount( epoch: Epoch, token: &Address, - sub_prefix: &Option, val: token::Amount, denom: token::MaspDenom, ) -> (AssetType, Amount) { - let asset_type = asset_type_from_epoched_address( - epoch, - token, - sub_prefix - .as_ref() - .map(|k| k.to_string()) - .unwrap_or_default(), - denom, - ); + let asset_type = asset_type_from_epoched_address(epoch, token, denom); // Combine the value and unit into one amount let amount = Amount::from_nonnegative(asset_type, denom.denominate(&val)) .expect("invalid value or asset type for amount"); @@ -127,7 +117,6 @@ fn validate_tx( let (_transp_asset, transp_amt) = convert_amount( ctx.get_block_epoch().unwrap(), &transfer.token, - &transfer.sub_prefix, transfer.amount.into(), denom, ); @@ -191,11 +180,6 @@ fn validate_tx( asset_type_from_epoched_address( ctx.get_block_epoch().unwrap(), &transfer.token, - transfer - .sub_prefix - .as_ref() - .map(|k| k.to_string()) - .unwrap_or_default(), denom, ); @@ -215,7 +199,6 @@ fn validate_tx( let (_transp_asset, transp_amt) = convert_amount( ctx.get_block_epoch().unwrap(), &transfer.token, - &transfer.sub_prefix, transfer.amount.amount, denom, ); diff --git a/wasm/wasm_source/src/vp_testnet_faucet.rs b/wasm/wasm_source/src/vp_testnet_faucet.rs index 0878298a55..8436bf9707 100644 --- a/wasm/wasm_source/src/vp_testnet_faucet.rs +++ b/wasm/wasm_source/src/vp_testnet_faucet.rs @@ -33,7 +33,7 @@ fn validate_tx( } for key in keys_changed.iter() { - let is_valid = if let Some([_, owner]) = + let is_valid = if let Some([token, owner]) = token::is_any_token_balance_key(key) { if owner == &addr { @@ -41,7 +41,17 @@ fn validate_tx( let post: token::Amount = ctx.read_post(key)?.unwrap_or_default(); let change = post.change() - pre.change(); - + let maybe_denom = + storage_api::token::read_denom(&ctx.pre(), token)?; + if maybe_denom.is_none() { + debug_log!( + "A denomination for token address {} does not exist \ + in storage", + token, + ); + return reject(); + } + let denom = maybe_denom.unwrap(); if !change.non_negative() { // Allow to withdraw without a sig if there's a valid PoW if ctx.has_valid_pow() { @@ -50,7 +60,10 @@ fn validate_tx( &ctx.pre(), &addr, )?; - change >= -max_free_debit.change() + + token::Amount::from_uint(change.abs(), 0).unwrap() + <= token::Amount::from_uint(max_free_debit, denom) + .unwrap() } else { debug_log!("No PoW solution, a signature is required"); // Debit without a solution has to signed @@ -146,7 +159,7 @@ mod tests { // Credit the tokens to the source before running the transaction to be // able to transfer from it - tx_env.credit_tokens(&source, &token, None, amount); + tx_env.credit_tokens(&source, &token, amount); let amount = token::DenominatedAmount { amount, @@ -161,7 +174,6 @@ mod tests { &source, address, &token, - None, amount, &None, &None, @@ -297,7 +309,7 @@ mod tests { let vp_owner = address::testing::established_address_1(); let difficulty = testnet_pow::Difficulty::try_new(0).unwrap(); let withdrawal_limit = token::Amount::from_uint(MAX_FREE_DEBIT as u64, 0).unwrap(); - testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit).unwrap(); + testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit.into()).unwrap(); let target = address::testing::established_address_2(); let token = address::nam(); @@ -308,7 +320,7 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); tx_env.commit_genesis(); let amount = token::DenominatedAmount { amount, @@ -318,7 +330,7 @@ mod tests { // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Apply transfer in a transaction - tx_host_env::token::transfer(tx::ctx(), address, &target, &token, None, amount, &None, &None, &None).unwrap(); + tx_host_env::token::transfer(tx::ctx(), address, &target, &token, amount, &None, &None, &None).unwrap(); }); let vp_env = vp_host_env::take(); @@ -342,7 +354,7 @@ mod tests { let vp_owner = address::testing::established_address_1(); let difficulty = testnet_pow::Difficulty::try_new(0).unwrap(); let withdrawal_limit = token::Amount::from_uint(MAX_FREE_DEBIT as u64, 0).unwrap(); - testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit).unwrap(); + testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit.into()).unwrap(); let target = address::testing::established_address_2(); let target_key = key::testing::keypair_1(); @@ -355,9 +367,9 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); // write the denomination of NAM into storage - storage_api::token::write_denom(&mut tx_env.wl_storage, &token, None, token::NATIVE_MAX_DECIMAL_PLACES.into()).unwrap(); + storage_api::token::write_denom(&mut tx_env.wl_storage, &token, token::NATIVE_MAX_DECIMAL_PLACES.into()).unwrap(); tx_env.commit_genesis(); // Construct a PoW solution like a client would @@ -377,7 +389,7 @@ mod tests { let valid = solution.validate(tx::ctx(), address, target.clone()).unwrap(); assert!(valid); // Apply transfer in a transaction - tx_host_env::token::transfer(tx::ctx(), address, &target, &token, None, amount, &None, &None, &None).unwrap(); + tx_host_env::token::transfer(tx::ctx(), address, &target, &token, amount, &None, &None, &None).unwrap(); }); let mut vp_env = vp_host_env::take(); @@ -408,7 +420,7 @@ mod tests { // Init the VP let difficulty = testnet_pow::Difficulty::try_new(0).unwrap(); let withdrawal_limit = token::Amount::from_uint(MAX_FREE_DEBIT as u64, 0).unwrap(); - testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit).unwrap(); + testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit.into()).unwrap(); let keypair = key::testing::keypair_1(); let public_key = &keypair.ref_to(); diff --git a/wasm/wasm_source/src/vp_token.rs b/wasm/wasm_source/src/vp_token.rs deleted file mode 100644 index 29b639bd56..0000000000 --- a/wasm/wasm_source/src/vp_token.rs +++ /dev/null @@ -1,290 +0,0 @@ -//! A VP for a fungible token. Enforces that the total supply is unchanged in a -//! transaction that moves balance(s). - -use std::collections::BTreeSet; - -use namada_vp_prelude::address::{self, Address, InternalAddress}; -use namada_vp_prelude::storage::KeySeg; -use namada_vp_prelude::{storage, token, *}; - -#[validity_predicate] -fn validate_tx( - ctx: &Ctx, - tx_data: Tx, - addr: Address, - keys_changed: BTreeSet, - verifiers: BTreeSet
, -) -> VpResult { - debug_log!( - "validate_tx called with token addr: {}, key_changed: {:?}, \ - verifiers: {:?}", - addr, - keys_changed, - verifiers - ); - - if !is_valid_tx(ctx, &tx_data)? { - return reject(); - } - - for key in keys_changed.iter() { - if key.is_validity_predicate().is_some() { - let vp_hash: Vec = ctx.read_bytes_post(key)?.unwrap(); - if !is_vp_whitelisted(ctx, &vp_hash)? { - return reject(); - } - } - } - - token_checks(ctx, &addr, &keys_changed, &verifiers) -} - -/// A token validity predicate checks that the total supply is preserved. -/// This implies that: -/// -/// - The value associated with the `total_supply` storage key may not change. -/// - For any balance changes, the total of outputs must be equal to the total -/// of inputs. -fn token_checks( - ctx: &Ctx, - token: &Address, - keys_touched: &BTreeSet, - verifiers: &BTreeSet
, -) -> VpResult { - let mut change = token::Change::default(); - for key in keys_touched.iter() { - let owner: Option<&Address> = token::is_balance_key(token, key) - .or_else(|| { - token::is_multitoken_balance_key(token, key).map(|a| a.1) - }); - - match owner { - None => { - if token::is_total_supply_key(key, token) { - // check if total supply is changed, which it should never - // be from a tx - let total_pre: token::Amount = ctx.read_pre(key)?.unwrap(); - let total_post: token::Amount = - ctx.read_post(key)?.unwrap(); - if total_pre != total_post { - return reject(); - } - } else if key.segments.get(0) == Some(&token.to_db_key()) { - // Unknown changes to this address space are disallowed, but - // unknown changes anywhere else are permitted - return reject(); - } - } - Some(owner) => { - // accumulate the change - let pre: token::Change = match owner { - Address::Internal(InternalAddress::IbcMint) => { - token::Change::maximum() - } - Address::Internal(InternalAddress::IbcBurn) => { - token::Change::default() - } - _ => ctx - .read_pre::(key)? - .unwrap_or_default() - .change(), - }; - let post: token::Change = match owner { - Address::Internal(InternalAddress::IbcMint) => ctx - .read_temp::(key)? - .map(|x| x.change()) - .unwrap_or_else(token::Change::maximum), - Address::Internal(InternalAddress::IbcBurn) => ctx - .read_temp::(key)? - .unwrap_or_default() - .change(), - _ => ctx - .read_post::(key)? - .unwrap_or_default() - .change(), - }; - let this_change = post - pre; - change += this_change; - // make sure that the spender approved the transaction - if !(this_change.non_negative() - || verifiers.contains(owner) - || *owner == address::masp()) - { - return reject(); - } - } - } - } - Ok(change.is_zero()) -} - -#[cfg(test)] -mod tests { - // Use this as `#[test]` annotation to enable logging - use namada::core::ledger::storage_api::token; - use namada::proto::Data; - use namada::types::transaction::TxType; - use namada_tests::log::test; - use namada_tests::tx::{self, TestTxEnv}; - use namada_tests::vp::*; - use namada_vp_prelude::storage_api::StorageWrite; - - use super::*; - - #[test] - fn test_transfer_inputs_eq_outputs_is_accepted() { - // Initialize a tx environment - let mut tx_env = TestTxEnv::default(); - let token = address::nam(); - let src = address::testing::established_address_1(); - let dest = address::testing::established_address_2(); - let total_supply = - token::Amount::from_uint(10_098_123, 0).expect("Test failed"); - - // Spawn the accounts to be able to modify their storage - tx_env.spawn_accounts([&token, &src, &dest]); - token::credit_tokens( - &mut tx_env.wl_storage, - &token, - &src, - total_supply, - ) - .unwrap(); - // Commit the initial state - tx_env.commit_tx_and_block(); - - // Initialize VP environment from a transaction - vp_host_env::init_from_tx(token.clone(), tx_env, |_address| { - // Apply a transfer - - let amount = token::Amount::from_uint(100, 0).expect("Test failed"); - token::transfer(tx::ctx(), &token, &src, &dest, amount).unwrap(); - }); - - let vp_env = vp_host_env::take(); - let mut tx_data = Tx::new(TxType::Raw); - tx_data.set_data(Data::new(vec![])); - let keys_changed: BTreeSet = - vp_env.all_touched_storage_keys(); - let verifiers = vp_env.get_verifiers(); - vp_host_env::set(vp_env); - - assert!( - validate_tx(&CTX, tx_data, token, keys_changed, verifiers).unwrap(), - "A transfer where inputs == outputs should be accepted" - ); - } - - #[test] - fn test_transfer_inputs_neq_outputs_is_rejected() { - // Initialize a tx environment - let mut tx_env = TestTxEnv::default(); - let token = address::nam(); - let src = address::testing::established_address_1(); - let dest = address::testing::established_address_2(); - let total_supply = - token::Amount::from_uint(10_098_123, 0).expect("Test failed"); - - // Spawn the accounts to be able to modify their storage - tx_env.spawn_accounts([&token, &src, &dest]); - token::credit_tokens( - &mut tx_env.wl_storage, - &token, - &src, - total_supply, - ) - .unwrap(); - // Commit the initial state - tx_env.commit_tx_and_block(); - - // Initialize VP environment from a transaction - vp_host_env::init_from_tx(token.clone(), tx_env, |_address| { - // Apply a transfer - - let amount_in = - token::Amount::from_uint(100, 0).expect("Test failed"); - let amount_out = - token::Amount::from_uint(900, 0).expect("Test failed"); - - let src_key = token::balance_key(&token, &src); - let src_balance = - token::read_balance(tx::ctx(), &token, &src).unwrap(); - let new_src_balance = src_balance + amount_out; - let dest_key = token::balance_key(&token, &dest); - let dest_balance = - token::read_balance(tx::ctx(), &token, &dest).unwrap(); - let new_dest_balance = dest_balance + amount_in; - tx::ctx().write(&src_key, new_src_balance).unwrap(); - tx::ctx().write(&dest_key, new_dest_balance).unwrap(); - }); - - let vp_env = vp_host_env::take(); - let mut tx_data = Tx::new(TxType::Raw); - tx_data.set_data(Data::new(vec![])); - let keys_changed: BTreeSet = - vp_env.all_touched_storage_keys(); - let verifiers = vp_env.get_verifiers(); - vp_host_env::set(vp_env); - - assert!( - !validate_tx(&CTX, tx_data, token, keys_changed, verifiers) - .unwrap(), - "A transfer where inputs != outputs should be rejected" - ); - } - - #[test] - fn test_total_supply_change_is_rejected() { - // Initialize a tx environment - let mut tx_env = TestTxEnv::default(); - let token = address::nam(); - let owner = address::testing::established_address_1(); - let total_supply = - token::Amount::from_uint(10_098_123, 0).expect("Test failed"); - - // Spawn the accounts to be able to modify their storage - tx_env.spawn_accounts([&token, &owner]); - token::credit_tokens( - &mut tx_env.wl_storage, - &token, - &owner, - total_supply, - ) - .unwrap(); - // Commit the initial state - tx_env.commit_tx_and_block(); - - let total_supply_key = token::total_supply_key(&token); - - // Initialize VP environment from a transaction - vp_host_env::init_from_tx(token.clone(), tx_env, |_address| { - // Try to change total supply from a tx - - let current_supply = tx::ctx() - .read::(&total_supply_key) - .unwrap() - .unwrap_or_default(); - tx::ctx() - .write( - &total_supply_key, - current_supply - + token::Amount::from_uint(1, 0).expect("Test failed"), - ) - .unwrap(); - }); - - let vp_env = vp_host_env::take(); - let mut tx_data = Tx::new(TxType::Raw); - tx_data.set_data(Data::new(vec![])); - let keys_changed: BTreeSet = - vp_env.all_touched_storage_keys(); - let verifiers = vp_env.get_verifiers(); - vp_host_env::set(vp_env); - - assert!( - !validate_tx(&CTX, tx_data, token, keys_changed, verifiers) - .unwrap(), - "Change of a `total_supply` value should be rejected" - ); - } -} diff --git a/wasm/wasm_source/src/vp_user.rs b/wasm/wasm_source/src/vp_user.rs index f7523c6ae2..895c845336 100644 --- a/wasm/wasm_source/src/vp_user.rs +++ b/wasm/wasm_source/src/vp_user.rs @@ -26,10 +26,6 @@ impl<'a> From<&'a storage::Key> for KeyType<'a> { fn from(key: &'a storage::Key) -> KeyType<'a> { if let Some([_, owner]) = token::is_any_token_balance_key(key) { Self::Token { owner } - } else if let Some((_, [_, owner])) = - token::is_any_multitoken_balance_key(key) - { - Self::Token { owner } } else if proof_of_stake::is_pos_key(key) { Self::PoS } else if gov_storage::is_vote_key(key) { @@ -230,12 +226,11 @@ mod tests { // Credit the tokens to the source before running the transaction to be // able to transfer from it - tx_env.credit_tokens(&source, &token, None, amount); + tx_env.credit_tokens(&source, &token, amount); // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); @@ -252,7 +247,6 @@ mod tests { &source, address, &token, - None, amount, &None, &None, @@ -291,14 +285,13 @@ mod tests { storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); let amount = token::DenominatedAmount { amount, @@ -312,7 +305,6 @@ mod tests { address, &target, &token, - None, amount, &None, &None, @@ -353,12 +345,11 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); @@ -376,7 +367,6 @@ mod tests { address, &target, &token, - None, amount, &None, &None, @@ -453,14 +443,13 @@ mod tests { storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |_address| { @@ -536,14 +525,13 @@ mod tests { storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |_address| { @@ -596,7 +584,7 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&source, &token, None, amount); + tx_env.credit_tokens(&source, &token, amount); let amount = token::DenominatedAmount { amount, @@ -612,7 +600,6 @@ mod tests { &source, &target, &token, - None, amount, &None, &None, diff --git a/wasm/wasm_source/src/vp_validator.rs b/wasm/wasm_source/src/vp_validator.rs index 3b5647b291..7d16d84380 100644 --- a/wasm/wasm_source/src/vp_validator.rs +++ b/wasm/wasm_source/src/vp_validator.rs @@ -28,10 +28,6 @@ impl<'a> From<&'a storage::Key> for KeyType<'a> { fn from(key: &'a storage::Key) -> KeyType<'a> { if let Some([_, owner]) = token::is_any_token_balance_key(key) { Self::Token { owner } - } else if let Some((_, [_, owner])) = - token::is_any_multitoken_balance_key(key) - { - Self::Token { owner } } else if proof_of_stake::is_pos_key(key) { Self::PoS } else if gov_storage::is_vote_key(key) { @@ -237,12 +233,11 @@ mod tests { // Credit the tokens to the source before running the transaction to be // able to transfer from it - tx_env.credit_tokens(&source, &token, None, amount); + tx_env.credit_tokens(&source, &token, amount); // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); @@ -259,7 +254,6 @@ mod tests { &source, address, &token, - None, amount, &None, &None, @@ -297,7 +291,7 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); let amount = token::DenominatedAmount { amount, denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), @@ -306,7 +300,6 @@ mod tests { storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); @@ -319,7 +312,6 @@ mod tests { address, &target, &token, - None, amount, &None, &None, @@ -360,12 +352,11 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); @@ -383,7 +374,6 @@ mod tests { address, &target, &token, - None, amount, &None, &None, @@ -459,12 +449,11 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); @@ -547,12 +536,11 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); @@ -614,7 +602,7 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&source, &token, None, amount); + tx_env.credit_tokens(&source, &token, amount); let amount = token::DenominatedAmount { amount, denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), @@ -629,7 +617,6 @@ mod tests { &source, &target, &token, - None, amount, &None, &None, diff --git a/wasm_for_tests/tx_memory_limit.wasm b/wasm_for_tests/tx_memory_limit.wasm index 67bd369c09..186c290345 100755 Binary files a/wasm_for_tests/tx_memory_limit.wasm and b/wasm_for_tests/tx_memory_limit.wasm differ diff --git a/wasm_for_tests/tx_no_op.wasm b/wasm_for_tests/tx_no_op.wasm index a471438144..a0d7392cbf 100755 Binary files a/wasm_for_tests/tx_no_op.wasm and b/wasm_for_tests/tx_no_op.wasm differ diff --git a/wasm_for_tests/tx_proposal_code.wasm b/wasm_for_tests/tx_proposal_code.wasm index 3f6190c752..e45bdb6190 100755 Binary files a/wasm_for_tests/tx_proposal_code.wasm and b/wasm_for_tests/tx_proposal_code.wasm differ diff --git a/wasm_for_tests/tx_read_storage_key.wasm b/wasm_for_tests/tx_read_storage_key.wasm index d34053bf29..580ebc7366 100755 Binary files a/wasm_for_tests/tx_read_storage_key.wasm and b/wasm_for_tests/tx_read_storage_key.wasm differ diff --git a/wasm_for_tests/tx_write.wasm b/wasm_for_tests/tx_write.wasm index 632d99b37d..a955492503 100755 Binary files a/wasm_for_tests/tx_write.wasm and b/wasm_for_tests/tx_write.wasm differ diff --git a/wasm_for_tests/tx_write_storage_key.wasm b/wasm_for_tests/tx_write_storage_key.wasm index a0fb758ae9..5d600d185f 100755 Binary files a/wasm_for_tests/tx_write_storage_key.wasm and b/wasm_for_tests/tx_write_storage_key.wasm differ diff --git a/wasm_for_tests/vp_always_false.wasm b/wasm_for_tests/vp_always_false.wasm index 751f4a41d9..b7515747c6 100755 Binary files a/wasm_for_tests/vp_always_false.wasm and b/wasm_for_tests/vp_always_false.wasm differ diff --git a/wasm_for_tests/vp_always_true.wasm b/wasm_for_tests/vp_always_true.wasm index 656dca4d27..5c3df8e85e 100755 Binary files a/wasm_for_tests/vp_always_true.wasm and b/wasm_for_tests/vp_always_true.wasm differ diff --git a/wasm_for_tests/vp_eval.wasm b/wasm_for_tests/vp_eval.wasm index 07669c104e..b3c094a99e 100755 Binary files a/wasm_for_tests/vp_eval.wasm and b/wasm_for_tests/vp_eval.wasm differ diff --git a/wasm_for_tests/vp_memory_limit.wasm b/wasm_for_tests/vp_memory_limit.wasm index 13bafdbbc7..9632de221e 100755 Binary files a/wasm_for_tests/vp_memory_limit.wasm and b/wasm_for_tests/vp_memory_limit.wasm differ diff --git a/wasm_for_tests/vp_read_storage_key.wasm b/wasm_for_tests/vp_read_storage_key.wasm index 866f9db307..9f8a5008c7 100755 Binary files a/wasm_for_tests/vp_read_storage_key.wasm and b/wasm_for_tests/vp_read_storage_key.wasm differ diff --git a/wasm_for_tests/wasm_source/Cargo.lock b/wasm_for_tests/wasm_source/Cargo.lock index 19cdba1a22..c145c9062f 100644 --- a/wasm_for_tests/wasm_source/Cargo.lock +++ b/wasm_for_tests/wasm_source/Cargo.lock @@ -89,6 +89,55 @@ 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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +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.71" @@ -315,7 +364,7 @@ checksum = "f8175979259124331c1d7bf6586ee7e0da434155e4b2d48ec2c8386281d8df39" dependencies = [ "async-trait", "axum-core", - "bitflags", + "bitflags 1.3.2", "bytes", "futures-util", "http", @@ -511,6 +560,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "bitvec" version = "0.17.4" @@ -896,6 +951,33 @@ dependencies = [ "version_check", ] +[[package]] +name = "clap" +version = "4.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + [[package]] name = "clru" version = "0.5.0" @@ -957,6 +1039,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "concat-idents" version = "1.1.4" @@ -1467,7 +1555,7 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" dependencies = [ - "bitflags", + "bitflags 1.3.2", "byteorder", "lazy_static", "proc-macro-error", @@ -2567,7 +2655,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" dependencies = [ "base64 0.13.1", - "bitflags", + "bitflags 1.3.2", "bytes", "headers-core", "http", @@ -3142,6 +3230,17 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi 0.3.1", + "rustix 0.38.3", + "windows-sys 0.48.0", +] + [[package]] name = "itertools" version = "0.10.5" @@ -3229,9 +3328,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.144" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libloading" @@ -3315,6 +3414,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" +[[package]] +name = "linux-raw-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" + [[package]] name = "lock_api" version = "0.4.9" @@ -3602,7 +3707,7 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "namada" -version = "0.19.0" +version = "0.20.0" dependencies = [ "async-trait", "bimap", @@ -3662,7 +3767,7 @@ dependencies = [ [[package]] name = "namada_core" -version = "0.19.0" +version = "0.20.0" dependencies = [ "ark-bls12-381", "ark-ec", @@ -3714,7 +3819,7 @@ dependencies = [ [[package]] name = "namada_ethereum_bridge" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "ethers", @@ -3734,7 +3839,7 @@ dependencies = [ [[package]] name = "namada_macros" -version = "0.19.0" +version = "0.20.0" dependencies = [ "proc-macro2", "quote", @@ -3743,7 +3848,7 @@ dependencies = [ [[package]] name = "namada_proof_of_stake" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "data-encoding", @@ -3757,7 +3862,7 @@ dependencies = [ [[package]] name = "namada_test_utils" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "namada_core", @@ -3766,19 +3871,23 @@ dependencies = [ [[package]] name = "namada_tests" -version = "0.19.0" +version = "0.20.0" dependencies = [ + "async-trait", "chrono", + "clap", "concat-idents", "derivative", "hyper", "ibc-relayer", "ibc-relayer-types", + "lazy_static", "namada", "namada_core", "namada_test_utils", "namada_tx_prelude", "namada_vp_prelude", + "num-traits", "prost", "regex", "serde_json", @@ -3792,7 +3901,7 @@ dependencies = [ [[package]] name = "namada_tx_prelude" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "masp_primitives", @@ -3806,7 +3915,7 @@ dependencies = [ [[package]] name = "namada_vm_env" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "masp_primitives", @@ -3815,7 +3924,7 @@ dependencies = [ [[package]] name = "namada_vp_prelude" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "namada_core", @@ -3828,7 +3937,7 @@ dependencies = [ [[package]] name = "namada_wasm_for_tests" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "getrandom 0.2.9", @@ -4460,7 +4569,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" dependencies = [ "bit-set", - "bitflags", + "bitflags 1.3.2", "byteorder", "lazy_static", "num-traits", @@ -4553,7 +4662,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63" dependencies = [ - "bitflags", + "bitflags 1.3.2", "memchr", "unicase", ] @@ -4687,7 +4796,7 @@ version = "10.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -4736,7 +4845,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -4745,7 +4854,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -4808,7 +4917,7 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", "mach", "winapi", @@ -5008,11 +5117,24 @@ version = "0.37.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.3.7", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4" +dependencies = [ + "bitflags 2.3.3", + "errno", + "libc", + "linux-raw-sys 0.4.3", "windows-sys 0.48.0", ] @@ -5303,7 +5425,7 @@ version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -5649,6 +5771,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "strum" version = "0.24.1" @@ -5766,7 +5894,7 @@ dependencies = [ "cfg-if 1.0.0", "fastrand", "redox_syscall 0.3.5", - "rustix", + "rustix 0.37.19", "windows-sys 0.45.0", ] @@ -6456,6 +6584,12 @@ 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" diff --git a/wasm_for_tests/wasm_source/Cargo.toml b/wasm_for_tests/wasm_source/Cargo.toml index ebf94785ea..ac6fe1f981 100644 --- a/wasm_for_tests/wasm_source/Cargo.toml +++ b/wasm_for_tests/wasm_source/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "namada_wasm_for_tests" resolver = "2" -version = "0.19.0" +version = "0.20.0" [lib] crate-type = ["cdylib"] diff --git a/wasm_for_tests/wasm_source/src/lib.rs b/wasm_for_tests/wasm_source/src/lib.rs index a72a3c7f98..bc99e36551 100644 --- a/wasm_for_tests/wasm_source/src/lib.rs +++ b/wasm_for_tests/wasm_source/src/lib.rs @@ -16,7 +16,8 @@ pub mod main { #[transaction] fn apply_tx(_ctx: &mut Ctx, tx_data: Tx) -> TxResult { - let len = usize::try_from_slice(&tx_data.data().as_ref().unwrap()[..]).unwrap(); + let len = usize::try_from_slice(&tx_data.data().as_ref().unwrap()[..]) + .unwrap(); log_string(format!("allocate len {}", len)); let bytes: Vec = vec![6_u8; len]; // use the variable to prevent it from compiler optimizing it away @@ -51,7 +52,9 @@ pub mod main { #[transaction] fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { // Allocates a memory of size given from the `tx_data (usize)` - let key = storage::Key::try_from_slice(&tx_data.data().as_ref().unwrap()[..]).unwrap(); + let key = + storage::Key::try_from_slice(&tx_data.data().as_ref().unwrap()[..]) + .unwrap(); log_string(format!("key {}", key)); let _result: Vec = ctx.read(&key)?.unwrap(); Ok(()) @@ -64,7 +67,7 @@ pub mod main { use borsh::BorshDeserialize; use namada_test_utils::tx_data::TxWriteData; use namada_tx_prelude::{ - log_string, transaction, Ctx, StorageRead, StorageWrite, TxResult, Tx, + log_string, transaction, Ctx, StorageRead, StorageWrite, Tx, TxResult, }; const TX_NAME: &str = "tx_write"; @@ -173,8 +176,12 @@ pub mod main { _verifiers: BTreeSet
, ) -> VpResult { use validity_predicate::EvalVp; - let EvalVp { vp_code_hash, input }: EvalVp = - EvalVp::try_from_slice(&tx_data.data().as_ref().unwrap()[..]).unwrap(); + let EvalVp { + vp_code_hash, + input, + }: EvalVp = + EvalVp::try_from_slice(&tx_data.data().as_ref().unwrap()[..]) + .unwrap(); ctx.eval(vp_code_hash, input) } } @@ -193,7 +200,8 @@ pub mod main { _keys_changed: BTreeSet, _verifiers: BTreeSet
, ) -> VpResult { - let len = usize::try_from_slice(&tx_data.data().as_ref().unwrap()[..]).unwrap(); + let len = usize::try_from_slice(&tx_data.data().as_ref().unwrap()[..]) + .unwrap(); log_string(format!("allocate len {}", len)); let bytes: Vec = vec![6_u8; len]; // use the variable to prevent it from compiler optimizing it away @@ -217,7 +225,9 @@ pub mod main { _verifiers: BTreeSet
, ) -> VpResult { // Allocates a memory of size given from the `tx_data (usize)` - let key = storage::Key::try_from_slice(&tx_data.data().as_ref().unwrap()[..]).unwrap(); + let key = + storage::Key::try_from_slice(&tx_data.data().as_ref().unwrap()[..]) + .unwrap(); log_string(format!("key {}", key)); let _result: Vec = ctx.read_pre(&key)?.unwrap(); accept()