diff --git a/.dockerignore b/.dockerignore index d53ebfff61..1e084d8263 100644 --- a/.dockerignore +++ b/.dockerignore @@ -4,6 +4,7 @@ yarn.lock .github/ .gitignore dist/** +.git # dockerfiles are not needed inside the docker build Dockerfile diff --git a/.github/actions/performance-tests/art.yaml b/.github/actions/performance-tests/art.yaml index 806a4d6321..0424eb96a1 100644 --- a/.github/actions/performance-tests/art.yaml +++ b/.github/actions/performance-tests/art.yaml @@ -9,11 +9,10 @@ config: http: timeout: 60 plugins: + expect: {} + ensure: {} metrics-by-endpoint: useOnlyRequestNames: true - ensure: - - type: "failure" - threshold: 1 publish-metrics: - type: prometheus pushgateway: 'https://pushgateway-testnet.rpc.zetachain.com/-=CLOUDFLARE_UUID=-' @@ -26,6 +25,7 @@ config: - type: "html" filename: "artillery_report.html" logLevel: debug + scenarios: - name: web3_clientVersion flow: @@ -38,13 +38,13 @@ scenarios: params: [] capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" - name: web3_sha3 flow: @@ -58,13 +58,13 @@ scenarios: - "0x68656c6c6f20776f726c64" capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" - name: net_version flow: @@ -77,13 +77,13 @@ scenarios: params: [] capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" - name: net_listening flow: @@ -96,13 +96,16 @@ scenarios: params: [] capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" + - equals: + - "true" + - "{{ response.result }}" - name: net_peerCount flow: @@ -115,13 +118,13 @@ scenarios: params: [] capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" - name: eth_chainId flow: @@ -134,13 +137,13 @@ scenarios: params: [] capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" - name: eth_getStorageAt flow: @@ -154,13 +157,13 @@ scenarios: - "latest" capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" - name: eth_getTransactionByBlockHashAndIndex flow: @@ -171,16 +174,17 @@ scenarios: jsonrpc: "2.0" method: "eth_getTransactionByBlockHashAndIndex" params: + - "0xbd8bd16e5d4375ed4bab633c4fb8aca58fdf7ed6c340853b20cfa91ed9a3b9e1" - "0x0" capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" - name: eth_getTransactionByBlockNumberAndIndex flow: @@ -195,13 +199,13 @@ scenarios: - "0x0" capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" - name: eth_getUncleByBlockHashAndIndex flow: @@ -212,16 +216,17 @@ scenarios: jsonrpc: "2.0" method: "eth_getUncleByBlockHashAndIndex" params: + - "0xbd8bd16e5d4375ed4bab633c4fb8aca58fdf7ed6c340853b20cfa91ed9a3b9e1" - "0x0" capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" - name: eth_getUncleByBlockNumberAndIndex flow: @@ -236,13 +241,13 @@ scenarios: - "0x0" capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" - name: eth_newFilter flow: @@ -253,21 +258,19 @@ scenarios: jsonrpc: "2.0" method: "eth_newFilter" params: - - { - fromBlock: "latest", - toBlock: "latest", - address: "0x0", + - fromBlock: "latest" + toBlock: "latest" + address: "0x81eF14691E9ea2f711cf56a9f0889c49C5Fe995a" topics: [] - } capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" - name: eth_getFilterChanges flow: @@ -278,16 +281,16 @@ scenarios: jsonrpc: "2.0" method: "eth_getFilterChanges" params: - - "0x0" + - "0xbd8bd16e5d4375ed4bab633c4fb8aca58fdf7ed6c340853b20cfa91ed9a3b9e1" capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" - name: eth_getFilterLogs flow: @@ -298,16 +301,16 @@ scenarios: jsonrpc: "2.0" method: "eth_getFilterLogs" params: - - "0x0" + - "0xbd8bd16e5d4375ed4bab633c4fb8aca58fdf7ed6c340853b20cfa91ed9a3b9e1" capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" - name: eth_blockNumber flow: @@ -320,13 +323,13 @@ scenarios: params: [] capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" - name: eth_getBlockByHash flow: @@ -337,17 +340,17 @@ scenarios: jsonrpc: "2.0" method: "eth_getBlockByHash" params: - - "0x0" + - "0x8716674e0acdbe87bab4595ecd33436f8a1a1b32fcda721362fe98dfc764affe" - true capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" - name: eth_getBlockByNumber flow: @@ -362,13 +365,13 @@ scenarios: - true capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" - name: eth_getBlockTransactionCountByHash flow: @@ -379,16 +382,16 @@ scenarios: jsonrpc: "2.0" method: "eth_getBlockTransactionCountByHash" params: - - "0x0" + - "0x8716674e0acdbe87bab4595ecd33436f8a1a1b32fcda721362fe98dfc764affe" capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" - name: eth_getBlockTransactionCountByNumber flow: @@ -402,13 +405,13 @@ scenarios: - "latest" capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" - name: eth_getCode flow: @@ -419,17 +422,17 @@ scenarios: jsonrpc: "2.0" method: "eth_getCode" params: - - "0x0" + - "0x0cbe0dF132a6c6B4a2974Fa1b7Fb953CF0Cc798a" - "latest" capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" - name: eth_getTransactionByHash flow: @@ -440,16 +443,16 @@ scenarios: jsonrpc: "2.0" method: "eth_getTransactionByHash" params: - - "0x0" + - "0x1a363840b12fa22aae5920edf11e2c59dde5260ffe5c9bc108250fe8380f5e75" capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" - name: eth_getTransactionReceipt flow: @@ -460,16 +463,16 @@ scenarios: jsonrpc: "2.0" method: "eth_getTransactionReceipt" params: - - "0x0" + - "0x1a363840b12fa22aae5920edf11e2c59dde5260ffe5c9bc108250fe8380f5e75" capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" - name: eth_getLogs flow: @@ -480,19 +483,16 @@ scenarios: jsonrpc: "2.0" method: "eth_getLogs" params: - - fromBlock: "0x1" - toBlock: "latest" - address: "0x0" - topics: ["0x0"] + - address: "0x0cbe0dF132a6c6B4a2974Fa1b7Fb953CF0Cc798a" capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" - name: eth_getBalance flow: @@ -503,17 +503,17 @@ scenarios: jsonrpc: "2.0" method: "eth_getBalance" params: - - "0x0" + - "0x735b14BB79463307AAcBED86DAf3322B1e6226aB" - "latest" capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" - name: eth_estimateGas flow: @@ -524,13 +524,14 @@ scenarios: jsonrpc: "2.0" method: "eth_estimateGas" params: - - from: "0x0" + - from: "0x239e96c8f17C85c30100AC26F635Ea15f23E9c67" + to: "0x0cbe0dF132a6c6B4a2974Fa1b7Fb953CF0Cc798a" capture: - json: "$" - as: "response" + as: response ensure: - - statusCode: 200 - - statusCode: 201 - assert: - - subject: response.error - equals: null + - statusCode: + - 200 + - 201 + expect: + - notHasProperty: "{{ response.error }}" \ No newline at end of file diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index c3d413ee54..773775e816 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -89,7 +89,6 @@ jobs: core.setOutput('SOLANA_TESTS', true); core.setOutput('TON_TESTS', true); core.setOutput('V2_TESTS', true); // for v2 tests, TODO: remove this once we fully migrate to v2 (https://github.com/zeta-chain/node/issues/2627) - core.setOutput('V2_MIGRATION_TESTS', true); // for v2 tests, TODO: remove this once we fully migrate to v2 (https://github.com/zeta-chain/node/issues/2627) } else if (context.eventName === 'schedule') { core.setOutput('DEFAULT_TESTS', true); core.setOutput('UPGRADE_TESTS', true); @@ -102,7 +101,6 @@ jobs: core.setOutput('SOLANA_TESTS', true); core.setOutput('TON_TESTS', true); core.setOutput('V2_TESTS', true); // for v2 tests, TODO: remove this once we fully migrate to v2 (https://github.com/zeta-chain/node/issues/2627) - core.setOutput('V2_MIGRATION_TESTS', true); // for v2 tests, TODO: remove this once we fully migrate to v2 (https://github.com/zeta-chain/node/issues/2627) } else if (context.eventName === 'workflow_dispatch') { const makeTargets = context.payload.inputs['make-targets'].split(','); core.setOutput('DEFAULT_TESTS', makeTargets.includes('default-test')); diff --git a/.github/workflows/reusable-e2e.yml b/.github/workflows/reusable-e2e.yml index 1ef194667e..aaac140960 100644 --- a/.github/workflows/reusable-e2e.yml +++ b/.github/workflows/reusable-e2e.yml @@ -69,6 +69,14 @@ jobs: } skip-extraction: ${{ steps.restore-go-cache.outputs.cache-hit || github.event_name != 'push' }} + # this ensures that the version is consistent between cache build and make build + - name: Set version for cache + run: | + NODE_VERSION=$(./version.sh) + echo "NODE_VERSION=$NODE_VERSION" >> $GITHUB_ENV + NODE_COMMIT=$(git log -1 --format='%H') + echo "NODE_COMMIT=$NODE_COMMIT" >> $GITHUB_ENV + # build zetanode with cache options - name: Build zetanode for cache uses: docker/build-push-action@v6 @@ -83,6 +91,9 @@ jobs: cache-from: ${{ env.CACHE_FROM_CONFIG }} cache-to: ${{ github.event_name == 'push' && env.CACHE_TO_CONFIG || '' }} target: latest-runtime + build-args: | + NODE_VERSION=${{ env.NODE_VERSION }} + NODE_COMMIT=${{ env.NODE_COMMIT }} - name: Enable monitoring if: inputs.enable-monitoring diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index f76ee3d14b..fcffcf7855 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -15,8 +15,26 @@ jobs: env: SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} container: - image: semgrep/semgrep + image: ghcr.io/zeta-chain/semgrep-semgrep:1.90.0 + if: (github.actor != 'dependabot[bot]') steps: - - uses: actions/checkout@v4 - - run: semgrep ci + - uses: actions/checkout@v4 + - name: Checkout semgrep-utilities repo + uses: actions/checkout@v4 + with: + repository: zeta-chain/semgrep-utilities + path: semgrep-utilities + + # uses json for semgrep script for transformation in the next step + - run: semgrep ci --json --output semgrep-findings.json + + # transforms the the output from the above into a GHAS compatible SARIF + # SARIF output by "semgrep --sarif" doesn't integrate well with GHAS dashboard + # Example: the event name uses segmrep rules name/ID, severities are [error, warning, info], tags are a bit confusing) + - run: python semgrep-utilities/utilities/github-sarif-helper/src/semgrep-json-to-sarif.py --json semgrep-findings.json --sarif semgrep-github.sarif + + - name: Upload SARIF file for GitHub Advanced Security Dashboard + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: semgrep-github.sarif diff --git a/.gitignore b/.gitignore index e0f51f8d4a..3f8e6a6ff4 100644 --- a/.gitignore +++ b/.gitignore @@ -56,4 +56,4 @@ contrib/localnet/addresses.txt # Config for e2e tests e2e_conf* -contrib/localnet/scripts/extra-evm-chains.json \ No newline at end of file +contrib/localnet/scripts/zetaclient-config-overlay.json \ No newline at end of file diff --git a/Dockerfile-localnet b/Dockerfile-localnet index cfe5e19a48..0ec2408120 100644 --- a/Dockerfile-localnet +++ b/Dockerfile-localnet @@ -19,9 +19,13 @@ COPY go.sum . RUN go mod download COPY version.sh . COPY --exclude=*.sh --exclude=*.md --exclude=*.yml . . +ARG NODE_VERSION +ARG NODE_COMMIT -RUN --mount=type=cache,target="/root/.cache/go-build" make install -RUN --mount=type=cache,target="/root/.cache/go-build" make install-zetae2e +RUN --mount=type=cache,target="/root/.cache/go-build" \ + NODE_VERSION=${NODE_VERSION} \ + NODE_COMMIT=${NODE_COMMIT} \ + make install install-zetae2e FROM ghcr.io/zeta-chain/golang:1.22.5-bookworm AS cosmovisor-build RUN go install cosmossdk.io/tools/cosmovisor/cmd/cosmovisor@v1.6.0 diff --git a/Makefile b/Makefile index f4aa4f5e47..f1e56c2953 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ .PHONY: build PACKAGE_NAME := github.com/zeta-chain/node -VERSION := $(shell ./version.sh) -COMMIT := $(shell [ -z "${COMMIT_ID}" ] && git log -1 --format='%H' || echo ${COMMIT_ID} ) +NODE_VERSION := $(shell ./version.sh) +NODE_COMMIT := $(shell [ -z "${NODE_COMMIT}" ] && git log -1 --format='%H' || echo ${NODE_COMMIT} ) BUILDTIME := $(shell date -u +"%Y%m%d.%H%M%S" ) DOCKER ?= docker # allow setting of NODE_COMPOSE_ARGS to pass additional args to docker compose @@ -17,11 +17,11 @@ GOPATH ?= '$(HOME)/go' ldflags = -X github.com/cosmos/cosmos-sdk/version.Name=zetacore \ -X github.com/cosmos/cosmos-sdk/version.ServerName=zetacored \ -X github.com/cosmos/cosmos-sdk/version.ClientName=zetaclientd \ - -X github.com/cosmos/cosmos-sdk/version.Version=$(VERSION) \ - -X github.com/cosmos/cosmos-sdk/version.Commit=$(COMMIT) \ + -X github.com/cosmos/cosmos-sdk/version.Version=$(NODE_VERSION) \ + -X github.com/cosmos/cosmos-sdk/version.Commit=$(NODE_COMMIT) \ -X github.com/zeta-chain/node/pkg/constant.Name=zetacored \ - -X github.com/zeta-chain/node/pkg/constant.Version=$(VERSION) \ - -X github.com/zeta-chain/node/pkg/constant.CommitHash=$(COMMIT) \ + -X github.com/zeta-chain/node/pkg/constant.Version=$(NODE_VERSION) \ + -X github.com/zeta-chain/node/pkg/constant.CommitHash=$(NODE_COMMIT) \ -X github.com/zeta-chain/node/pkg/constant.BuildTime=$(BUILDTIME) \ -X github.com/cosmos/cosmos-sdk/types.DBBackend=pebbledb @@ -233,7 +233,7 @@ stop-localnet: zetanode: @echo "Building zetanode" - $(DOCKER) build -t zetanode --target latest-runtime -f ./Dockerfile-localnet . + $(DOCKER) build -t zetanode --build-arg NODE_VERSION=$(NODE_VERSION) --build-arg NODE_COMMIT=$(NODE_COMMIT) --target latest-runtime -f ./Dockerfile-localnet . $(DOCKER) build -t orchestrator -f contrib/localnet/orchestrator/Dockerfile.fastbuild . .PHONY: zetanode @@ -296,15 +296,24 @@ start-v2-test: zetanode ############################################################################### # build from source only if requested +# NODE_VERSION and NODE_COMMIT must be set as old-runtime depends on lastest-runtime ifdef UPGRADE_TEST_FROM_SOURCE zetanode-upgrade: zetanode @echo "Building zetanode-upgrade from source" - $(DOCKER) build -t zetanode:old -f Dockerfile-localnet --target old-runtime-source --build-arg OLD_VERSION='release/v19' . + $(DOCKER) build -t zetanode:old -f Dockerfile-localnet --target old-runtime-source \ + --build-arg OLD_VERSION='release/v20' \ + --build-arg NODE_VERSION=$(NODE_VERSION) \ + --build-arg NODE_COMMIT=$(NODE_COMMIT) + . .PHONY: zetanode-upgrade else zetanode-upgrade: zetanode @echo "Building zetanode-upgrade from binaries" - $(DOCKER) build -t zetanode:old -f Dockerfile-localnet --target old-runtime --build-arg OLD_VERSION='https://github.com/zeta-chain/node/releases/download/v19.1.1' . + $(DOCKER) build -t zetanode:old -f Dockerfile-localnet --target old-runtime \ + --build-arg OLD_VERSION='https://github.com/zeta-chain/node/releases/download/v20.0.2' \ + --build-arg NODE_VERSION=$(NODE_VERSION) \ + --build-arg NODE_COMMIT=$(NODE_COMMIT) \ + . .PHONY: zetanode-upgrade endif diff --git a/app/app.go b/app/app.go index 971e6ce710..1e64321f21 100644 --- a/app/app.go +++ b/app/app.go @@ -5,6 +5,7 @@ import ( "io" "net/http" "os" + "runtime/debug" "time" cosmoserrors "cosmossdk.io/errors" @@ -194,7 +195,7 @@ var maccPerms = map[string][]string{ fungibletypes.ModuleName: {authtypes.Minter, authtypes.Burner}, emissionstypes.ModuleName: nil, emissionstypes.UndistributedObserverRewardsPool: nil, - emissionstypes.UndistributedTssRewardsPool: nil, + emissionstypes.UndistributedTSSRewardsPool: nil, } // module accounts that are NOT allowed to receive tokens @@ -857,6 +858,26 @@ func (app *App) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.Respo // InitChainer application update at chain initialization func (app *App) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { + // InitChainErrorMessage is the error message displayed when trying to sync testnet or mainnet from block 1 using the latest binary. + const InitChainErrorMessage = ` +Unable to sync testnet or mainnet from block 1 using the latest version. +Please use a snapshot to sync your node. +Refer to the documentation for more information: +https://www.zetachain.com/docs/nodes/start-here/syncing/` + + // The defer is used to catch panics during InitChain + // and display a more meaningful message for people trying to sync a node from block 1 using the latest binary. + // We exit the process after displaying the message as we do not need to start a node with empty state. + + defer func() { + if r := recover(); r != nil { + ctx.Logger().Error("panic occurred during InitGenesis", "error", r) + ctx.Logger().Debug("stack trace", "stack", string(debug.Stack())) + ctx.Logger(). + Info(InitChainErrorMessage) + os.Exit(1) + } + }() var genesisState GenesisState if err := tmjson.Unmarshal(req.AppStateBytes, &genesisState); err != nil { panic(err) diff --git a/changelog.md b/changelog.md index ecd222d1ef..b666c26f7d 100644 --- a/changelog.md +++ b/changelog.md @@ -16,6 +16,11 @@ * [2911](https://github.com/zeta-chain/node/pull/2911) - add chain static information for btc testnet4 * [2904](https://github.com/zeta-chain/node/pull/2904) - integrate authenticated calls smart contract functionality into protocol * [2919](https://github.com/zeta-chain/node/pull/2919) - add inbound sender to revert context +* [2957](https://github.com/zeta-chain/node/pull/2957) - enable Bitcoin inscription support on testnet +* [2896](https://github.com/zeta-chain/node/pull/2896) - add TON inbound observation +* [2987](https://github.com/zeta-chain/node/pull/2987) - add non-EVM standard inbound memo package +* [2979](https://github.com/zeta-chain/node/pull/2979) - add fungible keeper ability to lock/unlock ZRC20 tokens +* [3012](https://github.com/zeta-chain/node/pull/3012) - integrate authenticated calls erc20 smart contract functionality into protocol ### Refactor @@ -25,6 +30,7 @@ * [2826](https://github.com/zeta-chain/node/pull/2826) - remove unused code from emissions module and add new parameter for fixed block reward amount * [2890](https://github.com/zeta-chain/node/pull/2890) - refactor `MsgUpdateChainInfo` to accept a single chain, and add `MsgRemoveChainInfo` to remove a chain * [2899](https://github.com/zeta-chain/node/pull/2899) - remove btc deposit fee v1 and improve unit tests +* [2952](https://github.com/zeta-chain/node/pull/2952) - add error_message to cctx.status ### Tests @@ -36,6 +42,7 @@ * [2874](https://github.com/zeta-chain/node/pull/2874) - add support for multiple runs for precompile tests * [2895](https://github.com/zeta-chain/node/pull/2895) - add e2e test for bitcoin deposit and call * [2894](https://github.com/zeta-chain/node/pull/2894) - increase gas limit for TSS vote tx +* [2932](https://github.com/zeta-chain/node/pull/2932) - add gateway upgrade as part of the upgrade test * [2947](https://github.com/zeta-chain/node/pull/2947) - initialize simulation tests ### Fixes @@ -46,6 +53,9 @@ * [2944](https://github.com/zeta-chain/node/pull/2844) - add tsspubkey to index for tss keygen voting * [2842](https://github.com/zeta-chain/node/pull/2842) - fix: move interval assignment out of cctx loop in EVM outbound tx scheduler * [2853](https://github.com/zeta-chain/node/pull/2853) - calling precompile through sc with sc state update +* [2925](https://github.com/zeta-chain/node/pull/2925) - add recover to init chainer to diplay informative message when starting a node from block 1 +* [2909](https://github.com/zeta-chain/node/pull/2909) - add legacy messages back to codec for querier backward compatibility +* [3018](https://github.com/zeta-chain/node/pull/3018) - support `DepositAndCall` and `WithdrawAndCall` with empty payload ## v20.0.0 diff --git a/cmd/zetae2e/config/config.go b/cmd/zetae2e/config/config.go index 8e87e63bef..0cea78af39 100644 --- a/cmd/zetae2e/config/config.go +++ b/cmd/zetae2e/config/config.go @@ -68,6 +68,7 @@ func ExportContractsFromRunner(r *runner.E2ERunner, conf config.Config) config.C conf.Contracts.ZEVM.ERC20ZRC20Addr = config.DoubleQuotedString(r.ERC20ZRC20Addr.Hex()) conf.Contracts.ZEVM.BTCZRC20Addr = config.DoubleQuotedString(r.BTCZRC20Addr.Hex()) conf.Contracts.ZEVM.SOLZRC20Addr = config.DoubleQuotedString(r.SOLZRC20Addr.Hex()) + conf.Contracts.ZEVM.TONZRC20Addr = config.DoubleQuotedString(r.TONZRC20Addr.Hex()) conf.Contracts.ZEVM.UniswapFactoryAddr = config.DoubleQuotedString(r.UniswapV2FactoryAddr.Hex()) conf.Contracts.ZEVM.UniswapRouterAddr = config.DoubleQuotedString(r.UniswapV2RouterAddr.Hex()) conf.Contracts.ZEVM.ConnectorZEVMAddr = config.DoubleQuotedString(r.ConnectorZEVMAddr.Hex()) diff --git a/cmd/zetae2e/config/contracts.go b/cmd/zetae2e/config/contracts.go index d6cba953a5..9af3ccd812 100644 --- a/cmd/zetae2e/config/contracts.go +++ b/cmd/zetae2e/config/contracts.go @@ -135,6 +135,17 @@ func setContractsFromConfig(r *runner.E2ERunner, conf config.Config) error { } } + if c := conf.Contracts.ZEVM.TONZRC20Addr; c != "" { + r.TONZRC20Addr, err = c.AsEVMAddress() + if err != nil { + return fmt.Errorf("invalid TONZRC20Addr: %w", err) + } + r.TONZRC20, err = zrc20.NewZRC20(r.TONZRC20Addr, r.ZEVMClient) + if err != nil { + return err + } + } + if c := conf.Contracts.ZEVM.UniswapFactoryAddr; c != "" { r.UniswapV2FactoryAddr, err = c.AsEVMAddress() if err != nil { diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index ced848aadc..de46edf450 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -46,6 +46,7 @@ const ( flagTestV2Migration = "test-v2-migration" flagSkipTrackerCheck = "skip-tracker-check" flagSkipPrecompiles = "skip-precompiles" + flagUpgradeContracts = "upgrade-contracts" ) var ( @@ -83,6 +84,8 @@ func NewLocalCmd() *cobra.Command { cmd.Flags().Bool(flagTestV2Migration, false, "set to true to run tests for v2 contracts migration test") cmd.Flags().Bool(flagSkipTrackerCheck, false, "set to true to skip tracker check at the end of the tests") cmd.Flags().Bool(flagSkipPrecompiles, false, "set to true to skip stateful precompiled contracts test") + cmd.Flags(). + Bool(flagUpgradeContracts, false, "set to true to upgrade Gateways and ERC20Custody contracts during setup for ZEVM and EVM") return cmd } @@ -112,6 +115,7 @@ func localE2ETest(cmd *cobra.Command, _ []string) { testV2 = must(cmd.Flags().GetBool(flagTestV2)) testV2Migration = must(cmd.Flags().GetBool(flagTestV2Migration)) skipPrecompiles = must(cmd.Flags().GetBool(flagSkipPrecompiles)) + upgradeContracts = must(cmd.Flags().GetBool(flagUpgradeContracts)) ) logger := runner.NewLogger(verbose, color.FgWhite, "setup") @@ -202,23 +206,28 @@ func localE2ETest(cmd *cobra.Command, _ []string) { logger.Print("⚙️ setting up networks") startTime := time.Now() + // TODO: merge v1 and v2 together + // https://github.com/zeta-chain/node/issues/2627 + deployerRunner.SetupEVM(contractsDeployed, true) - if testV2 { - deployerRunner.SetupEVMV2() - } + deployerRunner.SetupEVMV2() deployerRunner.SetZEVMSystemContracts() - if testV2 { - // NOTE: v2 (gateway) setup called here because system contract needs to be set first, then gateway, then zrc20 - deployerRunner.SetZEVMContractsV2() - } + // NOTE: v2 (gateway) setup called here because system contract needs to be set first, then gateway, then zrc20 + deployerRunner.SetZEVMContractsV2() deployerRunner.SetZEVMZRC20s() + // Update the chain params to use v2 contract for ERC20Custody + // TODO: this function should be removed and the chain params should be directly set to use v2 contract + // https://github.com/zeta-chain/node/issues/2627 + deployerRunner.UpdateChainParamsV2Contracts() + deployerRunner.ERC20CustodyAddr = deployerRunner.ERC20CustodyV2Addr + if testSolana { - deployerRunner.SetSolanaContracts(conf.AdditionalAccounts.UserSolana.SolanaPrivateKey.String()) + deployerRunner.SetupSolana(conf.AdditionalAccounts.UserSolana.SolanaPrivateKey.String()) } noError(deployerRunner.FundEmissionsPool()) @@ -321,7 +330,8 @@ func localE2ETest(cmd *cobra.Command, _ []string) { e2etests.TestPrecompilesPrototypeName, e2etests.TestPrecompilesPrototypeThroughContractName, e2etests.TestPrecompilesStakingName, - e2etests.TestPrecompilesStakingThroughContractName, + // Disabled until further notice, check https://github.com/zeta-chain/node/issues/3005. + // e2etests.TestPrecompilesStakingThroughContractName, e2etests.TestPrecompilesBankName, e2etests.TestPrecompilesBankFailName, e2etests.TestPrecompilesBankThroughContractName, @@ -399,15 +409,14 @@ func localE2ETest(cmd *cobra.Command, _ []string) { tonTests := []string{ e2etests.TestTONDepositName, + e2etests.TestTONDepositAndCallName, } eg.Go(tonTestRoutine(conf, deployerRunner, verbose, tonTests...)) } - if testV2 { - // update the ERC20 custody contract for v2 tests - // note: not run in testV2Migration because it is already run in the migration process - deployerRunner.UpdateChainParamsV2Contracts() + if upgradeContracts { + deployerRunner.UpgradeGatewaysAndERC20Custody() } if testV2 || testV2Migration { diff --git a/cmd/zetae2e/local/ton.go b/cmd/zetae2e/local/ton.go index 000c872b25..bbbbd991de 100644 --- a/cmd/zetae2e/local/ton.go +++ b/cmd/zetae2e/local/ton.go @@ -25,6 +25,7 @@ func tonTestRoutine( deployerRunner, conf.DefaultAccount, runner.NewLogger(verbose, color.FgCyan, "ton"), + runner.WithZetaTxServer(deployerRunner.ZetaTxServer), ) if err != nil { return errors.Wrap(err, "unable to init ton test runner") diff --git a/cmd/zetae2e/local/v2.go b/cmd/zetae2e/local/v2.go index fa98ef6677..22de8a6c51 100644 --- a/cmd/zetae2e/local/v2.go +++ b/cmd/zetae2e/local/v2.go @@ -26,6 +26,8 @@ func startV2Tests(eg *errgroup.Group, conf config.Config, deployerRunner *runner e2etests.TestV2ZEVMToEVMCallName, e2etests.TestV2ZEVMToEVMCallThroughContractName, e2etests.TestV2EVMToZEVMCallName, + e2etests.TestV2ETHDepositAndCallNoMessageName, + e2etests.TestV2ETHWithdrawAndCallNoMessageName, )) // Test happy paths for erc20 token workflow @@ -34,7 +36,9 @@ func startV2Tests(eg *errgroup.Group, conf config.Config, deployerRunner *runner e2etests.TestV2ERC20DepositName, e2etests.TestV2ERC20DepositAndCallName, e2etests.TestV2ERC20WithdrawName, + e2etests.TestV2ERC20WithdrawAndArbitraryCallName, e2etests.TestV2ERC20WithdrawAndCallName, + e2etests.TestV2ERC20DepositAndCallNoMessageName, )) // Test revert cases for gas token workflow diff --git a/contrib/localnet/docker-compose.yml b/contrib/localnet/docker-compose.yml index 2c2efbd87f..363f5e1f6d 100644 --- a/contrib/localnet/docker-compose.yml +++ b/contrib/localnet/docker-compose.yml @@ -227,6 +227,18 @@ services: -rpcauth=smoketest:63acf9b8dccecce914d85ff8c044b78b$$5892f9bbc84f4364e79f0970039f88bdd823f168d4acc76099ab97b14a766a99 -txindex=1 + bitcoin-node-sidecar: + image: ghcr.io/zeta-chain/node-localnet-bitcoin-sidecar:e0205d7 + container_name: bitcoin-node-sidecar + hostname: bitcoin-node-sidecar + networks: + mynetwork: + ipv4_address: 172.20.0.111 + environment: + - PORT=8000 + ports: + - "8000:8000" + solana: image: solana-local:latest container_name: solana diff --git a/contrib/localnet/orchestrator/start-zetae2e.sh b/contrib/localnet/orchestrator/start-zetae2e.sh index 9977b7dbd9..33f0d4e956 100644 --- a/contrib/localnet/orchestrator/start-zetae2e.sh +++ b/contrib/localnet/orchestrator/start-zetae2e.sh @@ -258,9 +258,9 @@ if [ "$LOCALNET_MODE" == "upgrade" ]; then # When the upgrade height is greater than 100 for upgrade test, the Bitcoin tests have been run once, therefore the Bitcoin wallet is already set up # Use light flag to skip advanced tests if [ "$UPGRADE_HEIGHT" -lt 100 ]; then - zetae2e local $E2E_ARGS --skip-setup --config "$deployed_config_path" --light ${COMMON_ARGS} + zetae2e local $E2E_ARGS --skip-setup --config "$deployed_config_path" --light --test-v2 --upgrade-contracts ${COMMON_ARGS} else - zetae2e local $E2E_ARGS --skip-setup --config "$deployed_config_path" --skip-bitcoin-setup --light ${COMMON_ARGS} + zetae2e local $E2E_ARGS --skip-setup --config "$deployed_config_path" --skip-bitcoin-setup --light --test-v2 --upgrade-contracts ${COMMON_ARGS} fi ZETAE2E_EXIT_CODE=$? diff --git a/contrib/localnet/scripts/start-zetaclientd.sh b/contrib/localnet/scripts/start-zetaclientd.sh index 7d2768b177..e403e2d599 100755 --- a/contrib/localnet/scripts/start-zetaclientd.sh +++ b/contrib/localnet/scripts/start-zetaclientd.sh @@ -109,9 +109,9 @@ then fi fi -# merge extra-evm-chains.json into zetaclient_config.json if specified -if [[ -f /root/extra-evm-chains.json ]]; then - jq '.EVMChainConfigs *= input' /root/.zetacored/config/zetaclient_config.json /root/extra-evm-chains.json > /tmp/merged_config.json +# merge zetaclient-config-overlay.json into zetaclient_config.json if specified +if [[ -f /root/zetaclient-config-overlay.json ]]; then + jq -s '.[0] * .[1]' /root/.zetacored/config/zetaclient_config.json /root/zetaclient-config-overlay.json > /tmp/merged_config.json mv /tmp/merged_config.json /root/.zetacored/config/zetaclient_config.json fi diff --git a/docs/openapi/openapi.swagger.yaml b/docs/openapi/openapi.swagger.yaml index a8c0a2516e..9dc72eabb9 100644 --- a/docs/openapi/openapi.swagger.yaml +++ b/docs/openapi/openapi.swagger.yaml @@ -56998,7 +56998,9 @@ definitions: - bitcoin - op_stack - solana_consensus + - catchain_consensus default: ethereum + description: '- catchain_consensus: ton' title: |- Consensus represents the consensus algorithm used by the chain this can represent the consensus of a L1 @@ -57014,6 +57016,7 @@ definitions: - optimism - base - solana + - ton default: eth title: |- Network represents the network of the chain @@ -57048,6 +57051,7 @@ definitions: - no_vm - evm - svm + - tvm default: no_vm title: |- Vm represents the virtual machine type of the chain to support smart @@ -57198,6 +57202,11 @@ definitions: format: uint64 tx_finalization_status: $ref: '#/definitions/crosschainTxFinalizationStatus' + is_cross_chain_call: + type: boolean + title: |- + this field describes if a smart contract call should be made for a inbound + with assets only used for protocol contract version 2 crosschainInboundTracker: type: object properties: @@ -58446,6 +58455,14 @@ definitions: $ref: '#/definitions/crosschainCctxStatus' status_message: type: string + description: |- + status_message carries information about the status transitions: + why they were triggered, old and new status. + error_message: + type: string + description: |- + error_message carries information about the error that caused the tx + to be PendingRevert, Reverted or Aborted. lastUpdate_timestamp: type: string format: int64 diff --git a/docs/releases/v20_breaking_changes.md b/docs/releases/v20_breaking_changes.md deleted file mode 100644 index 1519ccf8a6..0000000000 --- a/docs/releases/v20_breaking_changes.md +++ /dev/null @@ -1,9 +0,0 @@ - -# V20 Breaking Changes - -### Emissions factors deprecated - -* `EmissionsFactors` have been deprecated and removed from the `emissions` module. - - This results in the removal of the query `/zeta-chain/emissions/get_emissions_factors`. - - The fixed block reward amount can now be queried via `/zeta-chain/emissions/params`. This is constant for every block and does not depend on any factors. - diff --git a/docs/releases/v21_breaking_changes.md b/docs/releases/v21_breaking_changes.md index df82831bad..818258fdf3 100644 --- a/docs/releases/v21_breaking_changes.md +++ b/docs/releases/v21_breaking_changes.md @@ -8,4 +8,11 @@ * If the chain already exists, the details will be updated. * If the chain does not exist, it will be added to the list of chains and saved to the store. * A new transaction type `RemoveChainInfo` has also been added to remove a chain from the list of chains. - * It accepts the chain-id of the chain to be removed as a parameter. \ No newline at end of file + * It accepts the chain-id of the chain to be removed as a parameter. + +### Emissions factors deprecated + +* `EmissionsFactors` have been deprecated and removed from the `emissions` module. + - This results in the removal of the query `/zeta-chain/emissions/get_emissions_factors`. + - The fixed block reward amount can now be queried via `/zeta-chain/emissions/params`. This is constant for every block and does not depend on any factors. + diff --git a/docs/rfc/001_standard_memo_format.md b/docs/rfc/001_standard_memo_format.md new file mode 100644 index 0000000000..35604f12c9 --- /dev/null +++ b/docs/rfc/001_standard_memo_format.md @@ -0,0 +1,220 @@ +# A proposed standard memo format for non-EVM chains +
+ +## The goal + +This doc is aimed to introduce a standard format of ZetaChain inbound `memo` across non-EVM chains. The `memo` in an inbound transaction is to carry both instructions and data, by which the cross-chain transaction to be processed as expected in the `zetacore`. A standard memo format helps to ensure consistent developer experience across EVM and non-EVM chains. +

+ + +## The EVM gateway inbound events + +We want to mimic below `Deposit` and `Called` events in the EVM `gateway` contract for non-EVM chains. Typically, we at least want the following 3 pieces at the time of writing. + +- The `receiver` address in ZEVM. +- The `payload` of the call. +- The `RevertOptions`. + +``` +event Deposited( + address indexed sender, + address indexed receiver, + uint256 amount, + address asset, + bytes payload, + RevertOptions revertOptions +); + +event Called( + address indexed sender, + address indexed receiver, + bytes payload, + RevertOptions revertOptions +); +``` +

+ + +## 1. The Memo Diagram + +![](images/001_layout.png)

+ + +## 2. The memo structure + +The raw-byte representation of a standard non-EVM memo contains `9` sections (2 mandatory + 7 optional). + +`Section 0` is the `header`, which controls how the memo should be interpreted. + +`Section 1~8` are data sections that carry actual data attached to the memo. + + +| | Section 0 | Section 1 | Section 2 | Section 3 | Section 4 | Section 5 | Section 6 | Section 7 | Section 8 | +|-----------------|------------|-------------|-------------|----------------|--------------|--------------|---------------|-------------|-------------| +| **Name** | Header | Receiver | Payload | RevertAddress | CallOnRevert | AbortAddress | RevertMessage | reserved | reserved | +| **Size (byte)** | 4 | 20 | variable | variable | 1 | 20 | variable | -- | -- | +| **Type** | [4]byte | ZEVM address| bytes | non-EVM address| bool | ZEVM address | bytes | -- | -- | +| **Optional** | No | Yes | Yes | Yes | Yes | Yes | Yes | -- | -- | +

+ + +## 3. The header + +The `4-byte` header is a self-contained declaration of the memo properties and rules. The proposed header structure is designed in a flexible manner. The data fields are all declared as optional. With `reserved` flags, new features and fields can be added in the future without breaking compatibility. + +### `byte-0`: Memo Identifier + +The ASCII code `0x5A` of letter `'Z'` is used as the identifier of the standard ZetaChain memo format. Once legacy memo is phased out, zetaclient will only accept memos beginning with `0x5A`. + + +### `byte-1`: Control Byte + +| | bit 4 ~ 7 | bit 0 ~ 3 | +|-----------------|------------------------------------------------|--------------------------------------| +| **Name** | version # | encoding format | +| **Optional** | No (0b0000 for now) | No (0b0000 or 0b0001 or 0b0010) | +
+ + +### `byte-2`: Control Byte + +| | bit 4 ~ 7 | bit 0 ~ 3 | +|-----------------|------------------------------------------------|--------------------------------| +| **Name** | operation code | control bytes reserved | +| **Optional** | No (0b0000 or 0001 or 0010 for now) | -- | +
+ + + +### `byte-3`: Data Flags + +| | bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 | +|-----------------|---------------|--------------|---------------|-------------------|------------------|-------------------|-----------------|------------------| +| **Name** | flag reserved | flag reserved| flag reserved | flag CallOnRevert | flag AbortAddress| flag RevertAddress| flag Payload | flag Receiver | +| **Optional** | -- | -- | -- | -- | Yes | Yes | Yes | Yes | +
+ +Supported `operation codes` : + +The `operation code` is designed for the developers to explicitly specify the intention of an inbound transaction in non-contract chains (e.g. Bitcoin, Doge, …). +The operation code is not necessary for non-contract chains, because the method name tells the intention of the transaction. + +A 4-bit operation code would allow `16` operation codes at maximum. + +| 0b0000 | 0b0001 | 0b0010 | +|-------------|-------------------|-------------| +| deposit | deposit_and_call | call | +
+ + +Supported `encoding flags`: +This flag is to tell `zetaclient` how the memo fields should be decoded. A 2-bit weight flag seems good enough and won’t change in future.
+Note: the `compact *` encoding is to minimizes length of memo data (compared to ABI) and will be explained in following sections. + +| 0b00 00 | 0b000 | 0b0010 | +|--------------|----------------|--------------| +| ABI encoded | compact short | compact long | +

+ + +## 4. The types + +To decode/encode the memo fields with `compact *` format, we've defined a few types below. +To decode/encode the memo fields with `ABI encoded` format, just follow the [ABI Spec](https://docs.soliditylang.org/en/develop/abi-spec.html). + + +| Field | Size (byte) | Description | +|-----------------------------|--------------|---------------------------------------------------------------------------------------------------------------------------------| +| **ZEVM address** | fixed, 20 | The raw bytes representation of a ZEVM address. ZEVM address is the same as standard Ethereum address. | +| **non-EVM address** | variable | The raw bytes representation of a non-EVM external chain address. Different addresses have different lengths, e.g., Bitcoin vs Solana; Bitcoin Segwit vs Legacy. | +| **bytes** | variable | The raw bytes of arbitrary data. | +

+ + +## 5. Byte layout of variable-length types + +There are two ways to encode your the memo fields into raw bytes. The `ABI encoded` and `compact *` encoded. + +| Decoding flag | Format | Layout | Description | Rule | +|---------------|---------------|-----------------------------------------|-----------------------------------------------|----------------------------------------------------------| +| 0b0000 | ABI encoded | ABI encoded types | Types are packed/unpacked with ABI standard | [ABI Spec](https://docs.soliditylang.org/en/develop/abi-spec.html) | +| 0b0001 | compact short | [1-byte length] + [data] | Carry up to 255 bytes of data. | A valid length (>= 0) must match data length. | +| 0b0010 | compact long | [2-byte length, little-endian] + [data] | Carry up to 65535 bytes (64KB) of data. | A valid length (>= 0) must match data length. | +| + + +## 6. How to pack your inbound memo + +### Step-1: prepare your memo header + +```go +func MakeHead() []byte { + header := make([]byte, 4) + header[0] = 'Z' + header[1] = 0<<4 | 0b0010 // version + encoding format (e.g. compact long) + header[2] = 0b0001 << 4 // operation code (e.g. DepositAndCall) + header[3] = 0b00000111 // receiver, payload and revertAddress are set + return header +} +``` + + +### Step-2: pack your memo data + +For use cases that requires compact transaction data, choose encoding format of `compact short` or `compact long`. + +```go +func CompactPack(receiver common.Address, payload []byte, revertAddress string) []byte { + data := make([]byte, 0) + + // pack receiver (fixed 20 bytes) + data = append(data, receiver.Bytes()...) + + // pack payload (dynamic) + // encode length with 1 byte if 'compact short' is your choice + lenthPayload := make([]byte, 2) + binary.LittleEndian.PutUint16(lenthPayload, uint16(len(payload))) + + data = append(data, lenthPayload...) // write length + data = append(data, payload...) // write payload + + // pack revert address (dynamic). + // encode length with 1 byte if 'compact short' is your choice + lengthRevertAddr := make([]byte, 2) + binary.LittleEndian.PutUint16(lengthRevertAddr, uint16(len([]byte(revertAddress)))) + + data = append(data, lengthRevertAddr...) // write length + data = append(data, []byte(revertAddress)...) // write revert address + + return data +} +``` + + +Also, ABI encoding format is another option to consider. + +```go +func ABIPack(receiver common.Address, payload []byte, revertAddress string) ([]byte, error) { + // define the ABI for encoding the types: address, bytes, string + abiString := `[{"type":"function","name":"encode","inputs":[ + {"type":"address"}, + {"type":"bytes"}, + {"type":"string"}], + "outputs":[]}]` + + // parse the ABI + parsedABI, err := abi.JSON(strings.NewReader(abiString)) + if err != nil { + return nil, err + } + + // pack the values using the ABI + data, err := parsedABI.Pack("encode", receiver, payload, revertAddress) + if err != nil { + return nil, err + } + + // remove the 4-byte selector + return data[4:], nil +} +``` diff --git a/docs/rfc/images/001_layout.png b/docs/rfc/images/001_layout.png new file mode 100644 index 0000000000..05ddaf06e9 Binary files /dev/null and b/docs/rfc/images/001_layout.png differ diff --git a/docs/spec/crosschain/messages.md b/docs/spec/crosschain/messages.md index c369443d27..3182bec082 100644 --- a/docs/spec/crosschain/messages.md +++ b/docs/spec/crosschain/messages.md @@ -63,6 +63,7 @@ message MsgVoteGasPrice { uint64 price = 3; uint64 priority_fee = 6; uint64 block_number = 4; + string supply = 5; } ``` @@ -190,6 +191,7 @@ message MsgVoteInbound { ProtocolContractVersion protocol_contract_version = 16; RevertOptions revert_options = 17; CallOptions call_options = 18; + bool is_cross_chain_call = 19; } ``` diff --git a/e2e/config/config.go b/e2e/config/config.go index 089ea39b70..32a799c027 100644 --- a/e2e/config/config.go +++ b/e2e/config/config.go @@ -141,6 +141,7 @@ type ZEVM struct { ERC20ZRC20Addr DoubleQuotedString `yaml:"erc20_zrc20"` BTCZRC20Addr DoubleQuotedString `yaml:"btc_zrc20"` SOLZRC20Addr DoubleQuotedString `yaml:"sol_zrc20"` + TONZRC20Addr DoubleQuotedString `yaml:"ton_zrc20"` UniswapFactoryAddr DoubleQuotedString `yaml:"uniswap_factory"` UniswapRouterAddr DoubleQuotedString `yaml:"uniswap_router"` ConnectorZEVMAddr DoubleQuotedString `yaml:"connector_zevm"` diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index 29f5a3857a..979876b352 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -65,7 +65,8 @@ const ( /** * TON tests */ - TestTONDepositName = "ton_deposit" + TestTONDepositName = "ton_deposit" + TestTONDepositAndCallName = "ton_deposit_and_call" /* Bitcoin tests @@ -82,6 +83,7 @@ const ( TestBitcoinWithdrawP2SHName = "bitcoin_withdraw_p2sh" TestBitcoinWithdrawInvalidAddressName = "bitcoin_withdraw_invalid" TestBitcoinWithdrawRestrictedName = "bitcoin_withdraw_restricted" + TestExtractBitcoinInscriptionMemoName = "bitcoin_memo_from_inscription" /* Application tests @@ -128,20 +130,25 @@ const ( */ TestV2ETHDepositName = "v2_eth_deposit" TestV2ETHDepositAndCallName = "v2_eth_deposit_and_call" + TestV2ETHDepositAndCallNoMessageName = "v2_eth_deposit_and_call_no_message" TestV2ETHDepositAndCallRevertName = "v2_eth_deposit_and_call_revert" TestV2ETHDepositAndCallRevertWithCallName = "v2_eth_deposit_and_call_revert_with_call" TestV2ETHWithdrawName = "v2_eth_withdraw" TestV2ETHWithdrawAndArbitraryCallName = "v2_eth_withdraw_and_arbitrary_call" TestV2ETHWithdrawAndCallName = "v2_eth_withdraw_and_call" + TestV2ETHWithdrawAndCallNoMessageName = "v2_eth_withdraw_and_call_no_message" TestV2ETHWithdrawAndCallThroughContractName = "v2_eth_withdraw_and_call_through_contract" TestV2ETHWithdrawAndCallRevertName = "v2_eth_withdraw_and_call_revert" TestV2ETHWithdrawAndCallRevertWithCallName = "v2_eth_withdraw_and_call_revert_with_call" TestV2ERC20DepositName = "v2_erc20_deposit" TestV2ERC20DepositAndCallName = "v2_erc20_deposit_and_call" + TestV2ERC20DepositAndCallNoMessageName = "v2_erc20_deposit_and_call_no_message" TestV2ERC20DepositAndCallRevertName = "v2_erc20_deposit_and_call_revert" TestV2ERC20DepositAndCallRevertWithCallName = "v2_erc20_deposit_and_call_revert_with_call" TestV2ERC20WithdrawName = "v2_erc20_withdraw" + TestV2ERC20WithdrawAndArbitraryCallName = "v2_erc20_withdraw_and_arbitrary_call" TestV2ERC20WithdrawAndCallName = "v2_erc20_withdraw_and_call" + TestV2ERC20WithdrawAndCallNoMessageName = "v2_erc20_withdraw_and_call_no_message" TestV2ERC20WithdrawAndCallRevertName = "v2_erc20_withdraw_and_call_revert" TestV2ERC20WithdrawAndCallRevertWithCallName = "v2_erc20_withdraw_and_call_revert_with_call" TestV2ZEVMToEVMArbitraryCallName = "v2_zevm_to_evm_arbitrary_call" @@ -444,13 +451,28 @@ var AllE2ETests = []runner.E2ETest{ TestTONDepositName, "deposit TON into ZEVM", []runner.ArgDefinition{ - {Description: "amount in nano tons", DefaultValue: "900000000"}, // 0.9 TON + {Description: "amount in nano tons", DefaultValue: "1000000000"}, // 1.0 TON }, TestTONDeposit, ), + runner.NewE2ETest( + TestTONDepositAndCallName, + "deposit TON into ZEVM and call a contract", + []runner.ArgDefinition{ + {Description: "amount in nano tons", DefaultValue: "1000000000"}, // 1.0 TON + }, + TestTONDepositAndCall, + ), /* Bitcoin tests */ + runner.NewE2ETest( + TestExtractBitcoinInscriptionMemoName, + "extract memo from BTC inscription", []runner.ArgDefinition{ + {Description: "amount in btc", DefaultValue: "0.1"}, + }, + TestExtractBitcoinInscriptionMemo, + ), runner.NewE2ETest( TestBitcoinDepositName, "deposit Bitcoin into ZEVM", @@ -713,6 +735,14 @@ var AllE2ETests = []runner.E2ETest{ }, TestV2ETHDepositAndCall, ), + runner.NewE2ETest( + TestV2ETHDepositAndCallNoMessageName, + "deposit Ether into ZEVM and call a contract using V2 contract using no message content", + []runner.ArgDefinition{ + {Description: "amount in wei", DefaultValue: "10000000000000000"}, + }, + TestV2ETHDepositAndCallNoMessage, + ), runner.NewE2ETest( TestV2ETHDepositAndCallRevertName, "deposit Ether into ZEVM and call a contract using V2 contract that reverts", @@ -753,6 +783,14 @@ var AllE2ETests = []runner.E2ETest{ }, TestV2ETHWithdrawAndCall, ), + runner.NewE2ETest( + TestV2ETHWithdrawAndCallNoMessageName, + "withdraw Ether from ZEVM call a contract using V2 contract with no message content", + []runner.ArgDefinition{ + {Description: "amount in wei", DefaultValue: "100000"}, + }, + TestV2ETHWithdrawAndCallNoMessage, + ), runner.NewE2ETest( TestV2ETHWithdrawAndCallThroughContractName, "withdraw Ether from ZEVM call a contract using V2 contract through intermediary contract", @@ -793,6 +831,14 @@ var AllE2ETests = []runner.E2ETest{ }, TestV2ERC20DepositAndCall, ), + runner.NewE2ETest( + TestV2ERC20DepositAndCallNoMessageName, + "deposit ERC20 into ZEVM and call a contract using V2 contract with no message content", + []runner.ArgDefinition{ + {Description: "amount", DefaultValue: "100000"}, + }, + TestV2ERC20DepositAndCallNoMessage, + ), runner.NewE2ETest( TestV2ERC20DepositAndCallRevertName, "deposit ERC20 into ZEVM and call a contract using V2 contract that reverts", @@ -818,13 +864,25 @@ var AllE2ETests = []runner.E2ETest{ TestV2ERC20Withdraw, ), runner.NewE2ETest( - TestV2ERC20WithdrawAndCallName, - "withdraw ERC20 from ZEVM and call a contract using V2 contract", + TestV2ERC20WithdrawAndArbitraryCallName, + "withdraw ERC20 from ZEVM and arbitrary call a contract using V2 contract", []runner.ArgDefinition{ {Description: "amount", DefaultValue: "1000"}, }, + TestV2ERC20WithdrawAndArbitraryCall, + ), + runner.NewE2ETest( + TestV2ERC20WithdrawAndCallName, + "withdraw ERC20 from ZEVM and authenticated call a contract using V2 contract", + []runner.ArgDefinition{}, TestV2ERC20WithdrawAndCall, ), + runner.NewE2ETest( + TestV2ERC20WithdrawAndCallNoMessageName, + "withdraw ERC20 from ZEVM and authenticated call a contract using V2 contract with no message", + []runner.ArgDefinition{}, + TestV2ERC20WithdrawAndCallNoMessage, + ), runner.NewE2ETest( TestV2ERC20WithdrawAndCallRevertName, "withdraw ERC20 from ZEVM and call a contract using V2 contract that reverts", @@ -913,7 +971,7 @@ var AllE2ETests = []runner.E2ETest{ TestPrecompilesStakingName, "test stateful precompiled contracts staking", []runner.ArgDefinition{}, - TestPrecompilesStaking, + TestPrecompilesStakingIsDisabled, ), runner.NewE2ETest( TestPrecompilesStakingThroughContractName, diff --git a/e2e/e2etests/helpers.go b/e2e/e2etests/helpers.go index e3ab963354..64f0920c2a 100644 --- a/e2e/e2etests/helpers.go +++ b/e2e/e2etests/helpers.go @@ -4,6 +4,7 @@ import ( "math/big" "strconv" + "cosmossdk.io/math" "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" @@ -144,6 +145,10 @@ func parseBigInt(t require.TestingT, s string) *big.Int { return v } +func parseUint(t require.TestingT, s string) math.Uint { + return math.NewUintFromBigInt(parseBigInt(t, s)) +} + // bigIntFromFloat64 takes float64 (e.g. 0.001) that represents btc amount // and converts it to big.Int for downstream usage. func btcAmountFromFloat64(t require.TestingT, amount float64) *big.Int { diff --git a/e2e/e2etests/test_eth_deposit_call.go b/e2e/e2etests/test_eth_deposit_call.go index 570e7b05eb..9484888cf3 100644 --- a/e2e/e2etests/test_eth_deposit_call.go +++ b/e2e/e2etests/test_eth_deposit_call.go @@ -87,6 +87,11 @@ func TestEtherDepositAndCall(r *runner.E2ERunner, args []string) { r.Logger.Info("Cross-chain call to reverter reverted") - // check the status message contains revert error hash in case of revert - require.Contains(r, cctx.CctxStatus.StatusMessage, utils.ErrHashRevertFoo) + // Check the error carries the revert executed. + // tolerate the error in both the ErrorMessage field and the StatusMessage field + if cctx.CctxStatus.ErrorMessage != "" { + require.Contains(r, cctx.CctxStatus.ErrorMessage, "revert executed") + } else { + require.Contains(r, cctx.CctxStatus.StatusMessage, utils.ErrHashRevertFoo) + } } diff --git a/e2e/e2etests/test_extract_bitcoin_inscription_memo.go b/e2e/e2etests/test_extract_bitcoin_inscription_memo.go new file mode 100644 index 0000000000..eedc24b577 --- /dev/null +++ b/e2e/e2etests/test_extract_bitcoin_inscription_memo.go @@ -0,0 +1,57 @@ +package e2etests + +import ( + "encoding/hex" + + "github.com/btcsuite/btcd/btcjson" + "github.com/rs/zerolog/log" + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/node/e2e/runner" + btcobserver "github.com/zeta-chain/node/zetaclient/chains/bitcoin/observer" +) + +func TestExtractBitcoinInscriptionMemo(r *runner.E2ERunner, args []string) { + r.SetBtcAddress(r.Name, false) + + // obtain some initial fund + stop := r.MineBlocksIfLocalBitcoin() + defer stop() + r.Logger.Info("Mined blocks") + + // list deployer utxos + utxos, err := r.ListDeployerUTXOs() + require.NoError(r, err) + + amount := parseFloat(r, args[0]) + // this is just some random test memo for inscription + memo, err := hex.DecodeString( + "72f080c854647755d0d9e6f6821f6931f855b9acffd53d87433395672756d58822fd143360762109ab898626556b1c3b8d3096d2361f1297df4a41c1b429471a9aa2fc9be5f27c13b3863d6ac269e4b587d8389f8fd9649859935b0d48dea88cdb40f20c", + ) + require.NoError(r, err) + + txid := r.InscribeToTSSFromDeployerWithMemo(amount, utxos, memo) + + _, err = r.GenerateToAddressIfLocalBitcoin(6, r.BTCDeployerAddress) + require.NoError(r, err) + + rawtx, err := r.BtcRPCClient.GetRawTransactionVerbose(txid) + require.NoError(r, err) + r.Logger.Info("obtained reveal txn id %s", txid) + + dummyCoinbaseTxn := rawtx + events, err := btcobserver.FilterAndParseIncomingTx( + r.BtcRPCClient, + []btcjson.TxRawResult{*dummyCoinbaseTxn, *rawtx}, + 0, + r.BTCTSSAddress.String(), + log.Logger, + r.BitcoinParams, + ) + require.NoError(r, err) + + require.Equal(r, 1, len(events)) + event := events[0] + + require.Equal(r, event.MemoBytes, memo) +} diff --git a/e2e/e2etests/test_migrate_tss.go b/e2e/e2etests/test_migrate_tss.go index cb5e3c35c4..3825228c14 100644 --- a/e2e/e2etests/test_migrate_tss.go +++ b/e2e/e2etests/test_migrate_tss.go @@ -48,6 +48,8 @@ func TestMigrateTSS(r *runner.E2ERunner, _ []string) { btcBalance = btcBalance - 0.01 btcChain := chains.BitcoinRegtest.ChainId + r.WaitForTSSGeneration(2) + //migrate btc funds // #nosec G701 e2eTest - always in range migrationAmountBTC := sdkmath.NewUint(uint64(btcBalance * 1e8)) diff --git a/e2e/e2etests/test_precompiles_bank.go b/e2e/e2etests/test_precompiles_bank.go index 2517410339..308f684572 100644 --- a/e2e/e2etests/test_precompiles_bank.go +++ b/e2e/e2etests/test_precompiles_bank.go @@ -20,6 +20,7 @@ func TestPrecompilesBank(r *runner.E2ERunner, args []string) { higherBalanceAmount := big.NewInt(1001) higherAllowanceAmount := big.NewInt(501) spender := r.EVMAddress() + bankAddress := bank.ContractAddress // Increase the gasLimit. It's required because of the gas consumed by precompiled functions. previousGasLimit := r.ZEVMAuth.GasLimit @@ -29,7 +30,7 @@ func TestPrecompilesBank(r *runner.E2ERunner, args []string) { // Reset the allowance to 0; this is needed when running upgrade tests where // this test runs twice. - tx, err := r.ERC20ZRC20.Approve(r.ZEVMAuth, bank.ContractAddress, big.NewInt(0)) + tx, err := r.ERC20ZRC20.Approve(r.ZEVMAuth, bankAddress, big.NewInt(0)) require.NoError(r, err) receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) utils.RequireTxSuccessful(r, receipt, "Resetting allowance failed") @@ -59,7 +60,7 @@ func TestPrecompilesBank(r *runner.E2ERunner, args []string) { require.Equal(r, uint64(0), cosmosBalance.Uint64(), "spender cosmos coin balance should be 0") // Approve allowance of 500 ERC20ZRC20 tokens for the bank contract. Should pass. - tx, err := r.ERC20ZRC20.Approve(r.ZEVMAuth, bank.ContractAddress, depositAmount) + tx, err := r.ERC20ZRC20.Approve(r.ZEVMAuth, bankAddress, depositAmount) require.NoError(r, err) receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) utils.RequireTxSuccessful(r, receipt, "Approve ETHZRC20 bank allowance tx failed") @@ -72,7 +73,7 @@ func TestPrecompilesBank(r *runner.E2ERunner, args []string) { utils.RequiredTxFailed(r, receipt, "Depositting an amount higher than allowed should fail") // Approve allowance of 1000 ERC20ZRC20 tokens. - tx, err = r.ERC20ZRC20.Approve(r.ZEVMAuth, bank.ContractAddress, big.NewInt(1e3)) + tx, err = r.ERC20ZRC20.Approve(r.ZEVMAuth, bankAddress, big.NewInt(1e3)) require.NoError(r, err) receipt = utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) utils.RequireTxSuccessful(r, receipt, "Approve ETHZRC20 bank allowance tx failed") @@ -103,7 +104,7 @@ func TestPrecompilesBank(r *runner.E2ERunner, args []string) { require.Equal(r, uint64(500), cosmosBalance.Uint64(), "spender cosmos coin balance should be 500") // Bank: ERC20ZRC20 balance should be 500 tokens locked. - bankZRC20Balance, err := r.ERC20ZRC20.BalanceOf(&bind.CallOpts{Context: r.Ctx}, bank.ContractAddress) + bankZRC20Balance, err := r.ERC20ZRC20.BalanceOf(&bind.CallOpts{Context: r.Ctx}, bankAddress) require.NoError(r, err, "Call ERC20ZRC20.BalanceOf") require.Equal(r, uint64(500), bankZRC20Balance.Uint64(), "bank ERC20ZRC20 balance should be 500") @@ -115,7 +116,7 @@ func TestPrecompilesBank(r *runner.E2ERunner, args []string) { // Bank: ERC20ZRC20 balance should be 500 tokens locked after a failed withdraw. // No tokens should be unlocked with a failed withdraw. - bankZRC20Balance, err = r.ERC20ZRC20.BalanceOf(&bind.CallOpts{Context: r.Ctx}, bank.ContractAddress) + bankZRC20Balance, err = r.ERC20ZRC20.BalanceOf(&bind.CallOpts{Context: r.Ctx}, bankAddress) require.NoError(r, err, "Call ERC20ZRC20.BalanceOf") require.Equal(r, uint64(500), bankZRC20Balance.Uint64(), "bank ERC20ZRC20 balance should be 500") @@ -143,7 +144,7 @@ func TestPrecompilesBank(r *runner.E2ERunner, args []string) { require.Equal(r, uint64(1000), zrc20Balance.Uint64(), "spender ERC20ZRC20 balance should be 1000") // Bank: ERC20ZRC20 balance should be 0 tokens locked. - bankZRC20Balance, err = r.ERC20ZRC20.BalanceOf(&bind.CallOpts{Context: r.Ctx}, bank.ContractAddress) + bankZRC20Balance, err = r.ERC20ZRC20.BalanceOf(&bind.CallOpts{Context: r.Ctx}, bankAddress) require.NoError(r, err, "Call ERC20ZRC20.BalanceOf") require.Equal(r, uint64(0), bankZRC20Balance.Uint64(), "bank ERC20ZRC20 balance should be 0") } @@ -158,7 +159,7 @@ func TestPrecompilesBankNonZRC20(r *runner.E2ERunner, args []string) { r.ZEVMAuth.GasLimit = previousGasLimit }() - spender, bankAddr := r.EVMAddress(), bank.ContractAddress + spender, bankAddress := r.EVMAddress(), bank.ContractAddress // Create a bank contract caller. bankContract, err := bank.NewIBank(bank.ContractAddress, r.ZEVMClient) @@ -179,13 +180,13 @@ func TestPrecompilesBankNonZRC20(r *runner.E2ERunner, args []string) { ) // Allow the bank contract to spend 25 WZeta tokens. - tx, err := r.WZeta.Approve(r.ZEVMAuth, bankAddr, big.NewInt(25)) + tx, err := r.WZeta.Approve(r.ZEVMAuth, bankAddress, big.NewInt(25)) require.NoError(r, err, "Error approving allowance for bank contract") receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) require.EqualValues(r, uint64(1), receipt.Status, "approve allowance tx failed") // Check the allowance of the bank in WZeta tokens. Should be 25. - allowance, err := r.WZeta.Allowance(&bind.CallOpts{Context: r.Ctx}, spender, bankAddr) + allowance, err := r.WZeta.Allowance(&bind.CallOpts{Context: r.Ctx}, spender, bankAddress) require.NoError(r, err, "Error retrieving bank allowance") require.EqualValues(r, uint64(25), allowance.Uint64(), "Error allowance for bank contract") diff --git a/e2e/e2etests/test_precompiles_bank_through_contract.go b/e2e/e2etests/test_precompiles_bank_through_contract.go index 480663284e..6d6384fd9e 100644 --- a/e2e/e2etests/test_precompiles_bank_through_contract.go +++ b/e2e/e2etests/test_precompiles_bank_through_contract.go @@ -18,6 +18,7 @@ func TestPrecompilesBankThroughContract(r *runner.E2ERunner, args []string) { require.Len(r, args, 0, "No arguments expected") spender := r.EVMAddress() + bankAddress := bank.ContractAddress zrc20Address := r.ERC20ZRC20Addr oneThousand := big.NewInt(1e3) oneThousandOne := big.NewInt(1001) @@ -59,7 +60,7 @@ func TestPrecompilesBankThroughContract(r *runner.E2ERunner, args []string) { // Check initial balances. balanceShouldBe(r, 0, checkCosmosBalance(r, testBank, zrc20Address, spender)) balanceShouldBe(r, 1000, checkZRC20Balance(r, spender)) - balanceShouldBe(r, 0, checkZRC20Balance(r, bank.ContractAddress)) + balanceShouldBe(r, 0, checkZRC20Balance(r, bankAddress)) // Deposit without previous alllowance should fail. receipt = depositThroughTestBank(r, testBank, zrc20Address, oneThousand) @@ -68,10 +69,10 @@ func TestPrecompilesBankThroughContract(r *runner.E2ERunner, args []string) { // Check balances, should be the same. balanceShouldBe(r, 0, checkCosmosBalance(r, testBank, zrc20Address, spender)) balanceShouldBe(r, 1000, checkZRC20Balance(r, spender)) - balanceShouldBe(r, 0, checkZRC20Balance(r, bank.ContractAddress)) + balanceShouldBe(r, 0, checkZRC20Balance(r, bankAddress)) // Allow 500 ZRC20 to bank precompile. - approveAllowance(r, bank.ContractAddress, fiveHundred) + approveAllowance(r, bankAddress, fiveHundred) // Deposit 501 ERC20ZRC20 tokens to the bank contract, through TestBank. // It's higher than allowance but lower than balance, should fail. @@ -81,10 +82,10 @@ func TestPrecompilesBankThroughContract(r *runner.E2ERunner, args []string) { // Balances shouldn't change. balanceShouldBe(r, 0, checkCosmosBalance(r, testBank, zrc20Address, spender)) balanceShouldBe(r, 1000, checkZRC20Balance(r, spender)) - balanceShouldBe(r, 0, checkZRC20Balance(r, bank.ContractAddress)) + balanceShouldBe(r, 0, checkZRC20Balance(r, bankAddress)) // Allow 1000 ZRC20 to bank precompile. - approveAllowance(r, bank.ContractAddress, oneThousand) + approveAllowance(r, bankAddress, oneThousand) // Deposit 1001 ERC20ZRC20 tokens to the bank contract. // It's higher than spender balance but within approved allowance, should fail. @@ -94,7 +95,7 @@ func TestPrecompilesBankThroughContract(r *runner.E2ERunner, args []string) { // Balances shouldn't change. balanceShouldBe(r, 0, checkCosmosBalance(r, testBank, zrc20Address, spender)) balanceShouldBe(r, 1000, checkZRC20Balance(r, spender)) - balanceShouldBe(r, 0, checkZRC20Balance(r, bank.ContractAddress)) + balanceShouldBe(r, 0, checkZRC20Balance(r, bankAddress)) // Deposit 500 ERC20ZRC20 tokens to the bank contract, it's within allowance and balance. Should pass. receipt = depositThroughTestBank(r, testBank, zrc20Address, fiveHundred) @@ -103,7 +104,7 @@ func TestPrecompilesBankThroughContract(r *runner.E2ERunner, args []string) { // Balances should be transferred. Bank now locks 500 ZRC20 tokens. balanceShouldBe(r, 500, checkCosmosBalance(r, testBank, zrc20Address, spender)) balanceShouldBe(r, 500, checkZRC20Balance(r, spender)) - balanceShouldBe(r, 500, checkZRC20Balance(r, bank.ContractAddress)) + balanceShouldBe(r, 500, checkZRC20Balance(r, bankAddress)) // Check the deposit event. eventDeposit, err := bankPrecompileCaller.ParseDeposit(*receipt.Logs[0]) @@ -119,7 +120,7 @@ func TestPrecompilesBankThroughContract(r *runner.E2ERunner, args []string) { // Balances shouldn't change. balanceShouldBe(r, 500, checkCosmosBalance(r, testBank, zrc20Address, spender)) balanceShouldBe(r, 500, checkZRC20Balance(r, spender)) - balanceShouldBe(r, 500, checkZRC20Balance(r, bank.ContractAddress)) + balanceShouldBe(r, 500, checkZRC20Balance(r, bankAddress)) // Try to withdraw 500 ERC20ZRC20 tokens. Should pass. receipt = withdrawThroughTestBank(r, testBank, zrc20Address, fiveHundred) @@ -128,7 +129,7 @@ func TestPrecompilesBankThroughContract(r *runner.E2ERunner, args []string) { // Balances should be reverted to initial state. balanceShouldBe(r, 0, checkCosmosBalance(r, testBank, zrc20Address, spender)) balanceShouldBe(r, 1000, checkZRC20Balance(r, spender)) - balanceShouldBe(r, 0, checkZRC20Balance(r, bank.ContractAddress)) + balanceShouldBe(r, 0, checkZRC20Balance(r, bankAddress)) // Check the withdraw event. eventWithdraw, err := bankPrecompileCaller.ParseWithdraw(*receipt.Logs[0]) diff --git a/e2e/e2etests/test_precompiles_staking.go b/e2e/e2etests/test_precompiles_staking.go index 7b1fc44e1c..37a7e152de 100644 --- a/e2e/e2etests/test_precompiles_staking.go +++ b/e2e/e2etests/test_precompiles_staking.go @@ -13,6 +13,58 @@ import ( "github.com/zeta-chain/node/precompiles/staking" ) +func TestPrecompilesStakingIsDisabled(r *runner.E2ERunner, args []string) { + require.Len(r, args, 0, "No arguments expected") + + stakingContract, err := staking.NewIStaking(staking.ContractAddress, r.ZEVMClient) + require.NoError(r, err, "Failed to create staking contract caller") + + previousGasLimit := r.ZEVMAuth.GasLimit + r.ZEVMAuth.GasLimit = 10000000 + defer func() { + r.ZEVMAuth.GasLimit = previousGasLimit + }() + + validators, err := stakingContract.GetAllValidators(&bind.CallOpts{}) + require.NoError(r, err) + require.GreaterOrEqual(r, len(validators), 2) + + CleanValidatorDelegations(r, stakingContract, validators) + + // shares are 0 for both validators at the start + sharesBeforeVal1, err := stakingContract.GetShares(&bind.CallOpts{}, r.ZEVMAuth.From, validators[0].OperatorAddress) + require.NoError(r, err) + require.Equal(r, int64(0), sharesBeforeVal1.Int64()) + + sharesBeforeVal2, err := stakingContract.GetShares(&bind.CallOpts{}, r.ZEVMAuth.From, validators[1].OperatorAddress) + require.NoError(r, err) + require.Equal(r, int64(0), sharesBeforeVal2.Int64()) + + // stake 3 to validator1 + tx, err := stakingContract.Stake(r.ZEVMAuth, r.ZEVMAuth.From, validators[0].OperatorAddress, big.NewInt(3)) + require.NoError(r, err) + receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) + utils.RequiredTxFailed(r, receipt) + + // unstake 1 from validator1 + tx, err = stakingContract.Unstake(r.ZEVMAuth, r.ZEVMAuth.From, validators[0].OperatorAddress, big.NewInt(1)) + require.NoError(r, err) + receipt = utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) + utils.RequiredTxFailed(r, receipt) + + // move 1 stake from validator1 to validator2 + tx, err = stakingContract.MoveStake( + r.ZEVMAuth, + r.ZEVMAuth.From, + validators[0].OperatorAddress, + validators[1].OperatorAddress, + big.NewInt(1), + ) + require.NoError(r, err) + receipt = utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) + utils.RequiredTxFailed(r, receipt) +} + func TestPrecompilesStaking(r *runner.E2ERunner, args []string) { require.Len(r, args, 0, "No arguments expected") diff --git a/e2e/e2etests/test_solana_deposit_refund.go b/e2e/e2etests/test_solana_deposit_refund.go index e9155c9ddd..0a62b70ac6 100644 --- a/e2e/e2etests/test_solana_deposit_refund.go +++ b/e2e/e2etests/test_solana_deposit_refund.go @@ -31,6 +31,11 @@ func TestSolanaDepositAndCallRefund(r *runner.E2ERunner, args []string) { r.Logger.CCTX(*cctx, "solana_deposit_and_refund") utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_Reverted) - // check the status message contains revert error hash in case of revert - require.Contains(r, cctx.CctxStatus.StatusMessage, utils.ErrHashRevertFoo) + // Check the error carries the revert executed. + // tolerate the error in both the ErrorMessage field and the StatusMessage field + if cctx.CctxStatus.ErrorMessage != "" { + require.Contains(r, cctx.CctxStatus.ErrorMessage, "revert executed") + } else { + require.Contains(r, cctx.CctxStatus.StatusMessage, utils.ErrHashRevertFoo) + } } diff --git a/e2e/e2etests/test_solana_withdraw.go b/e2e/e2etests/test_solana_withdraw.go index 9ff5114c34..c7f6ccc58b 100644 --- a/e2e/e2etests/test_solana_withdraw.go +++ b/e2e/e2etests/test_solana_withdraw.go @@ -13,19 +13,22 @@ import ( func TestSolanaWithdraw(r *runner.E2ERunner, args []string) { require.Len(r, args, 1) + withdrawAmount := parseBigInt(r, args[0]) + // get ERC20 SOL balance before withdraw balanceBefore, err := r.SOLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) require.NoError(r, err) r.Logger.Info("runner balance of SOL before withdraw: %d", balanceBefore) + require.Equal(r, 1, balanceBefore.Cmp(withdrawAmount), "Insufficient balance for withdrawal") + // parse withdraw amount (in lamports), approve amount is 1 SOL approvedAmount := new(big.Int).SetUint64(solana.LAMPORTS_PER_SOL) - withdrawAmount := parseBigInt(r, args[0]) require.Equal( r, -1, withdrawAmount.Cmp(approvedAmount), - "Withdrawal amount must be less than the approved amount (1e9).", + "Withdrawal amount must be less than the approved amount (1e9)", ) // load deployer private key diff --git a/e2e/e2etests/test_ton_deposit.go b/e2e/e2etests/test_ton_deposit.go index a2e8df09d0..73860629df 100644 --- a/e2e/e2etests/test_ton_deposit.go +++ b/e2e/e2etests/test_ton_deposit.go @@ -1,42 +1,64 @@ package e2etests import ( + "time" + "cosmossdk.io/math" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/stretchr/testify/require" "github.com/zeta-chain/node/e2e/runner" "github.com/zeta-chain/node/e2e/runner/ton" + "github.com/zeta-chain/node/pkg/chains" + "github.com/zeta-chain/node/testutil/sample" + cctypes "github.com/zeta-chain/node/x/crosschain/types" ) -// TestTONDeposit (!) This boilerplate is a demonstration of E2E capabilities for TON integration -// Actual Deposit test is not implemented yet. -func TestTONDeposit(r *runner.E2ERunner, _ []string) { - ctx, deployer := r.Ctx, r.TONDeployer +func TestTONDeposit(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) // Given deployer - deployerBalance, err := deployer.GetBalance(ctx) - require.NoError(r, err, "failed to get deployer balance") - require.NotZero(r, deployerBalance, "deployer balance is zero") + ctx, deployer, chain := r.Ctx, r.TONDeployer, chains.TONLocalnet + + // Given amount + amount := parseUint(r, args[0]) + + // https://github.com/zeta-chain/protocol-contracts-ton/blob/main/contracts/gateway.fc#L28 + // (will be optimized & dynamic in the future) + depositFee := math.NewUint(10_000_000) // Given sample wallet with a balance of 50 TON sender, err := deployer.CreateWallet(ctx, ton.TONCoins(50)) require.NoError(r, err) - // That was funded (again) but the faucet - _, err = deployer.Fund(ctx, sender.GetAddress(), ton.TONCoins(30)) + // Given sample EVM address + recipient := sample.EthAddress() + + // ACT + err = r.TONDeposit(sender, amount, recipient) + + // ASSERT require.NoError(r, err) - // Check sender balance - sb, err := sender.GetBalance(ctx) + // Wait for CCTX mining + filter := func(cctx *cctypes.CrossChainTx) bool { + return cctx.InboundParams.SenderChainId == chain.ChainId && + cctx.InboundParams.Sender == sender.GetAddress().ToRaw() + } + + cctx := r.WaitForSpecificCCTX(filter, time.Minute) + + // Check CCTX + expectedDeposit := amount.Sub(depositFee) + + require.Equal(r, sender.GetAddress().ToRaw(), cctx.InboundParams.Sender) + require.Equal(r, expectedDeposit.Uint64(), cctx.InboundParams.Amount.Uint64()) + + // Check receiver's balance + balance, err := r.TONZRC20.BalanceOf(&bind.CallOpts{}, recipient) require.NoError(r, err) - senderBalance := math.NewUint(sb) + r.Logger.Info("Recipient's zEVM TON balance after deposit: %d", balance.Uint64()) - // note that it's not exactly 80 TON, but 79.99... due to gas fees - // We'll tackle gas math later. - r.Logger.Print( - "Balance of sender (%s): %s", - sender.GetAddress().ToHuman(false, true), - ton.FormatCoins(senderBalance), - ) + require.Equal(r, expectedDeposit.Uint64(), balance.Uint64()) } diff --git a/e2e/e2etests/test_ton_deposit_and_call.go b/e2e/e2etests/test_ton_deposit_and_call.go new file mode 100644 index 0000000000..43e5dcc4e0 --- /dev/null +++ b/e2e/e2etests/test_ton_deposit_and_call.go @@ -0,0 +1,69 @@ +package e2etests + +import ( + "time" + + "cosmossdk.io/math" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/node/e2e/runner" + "github.com/zeta-chain/node/e2e/runner/ton" + "github.com/zeta-chain/node/e2e/utils" + "github.com/zeta-chain/node/pkg/chains" + testcontract "github.com/zeta-chain/node/testutil/contracts" + cctypes "github.com/zeta-chain/node/x/crosschain/types" +) + +func TestTONDepositAndCall(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + // Given deployer + ctx, deployer, chain := r.Ctx, r.TONDeployer, chains.TONLocalnet + + // Given amount + amount := parseUint(r, args[0]) + + // https://github.com/zeta-chain/protocol-contracts-ton/blob/main/contracts/gateway.fc#L28 + // (will be optimized & dynamic in the future) + depositFee := math.NewUint(10_000_000) + + // Given sample wallet with a balance of 50 TON + sender, err := deployer.CreateWallet(ctx, ton.TONCoins(50)) + require.NoError(r, err) + + // Given sample zEVM contract + contractAddr, _, contract, err := testcontract.DeployExample(r.ZEVMAuth, r.ZEVMClient) + require.NoError(r, err) + r.Logger.Info("Example zevm contract deployed at: %s", contractAddr.String()) + + // Given call data + callData := []byte("hello from TON!") + + // ACT + err = r.TONDepositAndCall(sender, amount, contractAddr, callData) + + // ASSERT + require.NoError(r, err) + + // Wait for CCTX mining + filter := func(cctx *cctypes.CrossChainTx) bool { + return cctx.InboundParams.SenderChainId == chain.ChainId && + cctx.InboundParams.Sender == sender.GetAddress().ToRaw() + } + + r.WaitForSpecificCCTX(filter, time.Minute) + + expectedDeposit := amount.Sub(depositFee) + + // check if example contract has been called, bar value should be set to amount + utils.MustHaveCalledExampleContract(r, contract, expectedDeposit.BigInt()) + + // Check receiver's balance + balance, err := r.TONZRC20.BalanceOf(&bind.CallOpts{}, contractAddr) + require.NoError(r, err) + + r.Logger.Info("Contract's zEVM TON balance after deposit: %d", balance.Uint64()) + + require.Equal(r, expectedDeposit.Uint64(), balance.Uint64()) +} diff --git a/e2e/e2etests/test_v2_erc20_deposit_and_call.go b/e2e/e2etests/test_v2_erc20_deposit_and_call.go index 6328998e81..332f2d6009 100644 --- a/e2e/e2etests/test_v2_erc20_deposit_and_call.go +++ b/e2e/e2etests/test_v2_erc20_deposit_and_call.go @@ -37,7 +37,7 @@ func TestV2ERC20DepositAndCall(r *runner.E2ERunner, args []string) { // wait for the cctx to be mined cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) - r.Logger.CCTX(*cctx, "deposit") + r.Logger.CCTX(*cctx, "deposit_and_call") require.Equal(r, crosschaintypes.CctxStatus_OutboundMined, cctx.CctxStatus.Status) // check the payload was received on the contract diff --git a/e2e/e2etests/test_v2_erc20_deposit_and_call_no_message.go b/e2e/e2etests/test_v2_erc20_deposit_and_call_no_message.go new file mode 100644 index 0000000000..ff6adf74e6 --- /dev/null +++ b/e2e/e2etests/test_v2_erc20_deposit_and_call_no_message.go @@ -0,0 +1,48 @@ +package e2etests + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + + "github.com/zeta-chain/node/e2e/runner" + "github.com/zeta-chain/node/e2e/utils" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" +) + +func TestV2ERC20DepositAndCallNoMessage(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount, ok := big.NewInt(0).SetString(args[0], 10) + require.True(r, ok, "Invalid amount specified for TestV2ERC20DepositAndCallNoMessage") + + r.ApproveERC20OnEVM(r.GatewayEVMAddr) + + oldBalance, err := r.ERC20ZRC20.BalanceOf(&bind.CallOpts{}, r.TestDAppV2ZEVMAddr) + require.NoError(r, err) + + // perform the deposit + tx := r.V2ERC20DepositAndCall( + r.TestDAppV2ZEVMAddr, + amount, + []byte{}, + gatewayevm.RevertOptions{OnRevertGasLimit: big.NewInt(0)}, + ) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "deposit_and_call") + require.Equal(r, crosschaintypes.CctxStatus_OutboundMined, cctx.CctxStatus.Status) + + // check the payload was received on the contract + messageIndex, err := r.TestDAppV2ZEVM.GetNoMessageIndex(&bind.CallOpts{}, r.EVMAddress()) + require.NoError(r, err) + r.AssertTestDAppZEVMCalled(true, messageIndex, amount) + + // check the balance was updated + newBalance, err := r.ERC20ZRC20.BalanceOf(&bind.CallOpts{}, r.TestDAppV2ZEVMAddr) + require.NoError(r, err) + require.Equal(r, new(big.Int).Add(oldBalance, amount), newBalance) +} diff --git a/e2e/e2etests/test_v2_erc20_deposit_and_call_revert.go b/e2e/e2etests/test_v2_erc20_deposit_and_call_revert.go index ba2d031710..91d3f23a77 100644 --- a/e2e/e2etests/test_v2_erc20_deposit_and_call_revert.go +++ b/e2e/e2etests/test_v2_erc20_deposit_and_call_revert.go @@ -35,7 +35,7 @@ func TestV2ERC20DepositAndCallRevert(r *runner.E2ERunner, args []string) { // wait for the cctx to be reverted cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) - r.Logger.CCTX(*cctx, "deposit") + r.Logger.CCTX(*cctx, "deposit_and_call") require.Equal(r, crosschaintypes.CctxStatus_Reverted, cctx.CctxStatus.Status) // check the balance is more than 0 diff --git a/e2e/e2etests/test_v2_erc20_deposit_and_call_revert_with_call.go b/e2e/e2etests/test_v2_erc20_deposit_and_call_revert_with_call.go index 41b92fee4f..0bee6ff837 100644 --- a/e2e/e2etests/test_v2_erc20_deposit_and_call_revert_with_call.go +++ b/e2e/e2etests/test_v2_erc20_deposit_and_call_revert_with_call.go @@ -34,7 +34,7 @@ func TestV2ERC20DepositAndCallRevertWithCall(r *runner.E2ERunner, args []string) // wait for the cctx to be reverted cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) - r.Logger.CCTX(*cctx, "deposit") + r.Logger.CCTX(*cctx, "deposit_and_call") require.Equal(r, crosschaintypes.CctxStatus_Reverted, cctx.CctxStatus.Status) // check the payload was received on the contract diff --git a/e2e/e2etests/test_v2_erc20_withdraw_and_arbitrary_call.go b/e2e/e2etests/test_v2_erc20_withdraw_and_arbitrary_call.go new file mode 100644 index 0000000000..ca48f4be8b --- /dev/null +++ b/e2e/e2etests/test_v2_erc20_withdraw_and_arbitrary_call.go @@ -0,0 +1,41 @@ +package e2etests + +import ( + "math/big" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" + + "github.com/zeta-chain/node/e2e/runner" + "github.com/zeta-chain/node/e2e/utils" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" +) + +const payloadMessageWithdrawERC20 = "this is a test ERC20 withdraw and call payload" + +func TestV2ERC20WithdrawAndArbitraryCall(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount, ok := big.NewInt(0).SetString(args[0], 10) + require.True(r, ok, "Invalid amount specified for TestV2ERC20WithdrawAndCall") + + r.AssertTestDAppEVMCalled(false, payloadMessageWithdrawERC20, amount) + + r.ApproveERC20ZRC20(r.GatewayZEVMAddr) + r.ApproveETHZRC20(r.GatewayZEVMAddr) + + // perform the withdraw + tx := r.V2ERC20WithdrawAndArbitraryCall( + r.TestDAppV2EVMAddr, + amount, + r.EncodeERC20Call(r.ERC20Addr, amount, payloadMessageWithdrawERC20), + gatewayzevm.RevertOptions{OnRevertGasLimit: big.NewInt(0)}, + ) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "withdraw") + require.Equal(r, crosschaintypes.CctxStatus_OutboundMined, cctx.CctxStatus.Status) + + r.AssertTestDAppEVMCalled(true, payloadMessageWithdrawERC20, amount) +} diff --git a/e2e/e2etests/test_v2_erc20_withdraw_and_call.go b/e2e/e2etests/test_v2_erc20_withdraw_and_call.go index 6773e84f17..4596ba12da 100644 --- a/e2e/e2etests/test_v2_erc20_withdraw_and_call.go +++ b/e2e/e2etests/test_v2_erc20_withdraw_and_call.go @@ -3,6 +3,7 @@ package e2etests import ( "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/stretchr/testify/require" "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" @@ -11,15 +12,20 @@ import ( crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" ) -const payloadMessageWithdrawERC20 = "this is a test ERC20 withdraw and call payload" +const payloadMessageWithdrawAuthenticatedCallERC20 = "this is a test ERC20 withdraw and authenticated call payload" -func TestV2ERC20WithdrawAndCall(r *runner.E2ERunner, args []string) { - require.Len(r, args, 1) +func TestV2ERC20WithdrawAndCall(r *runner.E2ERunner, _ []string) { + previousGasLimit := r.ZEVMAuth.GasLimit + r.ZEVMAuth.GasLimit = 10000000 + defer func() { + r.ZEVMAuth.GasLimit = previousGasLimit + }() - amount, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok, "Invalid amount specified for TestV2ERC20WithdrawAndCall") + // called with fixed amount without arg since onCall implementation is for TestDappV2 is simple and generic + // without decoding the payload and amount handling for erc20, purpose of test is to verify correct sender and payload are used + amount := big.NewInt(10000) - r.AssertTestDAppEVMCalled(false, payloadMessageWithdrawERC20, amount) + r.AssertTestDAppEVMCalled(false, payloadMessageWithdrawAuthenticatedCallERC20, amount) r.ApproveERC20ZRC20(r.GatewayZEVMAddr) r.ApproveETHZRC20(r.GatewayZEVMAddr) @@ -28,7 +34,7 @@ func TestV2ERC20WithdrawAndCall(r *runner.E2ERunner, args []string) { tx := r.V2ERC20WithdrawAndCall( r.TestDAppV2EVMAddr, amount, - r.EncodeERC20Call(r.ERC20Addr, amount, payloadMessageWithdrawERC20), + []byte(payloadMessageWithdrawAuthenticatedCallERC20), gatewayzevm.RevertOptions{OnRevertGasLimit: big.NewInt(0)}, ) @@ -37,5 +43,13 @@ func TestV2ERC20WithdrawAndCall(r *runner.E2ERunner, args []string) { r.Logger.CCTX(*cctx, "withdraw") require.Equal(r, crosschaintypes.CctxStatus_OutboundMined, cctx.CctxStatus.Status) - r.AssertTestDAppEVMCalled(true, payloadMessageWithdrawERC20, amount) + r.AssertTestDAppEVMCalled(true, payloadMessageWithdrawAuthenticatedCallERC20, big.NewInt(0)) + + // check expected sender was used + senderForMsg, err := r.TestDAppV2EVM.SenderWithMessage( + &bind.CallOpts{}, + []byte(payloadMessageWithdrawAuthenticatedCallERC20), + ) + require.NoError(r, err) + require.Equal(r, r.ZEVMAuth.From, senderForMsg) } diff --git a/e2e/e2etests/test_v2_erc20_withdraw_and_call_no_message.go b/e2e/e2etests/test_v2_erc20_withdraw_and_call_no_message.go new file mode 100644 index 0000000000..50396ee58c --- /dev/null +++ b/e2e/e2etests/test_v2_erc20_withdraw_and_call_no_message.go @@ -0,0 +1,55 @@ +package e2etests + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" + + "github.com/zeta-chain/node/e2e/runner" + "github.com/zeta-chain/node/e2e/utils" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" +) + +func TestV2ERC20WithdrawAndCallNoMessage(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + previousGasLimit := r.ZEVMAuth.GasLimit + r.ZEVMAuth.GasLimit = 10000000 + defer func() { + r.ZEVMAuth.GasLimit = previousGasLimit + }() + + amount, ok := big.NewInt(0).SetString(args[0], 10) + require.True(r, ok, "Invalid amount specified for TestV2ERC20WithdrawAndCallNoMessage") + + r.ApproveERC20ZRC20(r.GatewayZEVMAddr) + r.ApproveETHZRC20(r.GatewayZEVMAddr) + + // perform the withdraw + tx := r.V2ERC20WithdrawAndCall( + r.TestDAppV2EVMAddr, + amount, + []byte{}, + gatewayzevm.RevertOptions{OnRevertGasLimit: big.NewInt(0)}, + ) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "withdraw_and_call") + require.Equal(r, crosschaintypes.CctxStatus_OutboundMined, cctx.CctxStatus.Status) + + // check called + messageIndex, err := r.TestDAppV2EVM.GetNoMessageIndex(&bind.CallOpts{}, r.EVMAddress()) + require.NoError(r, err) + r.AssertTestDAppEVMCalled(true, messageIndex, amount) + + // check expected sender was used + senderForMsg, err := r.TestDAppV2EVM.SenderWithMessage( + &bind.CallOpts{}, + []byte(messageIndex), + ) + require.NoError(r, err) + require.Equal(r, r.ZEVMAuth.From, senderForMsg) +} diff --git a/e2e/e2etests/test_v2_erc20_withdraw_and_call_revert.go b/e2e/e2etests/test_v2_erc20_withdraw_and_call_revert.go index 8454d09710..8ca60a238b 100644 --- a/e2e/e2etests/test_v2_erc20_withdraw_and_call_revert.go +++ b/e2e/e2etests/test_v2_erc20_withdraw_and_call_revert.go @@ -29,7 +29,7 @@ func TestV2ERC20WithdrawAndCallRevert(r *runner.E2ERunner, args []string) { require.EqualValues(r, int64(0), balance.Int64()) // perform the withdraw - tx := r.V2ERC20WithdrawAndCall( + tx := r.V2ERC20WithdrawAndArbitraryCall( r.TestDAppV2EVMAddr, amount, r.EncodeERC20CallRevert(r.ERC20Addr, amount), diff --git a/e2e/e2etests/test_v2_erc20_withdraw_and_call_revert_with_call.go b/e2e/e2etests/test_v2_erc20_withdraw_and_call_revert_with_call.go index 45bab52ae6..fb3b3201ff 100644 --- a/e2e/e2etests/test_v2_erc20_withdraw_and_call_revert_with_call.go +++ b/e2e/e2etests/test_v2_erc20_withdraw_and_call_revert_with_call.go @@ -26,7 +26,7 @@ func TestV2ERC20WithdrawAndCallRevertWithCall(r *runner.E2ERunner, args []string r.ApproveETHZRC20(r.GatewayZEVMAddr) // perform the withdraw - tx := r.V2ERC20WithdrawAndCall( + tx := r.V2ERC20WithdrawAndArbitraryCall( r.TestDAppV2EVMAddr, amount, r.EncodeERC20CallRevert(r.ERC20Addr, amount), diff --git a/e2e/e2etests/test_v2_eth_deposit_and_call_no_message.go b/e2e/e2etests/test_v2_eth_deposit_and_call_no_message.go new file mode 100644 index 0000000000..be8b55cdc8 --- /dev/null +++ b/e2e/e2etests/test_v2_eth_deposit_and_call_no_message.go @@ -0,0 +1,46 @@ +package e2etests + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + + "github.com/zeta-chain/node/e2e/runner" + "github.com/zeta-chain/node/e2e/utils" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" +) + +func TestV2ETHDepositAndCallNoMessage(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount, ok := big.NewInt(0).SetString(args[0], 10) + require.True(r, ok, "Invalid amount specified for TestV2ETHDepositAndCallNoMessage") + + oldBalance, err := r.ETHZRC20.BalanceOf(&bind.CallOpts{}, r.TestDAppV2ZEVMAddr) + require.NoError(r, err) + + // perform the deposit and call to the TestDAppV2ZEVMAddr + tx := r.V2ETHDepositAndCall( + r.TestDAppV2ZEVMAddr, + amount, + []byte{}, + gatewayevm.RevertOptions{OnRevertGasLimit: big.NewInt(0)}, + ) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "deposit_and_call") + require.Equal(r, crosschaintypes.CctxStatus_OutboundMined, cctx.CctxStatus.Status) + + // check the payload was received on the contract + messageIndex, err := r.TestDAppV2ZEVM.GetNoMessageIndex(&bind.CallOpts{}, r.EVMAddress()) + require.NoError(r, err) + r.AssertTestDAppZEVMCalled(true, messageIndex, amount) + + // check the balance was updated + newBalance, err := r.ETHZRC20.BalanceOf(&bind.CallOpts{}, r.TestDAppV2ZEVMAddr) + require.NoError(r, err) + require.Equal(r, new(big.Int).Add(oldBalance, amount), newBalance) +} diff --git a/e2e/e2etests/test_v2_eth_withdraw_and_arbitrary_call.go b/e2e/e2etests/test_v2_eth_withdraw_and_arbitrary_call.go index 932034d963..b290e33fcb 100644 --- a/e2e/e2etests/test_v2_eth_withdraw_and_arbitrary_call.go +++ b/e2e/e2etests/test_v2_eth_withdraw_and_arbitrary_call.go @@ -24,7 +24,7 @@ func TestV2ETHWithdrawAndArbitraryCall(r *runner.E2ERunner, args []string) { r.ApproveETHZRC20(r.GatewayZEVMAddr) // perform the withdraw - tx := r.V2ETHWithdrawAndCall( + tx := r.V2ETHWithdrawAndArbitraryCall( r.TestDAppV2EVMAddr, amount, r.EncodeGasCall(payloadMessageWithdrawETH), diff --git a/e2e/e2etests/test_v2_eth_withdraw_and_call.go b/e2e/e2etests/test_v2_eth_withdraw_and_call.go index de0cadabcf..bffd037e72 100644 --- a/e2e/e2etests/test_v2_eth_withdraw_and_call.go +++ b/e2e/e2etests/test_v2_eth_withdraw_and_call.go @@ -30,13 +30,8 @@ func TestV2ETHWithdrawAndCall(r *runner.E2ERunner, args []string) { r.ApproveETHZRC20(r.GatewayZEVMAddr) - // set expected sender - tx, err := r.TestDAppV2EVM.SetExpectedOnCallSender(r.EVMAuth, r.ZEVMAuth.From) - require.NoError(r, err) - utils.MustWaitForTxReceipt(r.Ctx, r.EVMClient, tx, r.Logger, r.ReceiptTimeout) - // perform the withdraw - tx = r.V2ETHWithdrawAndAuthenticatedCall( + tx := r.V2ETHWithdrawAndCall( r.TestDAppV2EVMAddr, amount, []byte(payloadMessageAuthenticatedWithdrawETH), diff --git a/e2e/e2etests/test_v2_eth_withdraw_and_call_no_message.go b/e2e/e2etests/test_v2_eth_withdraw_and_call_no_message.go new file mode 100644 index 0000000000..2e7531f9f1 --- /dev/null +++ b/e2e/e2etests/test_v2_eth_withdraw_and_call_no_message.go @@ -0,0 +1,54 @@ +package e2etests + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" + + "github.com/zeta-chain/node/e2e/runner" + "github.com/zeta-chain/node/e2e/utils" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" +) + +func TestV2ETHWithdrawAndCallNoMessage(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + previousGasLimit := r.ZEVMAuth.GasLimit + r.ZEVMAuth.GasLimit = 10000000 + defer func() { + r.ZEVMAuth.GasLimit = previousGasLimit + }() + + amount, ok := big.NewInt(0).SetString(args[0], 10) + require.True(r, ok, "Invalid amount specified for TestV2ETHWithdrawAndCallNoMessage") + + r.ApproveETHZRC20(r.GatewayZEVMAddr) + + // perform the withdraw + tx := r.V2ETHWithdrawAndCall( + r.TestDAppV2EVMAddr, + amount, + []byte{}, + gatewayzevm.RevertOptions{OnRevertGasLimit: big.NewInt(0)}, + ) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "withdraw_and_call") + require.Equal(r, crosschaintypes.CctxStatus_OutboundMined, cctx.CctxStatus.Status) + + // check called + messageIndex, err := r.TestDAppV2EVM.GetNoMessageIndex(&bind.CallOpts{}, r.EVMAddress()) + require.NoError(r, err) + r.AssertTestDAppEVMCalled(true, messageIndex, amount) + + // check expected sender was used + senderForMsg, err := r.TestDAppV2EVM.SenderWithMessage( + &bind.CallOpts{}, + []byte(messageIndex), + ) + require.NoError(r, err) + require.Equal(r, r.ZEVMAuth.From, senderForMsg) +} diff --git a/e2e/e2etests/test_v2_eth_withdraw_and_call_revert.go b/e2e/e2etests/test_v2_eth_withdraw_and_call_revert.go index 6df64c9a69..bfa0172e7b 100644 --- a/e2e/e2etests/test_v2_eth_withdraw_and_call_revert.go +++ b/e2e/e2etests/test_v2_eth_withdraw_and_call_revert.go @@ -28,10 +28,15 @@ func TestV2ETHWithdrawAndCallRevert(r *runner.E2ERunner, args []string) { require.EqualValues(r, int64(0), balance.Int64()) // perform the withdraw - tx := r.V2ETHWithdrawAndCall(r.TestDAppV2EVMAddr, amount, r.EncodeGasCall("revert"), gatewayzevm.RevertOptions{ - RevertAddress: revertAddress, - OnRevertGasLimit: big.NewInt(0), - }) + tx := r.V2ETHWithdrawAndArbitraryCall( + r.TestDAppV2EVMAddr, + amount, + r.EncodeGasCall("revert"), + gatewayzevm.RevertOptions{ + RevertAddress: revertAddress, + OnRevertGasLimit: big.NewInt(0), + }, + ) // wait for the cctx to be mined cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) diff --git a/e2e/e2etests/test_v2_eth_withdraw_and_call_revert_with_call.go b/e2e/e2etests/test_v2_eth_withdraw_and_call_revert_with_call.go index f4d31d5d14..f613ebffc2 100644 --- a/e2e/e2etests/test_v2_eth_withdraw_and_call_revert_with_call.go +++ b/e2e/e2etests/test_v2_eth_withdraw_and_call_revert_with_call.go @@ -25,7 +25,7 @@ func TestV2ETHWithdrawAndCallRevertWithCall(r *runner.E2ERunner, args []string) r.ApproveETHZRC20(r.GatewayZEVMAddr) // perform the withdraw - tx := r.V2ETHWithdrawAndCall( + tx := r.V2ETHWithdrawAndArbitraryCall( r.TestDAppV2EVMAddr, amount, r.EncodeGasCall("revert"), diff --git a/e2e/e2etests/test_v2_eth_withdraw_and_call_through_contract.go b/e2e/e2etests/test_v2_eth_withdraw_and_call_through_contract.go index f2d4949032..28c36ee69d 100644 --- a/e2e/e2etests/test_v2_eth_withdraw_and_call_through_contract.go +++ b/e2e/e2etests/test_v2_eth_withdraw_and_call_through_contract.go @@ -40,13 +40,8 @@ func TestV2ETHWithdrawAndCallThroughContract(r *runner.E2ERunner, args []string) require.NoError(r, err) utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) - // set expected sender - tx, err = r.TestDAppV2EVM.SetExpectedOnCallSender(r.EVMAuth, gatewayCallerAddr) - require.NoError(r, err) - utils.MustWaitForTxReceipt(r.Ctx, r.EVMClient, tx, r.Logger, r.ReceiptTimeout) - // perform the authenticated call - tx = r.V2ETHWithdrawAndAuthenticatedCallThroughContract(gatewayCaller, r.TestDAppV2EVMAddr, + tx = r.V2ETHWithdrawAndCallThroughContract(gatewayCaller, r.TestDAppV2EVMAddr, amount, []byte(payloadMessageAuthenticatedWithdrawETHThroughContract), gatewayzevmcaller.RevertOptions{OnRevertGasLimit: big.NewInt(0)}) @@ -65,20 +60,4 @@ func TestV2ETHWithdrawAndCallThroughContract(r *runner.E2ERunner, args []string) ) require.NoError(r, err) require.Equal(r, gatewayCallerAddr, senderForMsg) - - // set expected sender to wrong one - tx, err = r.TestDAppV2EVM.SetExpectedOnCallSender(r.EVMAuth, r.ZEVMAuth.From) - require.NoError(r, err) - utils.MustWaitForTxReceipt(r.Ctx, r.EVMClient, tx, r.Logger, r.ReceiptTimeout) - - // repeat authenticated call through contract, should revert because of wrong sender - tx = r.V2ETHWithdrawAndAuthenticatedCallThroughContract(gatewayCaller, r.TestDAppV2EVMAddr, - amount, - []byte(payloadMessageAuthenticatedWithdrawETHThroughContract), - gatewayzevmcaller.RevertOptions{OnRevertGasLimit: big.NewInt(0)}) - - utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) - cctx = utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) - r.Logger.CCTX(*cctx, "withdraw") - require.Equal(r, crosschaintypes.CctxStatus_Reverted, cctx.CctxStatus.Status) } diff --git a/e2e/e2etests/test_v2_zevm_to_evm_arbitrary_call.go b/e2e/e2etests/test_v2_zevm_to_evm_arbitrary_call.go index 21104767f9..4b722fc4ae 100644 --- a/e2e/e2etests/test_v2_zevm_to_evm_arbitrary_call.go +++ b/e2e/e2etests/test_v2_zevm_to_evm_arbitrary_call.go @@ -22,9 +22,13 @@ func TestV2ZEVMToEVMArbitraryCall(r *runner.E2ERunner, args []string) { r.ApproveETHZRC20(r.GatewayZEVMAddr) // perform the call - tx := r.V2ZEVMToEMVCall(r.TestDAppV2EVMAddr, r.EncodeSimpleCall(payloadMessageEVMCall), gatewayzevm.RevertOptions{ - OnRevertGasLimit: big.NewInt(0), - }) + tx := r.V2ZEVMToEMVArbitraryCall( + r.TestDAppV2EVMAddr, + r.EncodeSimpleCall(payloadMessageEVMCall), + gatewayzevm.RevertOptions{ + OnRevertGasLimit: big.NewInt(0), + }, + ) // wait for the cctx to be mined cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) diff --git a/e2e/e2etests/test_v2_zevm_to_evm_call.go b/e2e/e2etests/test_v2_zevm_to_evm_call.go index ba9d5fba6f..9641778e3c 100644 --- a/e2e/e2etests/test_v2_zevm_to_evm_call.go +++ b/e2e/e2etests/test_v2_zevm_to_evm_call.go @@ -22,13 +22,8 @@ func TestV2ZEVMToEVMCall(r *runner.E2ERunner, args []string) { // necessary approval for fee payment r.ApproveETHZRC20(r.GatewayZEVMAddr) - // set expected sender - tx, err := r.TestDAppV2EVM.SetExpectedOnCallSender(r.EVMAuth, r.ZEVMAuth.From) - require.NoError(r, err) - utils.MustWaitForTxReceipt(r.Ctx, r.EVMClient, tx, r.Logger, r.ReceiptTimeout) - // perform the authenticated call - tx = r.V2ZEVMToEMVAuthenticatedCall( + tx := r.V2ZEVMToEMVCall( r.TestDAppV2EVMAddr, []byte(payloadMessageEVMAuthenticatedCall), gatewayzevm.RevertOptions{ diff --git a/e2e/e2etests/test_v2_zevm_to_evm_call_through_contract.go b/e2e/e2etests/test_v2_zevm_to_evm_call_through_contract.go index a46b9f09f9..7ff9158365 100644 --- a/e2e/e2etests/test_v2_zevm_to_evm_call_through_contract.go +++ b/e2e/e2etests/test_v2_zevm_to_evm_call_through_contract.go @@ -33,13 +33,8 @@ func TestV2ZEVMToEVMCallThroughContract(r *runner.E2ERunner, args []string) { require.NoError(r, err) utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) - // set expected sender - tx, err = r.TestDAppV2EVM.SetExpectedOnCallSender(r.EVMAuth, gatewayCallerAddr) - require.NoError(r, err) - utils.MustWaitForTxReceipt(r.Ctx, r.EVMClient, tx, r.Logger, r.ReceiptTimeout) - // perform the authenticated call - tx = r.V2ZEVMToEMVAuthenticatedCallThroughContract( + tx = r.V2ZEVMToEMVCallThroughContract( gatewayCaller, r.TestDAppV2EVMAddr, []byte(payloadMessageEVMAuthenticatedCallThroughContract), @@ -61,23 +56,4 @@ func TestV2ZEVMToEVMCallThroughContract(r *runner.E2ERunner, args []string) { ) require.NoError(r, err) require.Equal(r, gatewayCallerAddr, senderForMsg) - - // set expected sender to wrong one - tx, err = r.TestDAppV2EVM.SetExpectedOnCallSender(r.EVMAuth, r.ZEVMAuth.From) - require.NoError(r, err) - utils.MustWaitForTxReceipt(r.Ctx, r.EVMClient, tx, r.Logger, r.ReceiptTimeout) - - // repeat authenticated call through contract, should revert because of wrong sender - tx = r.V2ZEVMToEMVAuthenticatedCallThroughContract( - gatewayCaller, - r.TestDAppV2EVMAddr, - []byte(payloadMessageEVMAuthenticatedCallThroughContract), - gatewayzevmcaller.RevertOptions{ - OnRevertGasLimit: big.NewInt(0), - }, - ) - utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) - cctx = utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) - r.Logger.CCTX(*cctx, "call") - require.Equal(r, crosschaintypes.CctxStatus_Reverted, cctx.CctxStatus.Status) } diff --git a/e2e/runner/admin_evm.go b/e2e/runner/admin_evm.go index ed3f682d0b..88b0a1121d 100644 --- a/e2e/runner/admin_evm.go +++ b/e2e/runner/admin_evm.go @@ -24,13 +24,14 @@ func (r *E2ERunner) UpdateTssAddressForConnector() { func (r *E2ERunner) UpdateTssAddressForErc20custody() { require.NoError(r, r.SetTSSAddresses()) - tx, err := r.ERC20Custody.UpdateTSSAddress(r.EVMAuth, r.TSSAddress) + tx, err := r.ERC20CustodyV2.UpdateTSSAddress(r.EVMAuth, r.TSSAddress) require.NoError(r, err) r.Logger.Info("TSS ERC20 Address Update Tx: %s", tx.Hash().String()) receipt := utils.MustWaitForTxReceipt(r.Ctx, r.EVMClient, tx, r.Logger, r.ReceiptTimeout) utils.RequireTxSuccessful(r, receipt) - tssAddressOnCustody, err := r.ERC20Custody.TSSAddress(&bind.CallOpts{Context: r.Ctx}) + // we have to reference ERC20CustodyV2 since it's `TssAddress` on v2 and `TSSAddress` on v1 + tssAddressOnCustody, err := r.ERC20CustodyV2.TssAddress(&bind.CallOpts{Context: r.Ctx}) require.NoError(r, err) require.Equal(r, r.TSSAddress, tssAddressOnCustody) } diff --git a/e2e/runner/bitcoin.go b/e2e/runner/bitcoin.go index 6fbd6b40d7..d2c04fccf0 100644 --- a/e2e/runner/bitcoin.go +++ b/e2e/runner/bitcoin.go @@ -2,7 +2,9 @@ package runner import ( "bytes" + "encoding/hex" "fmt" + "net/http" "sort" "time" @@ -177,9 +179,17 @@ func (r *E2ERunner) SendToTSSFromDeployerWithMemo( amount float64, inputUTXOs []btcjson.ListUnspentResult, memo []byte, +) (*chainhash.Hash, error) { + return r.sendToAddrFromDeployerWithMemo(amount, r.BTCTSSAddress, inputUTXOs, memo) +} + +func (r *E2ERunner) sendToAddrFromDeployerWithMemo( + amount float64, + to btcutil.Address, + inputUTXOs []btcjson.ListUnspentResult, + memo []byte, ) (*chainhash.Hash, error) { btcRPC := r.BtcRPCClient - to := r.BTCTSSAddress btcDeployerAddress := r.BTCDeployerAddress require.NotNil(r, r.BTCDeployerAddress, "btcDeployerAddress is nil") @@ -288,6 +298,49 @@ func (r *E2ERunner) SendToTSSFromDeployerWithMemo( return txid, nil } +// InscribeToTSSFromDeployerWithMemo creates an inscription that is sent to the tss address with the corresponding memo +func (r *E2ERunner) InscribeToTSSFromDeployerWithMemo( + amount float64, + inputUTXOs []btcjson.ListUnspentResult, + memo []byte, +) *chainhash.Hash { + // TODO: replace builder with Go function to enable instructions + // https://github.com/zeta-chain/node/issues/2759 + builder := InscriptionBuilder{sidecarURL: "http://bitcoin-node-sidecar:8000", client: http.Client{}} + + address, err := builder.GenerateCommitAddress(memo) + require.NoError(r, err) + r.Logger.Info("received inscription commit address %s", address) + + receiver, err := chains.DecodeBtcAddress(address, r.GetBitcoinChainID()) + require.NoError(r, err) + + txnHash, err := r.sendToAddrFromDeployerWithMemo(amount, receiver, inputUTXOs, []byte(constant.DonationMessage)) + require.NoError(r, err) + r.Logger.Info("obtained inscription commit txn hash %s", txnHash.String()) + + // sendToAddrFromDeployerWithMemo makes sure index is 0 + outpointIdx := 0 + hexTx, err := builder.GenerateRevealTxn(r.BTCTSSAddress.String(), txnHash.String(), outpointIdx, amount) + require.NoError(r, err) + + // Decode the hex string into raw bytes + rawTxBytes, err := hex.DecodeString(hexTx) + require.NoError(r, err) + + // Deserialize the raw bytes into a wire.MsgTx structure + msgTx := wire.NewMsgTx(wire.TxVersion) + err = msgTx.Deserialize(bytes.NewReader(rawTxBytes)) + require.NoError(r, err) + r.Logger.Info("recovered inscription reveal txn %s", hexTx) + + txid, err := r.BtcRPCClient.SendRawTransaction(msgTx, true) + require.NoError(r, err) + r.Logger.Info("txid: %+v", txid) + + return txid +} + // GetBitcoinChainID gets the bitcoin chain ID from the network params func (r *E2ERunner) GetBitcoinChainID() int64 { chainID, err := chains.BitcoinChainIDFromNetworkName(r.BitcoinParams.Name) diff --git a/e2e/runner/bitcoin_inscription.go b/e2e/runner/bitcoin_inscription.go new file mode 100644 index 0000000000..6f90068905 --- /dev/null +++ b/e2e/runner/bitcoin_inscription.go @@ -0,0 +1,119 @@ +package runner + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "net/http" + + "github.com/pkg/errors" +) + +type commitResponse struct { + Address string `json:"address"` +} + +type revealResponse struct { + RawHex string `json:"rawHex"` +} + +type revealRequest struct { + Txn string `json:"txn"` + Idx int `json:"idx"` + Amount int `json:"amount"` + FeeRate int `json:"feeRate"` + To string `json:"to"` +} + +// InscriptionBuilder is a util struct that help create inscription commit and reveal transactions +type InscriptionBuilder struct { + sidecarURL string + client http.Client +} + +// GenerateCommitAddress generates a commit p2tr address that one can send funds to this address +func (r *InscriptionBuilder) GenerateCommitAddress(memo []byte) (string, error) { + // Create the payload + postData := map[string]string{ + "memo": hex.EncodeToString(memo), + } + + // Convert the payload to JSON + jsonData, err := json.Marshal(postData) + if err != nil { + return "", err + } + + postURL := r.sidecarURL + "/commit" + req, err := http.NewRequest("POST", postURL, bytes.NewBuffer(jsonData)) + if err != nil { + return "", errors.Wrap(err, "cannot create commit request") + } + req.Header.Set("Content-Type", "application/json") + + // Send the request + resp, err := r.client.Do(req) + if err != nil { + return "", errors.Wrap(err, "cannot send to sidecar") + } + defer resp.Body.Close() + + // Read the response body + var response commitResponse + err = json.NewDecoder(resp.Body).Decode(&response) + if err != nil { + return "", err + } + + fmt.Print("raw commit response ", response.Address) + + return response.Address, nil +} + +// GenerateRevealTxn creates the corresponding reveal txn to the commit txn. +func (r *InscriptionBuilder) GenerateRevealTxn(to string, txnHash string, idx int, amount float64) (string, error) { + postData := revealRequest{ + Txn: txnHash, + Idx: idx, + Amount: int(amount * 100000000), + FeeRate: 10, + To: to, + } + + // Convert the payload to JSON + jsonData, err := json.Marshal(postData) + if err != nil { + return "", err + } + + postURL := r.sidecarURL + "/reveal" + req, err := http.NewRequest("POST", postURL, bytes.NewBuffer(jsonData)) + if err != nil { + return "", errors.Wrap(err, "cannot create reveal request") + } + req.Header.Set("Content-Type", "application/json") + + // Send the request + resp, err := r.client.Do(req) + if err != nil { + return "", errors.Wrap(err, "cannot send reveal to sidecar") + } + defer resp.Body.Close() + + // Read the response body + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", errors.Wrap(err, "cannot read reveal response body") + } + + // Parse the JSON response + var response revealResponse + if err := json.Unmarshal(body, &response); err != nil { + return "", errors.Wrap(err, "cannot parse reveal response body") + } + + // Access the "address" field + return response.RawHex, nil +} diff --git a/e2e/runner/runner.go b/e2e/runner/runner.go index 57e5ab9a24..03cafb6fc4 100644 --- a/e2e/runner/runner.go +++ b/e2e/runner/runner.go @@ -18,7 +18,6 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" - "github.com/tonkeeper/tongo/ton" "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/erc20custody.sol" zetaeth "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/zeta.eth.sol" zetaconnectoreth "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/zetaconnector.eth.sol" @@ -40,6 +39,7 @@ import ( "github.com/zeta-chain/node/e2e/txserver" "github.com/zeta-chain/node/e2e/utils" "github.com/zeta-chain/node/pkg/contracts/testdappv2" + toncontracts "github.com/zeta-chain/node/pkg/contracts/ton" authoritytypes "github.com/zeta-chain/node/x/authority/types" crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" fungibletypes "github.com/zeta-chain/node/x/fungible/types" @@ -73,7 +73,7 @@ type E2ERunner struct { BTCDeployerAddress *btcutil.AddressWitnessPubKeyHash SolanaDeployerAddress solana.PublicKey TONDeployer *tonrunner.Deployer - TONGateway ton.AccountID + TONGateway *toncontracts.Gateway // all clients. // a reference to this type is required to enable creating a new E2ERunner. @@ -127,6 +127,8 @@ type E2ERunner struct { BTCZRC20 *zrc20.ZRC20 SOLZRC20Addr ethcommon.Address SOLZRC20 *zrc20.ZRC20 + TONZRC20Addr ethcommon.Address + TONZRC20 *zrc20.ZRC20 UniswapV2FactoryAddr ethcommon.Address UniswapV2Factory *uniswapv2factory.UniswapV2Factory UniswapV2RouterAddr ethcommon.Address @@ -230,6 +232,7 @@ func (r *E2ERunner) CopyAddressesFrom(other *E2ERunner) (err error) { r.ETHZRC20Addr = other.ETHZRC20Addr r.BTCZRC20Addr = other.BTCZRC20Addr r.SOLZRC20Addr = other.SOLZRC20Addr + r.TONZRC20Addr = other.TONZRC20Addr r.UniswapV2FactoryAddr = other.UniswapV2FactoryAddr r.UniswapV2RouterAddr = other.UniswapV2RouterAddr r.ConnectorZEVMAddr = other.ConnectorZEVMAddr @@ -275,6 +278,10 @@ func (r *E2ERunner) CopyAddressesFrom(other *E2ERunner) (err error) { if err != nil { return err } + r.TONZRC20, err = zrc20.NewZRC20(r.TONZRC20Addr, r.ZEVMClient) + if err != nil { + return err + } r.UniswapV2Factory, err = uniswapv2factory.NewUniswapV2Factory(r.UniswapV2FactoryAddr, r.ZEVMClient) if err != nil { return err @@ -359,6 +366,7 @@ func (r *E2ERunner) PrintContractAddresses() { r.Logger.Print("ERC20ZRC20: %s", r.ERC20ZRC20Addr.Hex()) r.Logger.Print("BTCZRC20: %s", r.BTCZRC20Addr.Hex()) r.Logger.Print("SOLZRC20: %s", r.SOLZRC20Addr.Hex()) + r.Logger.Print("TONZRC20: %s", r.TONZRC20Addr.Hex()) r.Logger.Print("UniswapFactory: %s", r.UniswapV2FactoryAddr.Hex()) r.Logger.Print("UniswapRouter: %s", r.UniswapV2RouterAddr.Hex()) r.Logger.Print("ConnectorZEVM: %s", r.ConnectorZEVMAddr.Hex()) diff --git a/e2e/runner/setup_solana.go b/e2e/runner/setup_solana.go index 8f209dd23e..17bf3149dd 100644 --- a/e2e/runner/setup_solana.go +++ b/e2e/runner/setup_solana.go @@ -1,14 +1,20 @@ package runner import ( + "time" + ethcommon "github.com/ethereum/go-ethereum/common" "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" "github.com/near/borsh-go" + "github.com/pkg/errors" "github.com/stretchr/testify/require" + "github.com/zeta-chain/node/e2e/utils" "github.com/zeta-chain/node/pkg/chains" + "github.com/zeta-chain/node/pkg/constant" solanacontracts "github.com/zeta-chain/node/pkg/contracts/solana" + observertypes "github.com/zeta-chain/node/x/observer/types" ) // SetupSolanaAccount imports the deployer's private key @@ -20,8 +26,8 @@ func (r *E2ERunner) SetupSolanaAccount() { r.Logger.Info("SolanaDeployerAddress: %s", r.SolanaDeployerAddress) } -// SetSolanaContracts set Solana contracts -func (r *E2ERunner) SetSolanaContracts(deployerPrivateKey string) { +// SetupSolana sets Solana contracts and params +func (r *E2ERunner) SetupSolana(deployerPrivateKey string) { r.Logger.Print("⚙️ initializing gateway program on Solana") // set Solana contracts @@ -78,4 +84,64 @@ func (r *E2ERunner) SetSolanaContracts(deployerPrivateKey string) { balance, err := r.SolanaClient.GetBalance(r.Ctx, pdaComputed, rpc.CommitmentConfirmed) require.NoError(r, err) r.Logger.Info("initial PDA balance: %d lamports", balance.Value) + + err = r.ensureSolanaChainParams() + require.NoError(r, err) +} + +func (r *E2ERunner) ensureSolanaChainParams() error { + if r.ZetaTxServer == nil { + return errors.New("ZetaTxServer is not initialized") + } + + creator := r.ZetaTxServer.MustGetAccountAddressFromName(utils.OperationalPolicyName) + + chainID := chains.SolanaLocalnet.ChainId + + chainParams := &observertypes.ChainParams{ + ChainId: chainID, + ConfirmationCount: 32, + ZetaTokenContractAddress: constant.EVMZeroAddress, + ConnectorContractAddress: constant.EVMZeroAddress, + Erc20CustodyContractAddress: constant.EVMZeroAddress, + GasPriceTicker: 5, + WatchUtxoTicker: 0, + InboundTicker: 2, + OutboundTicker: 2, + OutboundScheduleInterval: 2, + OutboundScheduleLookahead: 5, + BallotThreshold: observertypes.DefaultBallotThreshold, + MinObserverDelegation: observertypes.DefaultMinObserverDelegation, + IsSupported: true, + GatewayAddress: solanacontracts.SolanaGatewayProgramID, + } + + updateMsg := observertypes.NewMsgUpdateChainParams(creator, chainParams) + + if _, err := r.ZetaTxServer.BroadcastTx(utils.OperationalPolicyName, updateMsg); err != nil { + return errors.Wrap(err, "unable to broadcast solana chain params tx") + } + + resetMsg := observertypes.NewMsgResetChainNonces(creator, chainID, 0, 0) + if _, err := r.ZetaTxServer.BroadcastTx(utils.OperationalPolicyName, resetMsg); err != nil { + return errors.Wrap(err, "unable to broadcast solana chain nonce reset tx") + } + + r.Logger.Print("⚙️ voted for adding solana chain params (localnet). Waiting for confirmation") + + query := &observertypes.QueryGetChainParamsForChainRequest{ChainId: chainID} + + const duration = 2 * time.Second + + for i := 0; i < 10; i++ { + _, err := r.ObserverClient.GetChainParamsForChain(r.Ctx, query) + if err == nil { + r.Logger.Print("⚙️ solana chain params are set") + return nil + } + + time.Sleep(duration) + } + + return errors.New("unable to set Solana chain params") } diff --git a/e2e/runner/setup_ton.go b/e2e/runner/setup_ton.go index 9b744401ad..62d89c3329 100644 --- a/e2e/runner/setup_ton.go +++ b/e2e/runner/setup_ton.go @@ -2,10 +2,16 @@ package runner import ( "fmt" + "time" "github.com/pkg/errors" "github.com/zeta-chain/node/e2e/runner/ton" + "github.com/zeta-chain/node/e2e/utils" + "github.com/zeta-chain/node/pkg/chains" + "github.com/zeta-chain/node/pkg/constant" + toncontracts "github.com/zeta-chain/node/pkg/contracts/ton" + observertypes "github.com/zeta-chain/node/x/observer/types" ) // SetupTON setups TON deployer and deploys Gateway contract @@ -42,7 +48,12 @@ func (r *E2ERunner) SetupTON() error { return errors.Wrapf(err, "unable to deploy TON gateway") } - r.Logger.Print("💎TON Gateway deployed %s (%s)", gwAccount.ID.ToRaw(), gwAccount.ID.ToHuman(false, true)) + r.Logger.Print( + "💎TON Gateway deployed %s (%s) with TSS address %s", + gwAccount.ID.ToRaw(), + gwAccount.ID.ToHuman(false, true), + r.TSSAddress.Hex(), + ) // 3. Check that the gateway indeed was deployed and has desired TON balance. gwBalance, err := deployer.GetBalanceOf(ctx, gwAccount.ID) @@ -55,7 +66,58 @@ func (r *E2ERunner) SetupTON() error { } r.TONDeployer = deployer - r.TONGateway = gwAccount.ID + r.TONGateway = toncontracts.NewGateway(gwAccount.ID) - return nil + return r.ensureTONChainParams(gwAccount) +} + +func (r *E2ERunner) ensureTONChainParams(gw *ton.AccountInit) error { + if r.ZetaTxServer == nil { + return errors.New("ZetaTxServer is not initialized") + } + + creator := r.ZetaTxServer.MustGetAccountAddressFromName(utils.OperationalPolicyName) + + chainID := chains.TONLocalnet.ChainId + + chainParams := &observertypes.ChainParams{ + ChainId: chainID, + ConfirmationCount: 1, + GasPriceTicker: 5, + InboundTicker: 5, + OutboundTicker: 5, + ZetaTokenContractAddress: constant.EVMZeroAddress, + ConnectorContractAddress: constant.EVMZeroAddress, + Erc20CustodyContractAddress: constant.EVMZeroAddress, + OutboundScheduleInterval: 2, + OutboundScheduleLookahead: 5, + BallotThreshold: observertypes.DefaultBallotThreshold, + MinObserverDelegation: observertypes.DefaultMinObserverDelegation, + IsSupported: true, + GatewayAddress: gw.ID.ToRaw(), + } + + msg := observertypes.NewMsgUpdateChainParams(creator, chainParams) + + if _, err := r.ZetaTxServer.BroadcastTx(utils.OperationalPolicyName, msg); err != nil { + return errors.Wrap(err, "unable to broadcast TON chain params tx") + } + + r.Logger.Print("💎Voted for adding TON chain params (localnet). Waiting for confirmation") + + query := &observertypes.QueryGetChainParamsForChainRequest{ChainId: chainID} + + const duration = 2 * time.Second + + for i := 0; i < 10; i++ { + _, err := r.ObserverClient.GetChainParamsForChain(r.Ctx, query) + if err == nil { + r.Logger.Print("💎TON chain params are set") + return nil + } + + time.Sleep(duration) + } + + return errors.New("unable to set TON chain params") } diff --git a/e2e/runner/setup_zeta.go b/e2e/runner/setup_zeta.go index ffab5db811..1d1db47108 100644 --- a/e2e/runner/setup_zeta.go +++ b/e2e/runner/setup_zeta.go @@ -179,6 +179,7 @@ func (r *E2ERunner) SetZEVMZRC20s() { e2eutils.OperationalPolicyName, e2eutils.AdminPolicyName, r.ERC20Addr.Hex(), + r.skipChainOperations, ) require.NoError(r, err) @@ -191,6 +192,7 @@ func (r *E2ERunner) SetZEVMZRC20s() { r.SetupETHZRC20() r.SetupBTCZRC20() r.SetupSOLZRC20() + r.SetupTONZRC20() } // SetupETHZRC20 sets up the ETH ZRC20 in the runner from the values queried from the chain @@ -242,6 +244,27 @@ func (r *E2ERunner) SetupSOLZRC20() { r.SOLZRC20 = SOLZRC20 } +// SetupTONZRC20 sets up the TON ZRC20 in the runner from the values queried from the chain +func (r *E2ERunner) SetupTONZRC20() { + chainID := chains.TONLocalnet.ChainId + + // noop + if r.skipChainOperations(chainID) { + return + } + + TONZRC20Addr, err := r.SystemContract.GasCoinZRC20ByChainId(&bind.CallOpts{}, big.NewInt(chainID)) + require.NoError(r, err) + + r.TONZRC20Addr = TONZRC20Addr + r.Logger.Info("TON ZRC20 address: %s", TONZRC20Addr.Hex()) + + TONZRC20, err := zrc20.NewZRC20(TONZRC20Addr, r.ZEVMClient) + require.NoError(r, err) + + r.TONZRC20 = TONZRC20 +} + // EnableHeaderVerification enables the header verification for the given chain IDs func (r *E2ERunner) EnableHeaderVerification(chainIDList []int64) error { r.Logger.Print("⚙️ enabling verification flags for block headers") diff --git a/e2e/runner/solana.go b/e2e/runner/solana.go index 8ff67b9d72..4d5e6a9b9d 100644 --- a/e2e/runner/solana.go +++ b/e2e/runner/solana.go @@ -153,7 +153,7 @@ func (r *E2ERunner) WithdrawSOLZRC20( tx, err := r.SOLZRC20.Approve(r.ZEVMAuth, r.SOLZRC20Addr, approveAmount) require.NoError(r, err) receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) - utils.RequireTxSuccessful(r, receipt) + utils.RequireTxSuccessful(r, receipt, "approve") // withdraw tx, err = r.SOLZRC20.Withdraw(r.ZEVMAuth, []byte(to.String()), amount) @@ -162,7 +162,7 @@ func (r *E2ERunner) WithdrawSOLZRC20( // wait for tx receipt receipt = utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) - utils.RequireTxSuccessful(r, receipt) + utils.RequireTxSuccessful(r, receipt, "withdraw") r.Logger.Info("Receipt txhash %s status %d", receipt.TxHash, receipt.Status) // wait for the cctx to be mined diff --git a/e2e/runner/ton.go b/e2e/runner/ton.go new file mode 100644 index 0000000000..8746e25977 --- /dev/null +++ b/e2e/runner/ton.go @@ -0,0 +1,59 @@ +package runner + +import ( + "cosmossdk.io/math" + eth "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + "github.com/tonkeeper/tongo/wallet" + + toncontracts "github.com/zeta-chain/node/pkg/contracts/ton" +) + +// we need to use this send mode due to how wallet V5 works +// +// https://github.com/tonkeeper/w5/blob/main/contracts/wallet_v5.fc#L82 +// https://docs.ton.org/develop/smart-contracts/guidelines/message-modes-cookbook +const tonDepositSendCode = toncontracts.SendFlagSeparateFees + toncontracts.SendFlagIgnoreErrors + +// TONDeposit deposit TON to Gateway contract +func (r *E2ERunner) TONDeposit(sender *wallet.Wallet, amount math.Uint, zevmRecipient eth.Address) error { + require.NotNil(r, r.TONGateway, "TON Gateway is not initialized") + + require.NotNil(r, sender, "Sender wallet is nil") + require.False(r, amount.IsZero()) + require.NotEqual(r, (eth.Address{}).String(), zevmRecipient.String()) + + r.Logger.Info( + "Sending deposit of %s TON from %s to zEVM %s", + amount.String(), + sender.GetAddress().ToRaw(), + zevmRecipient.Hex(), + ) + + return r.TONGateway.SendDeposit(r.Ctx, sender, amount, zevmRecipient, tonDepositSendCode) +} + +// TONDepositAndCall deposit TON to Gateway contract with call data. +func (r *E2ERunner) TONDepositAndCall( + sender *wallet.Wallet, + amount math.Uint, + zevmRecipient eth.Address, + callData []byte, +) error { + require.NotNil(r, r.TONGateway, "TON Gateway is not initialized") + + require.NotNil(r, sender, "Sender wallet is nil") + require.False(r, amount.IsZero()) + require.NotEqual(r, (eth.Address{}).String(), zevmRecipient.String()) + require.NotEmpty(r, callData) + + r.Logger.Info( + "Sending deposit of %s TON from %s to zEVM %s and calling contract with %q", + amount.String(), + sender.GetAddress().ToRaw(), + zevmRecipient.Hex(), + string(callData), + ) + + return r.TONGateway.SendDepositAndCall(r.Ctx, sender, amount, zevmRecipient, callData, tonDepositSendCode) +} diff --git a/e2e/runner/ton/accounts.go b/e2e/runner/ton/accounts.go index f1a3fea659..3ffd8c0907 100644 --- a/e2e/runner/ton/accounts.go +++ b/e2e/runner/ton/accounts.go @@ -12,6 +12,8 @@ import ( "github.com/tonkeeper/tongo/ton" "github.com/tonkeeper/tongo/wallet" "golang.org/x/crypto/ed25519" + + toncontracts "github.com/zeta-chain/node/pkg/contracts/ton" ) const workchainID = 0 @@ -138,7 +140,7 @@ func buildGatewayData(tss eth.Address) (*boc.Cell, error) { cell = boc.NewCell() ) - err := errCollect( + err := toncontracts.ErrCollect( cell.WriteBit(true), // deposits_enabled zeroCoins.MarshalTLB(cell, enc), // total_locked zeroCoins.MarshalTLB(cell, enc), // fees @@ -153,16 +155,6 @@ func buildGatewayData(tss eth.Address) (*boc.Cell, error) { return cell, nil } -func errCollect(errs ...error) error { - for i, err := range errs { - if err != nil { - return errors.Wrapf(err, "error at index %d", i) - } - } - - return nil -} - // copied from tongo wallets_common.go func generateStateInit(code, data *boc.Cell) *tlb.StateInit { return &tlb.StateInit{ diff --git a/e2e/runner/v2_setup_evm.go b/e2e/runner/v2_setup_evm.go index 37f6423f29..5b8e686d9b 100644 --- a/e2e/runner/v2_setup_evm.go +++ b/e2e/runner/v2_setup_evm.go @@ -48,8 +48,8 @@ func (r *E2ERunner) SetupEVMV2() { initializerData, err := gatewayEVMABI.Pack("initialize", r.TSSAddress, r.ZetaEthAddr, r.Account.EVMAddress()) require.NoError(r, err) - // Deploy the proxy contract - proxyAddress, txProxy, _, err := erc1967proxy.DeployERC1967Proxy( + // Deploy gateway proxy contract + gatewayProxyAddress, gatewayProxyTx, _, err := erc1967proxy.DeployERC1967Proxy( r.EVMAuth, r.EVMClient, gatewayEVMAddr, @@ -57,33 +57,47 @@ func (r *E2ERunner) SetupEVMV2() { ) require.NoError(r, err) - r.GatewayEVMAddr = proxyAddress - r.GatewayEVM, err = gatewayevm.NewGatewayEVM(proxyAddress, r.EVMClient) + r.GatewayEVMAddr = gatewayProxyAddress + r.GatewayEVM, err = gatewayevm.NewGatewayEVM(gatewayProxyAddress, r.EVMClient) require.NoError(r, err) r.Logger.Info("Gateway EVM contract address: %s, tx hash: %s", gatewayEVMAddr.Hex(), txGateway.Hash().Hex()) + // Deploy erc20custody proxy contract r.Logger.Info("Deploying ERC20Custody contract") - erc20CustodyNewAddr, txCustody, erc20CustodyNew, err := erc20custodyv2.DeployERC20Custody( + erc20CustodyAddr, txCustody, _, err := erc20custodyv2.DeployERC20Custody(r.EVMAuth, r.EVMClient) + require.NoError(r, err) + + ensureTxReceipt(txCustody, "ERC20Custody deployment failed") + + erc20CustodyABI, err := erc20custodyv2.ERC20CustodyMetaData.GetAbi() + require.NoError(r, err) + + // Encode the initializer data + initializerData, err = erc20CustodyABI.Pack("initialize", r.GatewayEVMAddr, r.TSSAddress, r.Account.EVMAddress()) + require.NoError(r, err) + + // Deploy erc20custody proxy contract + erc20CustodyProxyAddress, erc20ProxyTx, _, err := erc1967proxy.DeployERC1967Proxy( r.EVMAuth, r.EVMClient, - r.GatewayEVMAddr, - r.TSSAddress, - r.Account.EVMAddress(), + erc20CustodyAddr, + initializerData, ) require.NoError(r, err) - r.ERC20CustodyV2Addr = erc20CustodyNewAddr - r.ERC20CustodyV2 = erc20CustodyNew + r.ERC20CustodyV2Addr = erc20CustodyProxyAddress + r.ERC20CustodyV2, err = erc20custodyv2.NewERC20Custody(erc20CustodyProxyAddress, r.EVMClient) + require.NoError(r, err) r.Logger.Info( "ERC20CustodyV2 contract address: %s, tx hash: %s", - erc20CustodyNewAddr.Hex(), + erc20CustodyAddr.Hex(), txCustody.Hash().Hex(), ) ensureTxReceipt(txCustody, "ERC20CustodyV2 deployment failed") // set custody contract in gateway - txSetCustody, err := r.GatewayEVM.SetCustody(r.EVMAuth, erc20CustodyNewAddr) + txSetCustody, err := r.GatewayEVM.SetCustody(r.EVMAuth, erc20CustodyProxyAddress) require.NoError(r, err) // deploy test dapp v2 @@ -96,7 +110,8 @@ func (r *E2ERunner) SetupEVMV2() { // check contract deployment receipt ensureTxReceipt(txDonation, "EVM donation tx failed") - ensureTxReceipt(txProxy, "Gateway proxy deployment failed") + ensureTxReceipt(gatewayProxyTx, "Gateway proxy deployment failed") + ensureTxReceipt(erc20ProxyTx, "ERC20Custody proxy deployment failed") ensureTxReceipt(txSetCustody, "Set custody in Gateway failed") ensureTxReceipt(txTestDAppV2, "TestDAppV2 deployment failed") diff --git a/e2e/runner/v2_upgrade.go b/e2e/runner/v2_upgrade.go new file mode 100644 index 0000000000..eb5af7f860 --- /dev/null +++ b/e2e/runner/v2_upgrade.go @@ -0,0 +1,76 @@ +package runner + +import ( + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/erc20custody.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" + + "github.com/zeta-chain/node/e2e/utils" +) + +// UpgradeGatewaysAndERC20Custody upgrades gateways and ERC20Custody contracts +// It deploys new contract implementation with the current imported artifacts and upgrades the contract +func (r *E2ERunner) UpgradeGatewaysAndERC20Custody() { + r.UpgradeGatewayZEVM() + r.UpgradeGatewayEVM() + r.UpgradeERC20Custody() +} + +// UpgradeGatewayZEVM upgrades the GatewayZEVM contract +func (r *E2ERunner) UpgradeGatewayZEVM() { + ensureTxReceipt := func(tx *ethtypes.Transaction, failMessage string) { + receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) + r.requireTxSuccessful(receipt, failMessage+" tx hash: "+tx.Hash().Hex()) + } + + r.Logger.Info("Upgrading Gateway ZEVM contract") + // Deploy the new gateway contract implementation + newImplementationAddress, txDeploy, _, err := gatewayzevm.DeployGatewayZEVM(r.ZEVMAuth, r.ZEVMClient) + require.NoError(r, err) + ensureTxReceipt(txDeploy, "New GatewayZEVM implementation deployment failed") + + // Upgrade + txUpgrade, err := r.GatewayZEVM.UpgradeToAndCall(r.ZEVMAuth, newImplementationAddress, []byte{}) + require.NoError(r, err) + ensureTxReceipt(txUpgrade, "GatewayZEVM upgrade failed") +} + +// UpgradeGatewayEVM upgrades the GatewayEVM contract +func (r *E2ERunner) UpgradeGatewayEVM() { + ensureTxReceipt := func(tx *ethtypes.Transaction, failMessage string) { + receipt := utils.MustWaitForTxReceipt(r.Ctx, r.EVMClient, tx, r.Logger, r.ReceiptTimeout) + r.requireTxSuccessful(receipt, failMessage+" tx hash: "+tx.Hash().Hex()) + } + + r.Logger.Info("Upgrading Gateway EVM contract") + // Deploy the new gateway contract implementation + newImplementationAddress, txDeploy, _, err := gatewayevm.DeployGatewayEVM(r.EVMAuth, r.EVMClient) + require.NoError(r, err) + ensureTxReceipt(txDeploy, "New GatewayEVM implementation deployment failed") + + // Upgrade + txUpgrade, err := r.GatewayEVM.UpgradeToAndCall(r.EVMAuth, newImplementationAddress, []byte{}) + require.NoError(r, err) + ensureTxReceipt(txUpgrade, "GatewayEVM upgrade failed") +} + +// UpgradeERC20CustodyZEVM upgrades the ERC20Custody contract +func (r *E2ERunner) UpgradeERC20Custody() { + ensureTxReceipt := func(tx *ethtypes.Transaction, failMessage string) { + receipt := utils.MustWaitForTxReceipt(r.Ctx, r.EVMClient, tx, r.Logger, r.ReceiptTimeout) + r.requireTxSuccessful(receipt, failMessage+" tx hash: "+tx.Hash().Hex()) + } + + r.Logger.Info("Upgrading ERC20Custody contract") + // Deploy the new erc20Custody contract implementation + newImplementationAddress, txDeploy, _, err := erc20custody.DeployERC20Custody(r.EVMAuth, r.EVMClient) + require.NoError(r, err) + ensureTxReceipt(txDeploy, "New ERC20Custody implementation deployment failed") + + // Upgrade + txUpgrade, err := r.ERC20CustodyV2.UpgradeToAndCall(r.EVMAuth, newImplementationAddress, []byte{}) + require.NoError(r, err) + ensureTxReceipt(txUpgrade, "ERC20Custody upgrade failed") +} diff --git a/e2e/runner/v2_zevm.go b/e2e/runner/v2_zevm.go index 4824884bd0..45dd8c6761 100644 --- a/e2e/runner/v2_zevm.go +++ b/e2e/runner/v2_zevm.go @@ -31,20 +31,20 @@ func (r *E2ERunner) V2ETHWithdraw( return tx } -// V2ETHWithdrawAndCall calls WithdrawAndCall of Gateway with gas token on ZEVM -func (r *E2ERunner) V2ETHWithdrawAndCall( +// V2ETHWithdrawAndCall calls WithdrawAndCall of Gateway with gas token on ZEVM using arbitrary call +func (r *E2ERunner) V2ETHWithdrawAndArbitraryCall( receiver ethcommon.Address, amount *big.Int, payload []byte, revertOptions gatewayzevm.RevertOptions, ) *ethtypes.Transaction { - tx, err := r.GatewayZEVM.WithdrawAndCall( + tx, err := r.GatewayZEVM.WithdrawAndCall0( r.ZEVMAuth, receiver.Bytes(), amount, r.ETHZRC20Addr, payload, - gasLimit, + gatewayzevm.CallOptions{GasLimit: gasLimit, IsArbitraryCall: true}, revertOptions, ) require.NoError(r, err) @@ -53,13 +53,13 @@ func (r *E2ERunner) V2ETHWithdrawAndCall( } // V2ETHWithdrawAndCall calls WithdrawAndCall of Gateway with gas token on ZEVM using authenticated call -func (r *E2ERunner) V2ETHWithdrawAndAuthenticatedCall( +func (r *E2ERunner) V2ETHWithdrawAndCall( receiver ethcommon.Address, amount *big.Int, payload []byte, revertOptions gatewayzevm.RevertOptions, ) *ethtypes.Transaction { - tx, err := r.GatewayZEVM.WithdrawAndCall2( + tx, err := r.GatewayZEVM.WithdrawAndCall0( r.ZEVMAuth, receiver.Bytes(), amount, @@ -76,9 +76,9 @@ func (r *E2ERunner) V2ETHWithdrawAndAuthenticatedCall( return tx } -// V2ETHWithdrawAndCall calls WithdrawAndCall of Gateway with gas token on ZEVM using authenticated call +// V2ETHWithdrawAndCallThroughContract calls WithdrawAndCall of Gateway with gas token on ZEVM using authenticated call // through contract -func (r *E2ERunner) V2ETHWithdrawAndAuthenticatedCallThroughContract( +func (r *E2ERunner) V2ETHWithdrawAndCallThroughContract( gatewayZEVMCaller *gatewayzevmcaller.GatewayZEVMCaller, receiver ethcommon.Address, amount *big.Int, @@ -120,7 +120,36 @@ func (r *E2ERunner) V2ERC20Withdraw( return tx } -// V2ERC20WithdrawAndCall calls WithdrawAndCall of Gateway with erc20 token on ZEVM +// V2ERC20WithdrawAndArbitraryCall calls WithdrawAndCall of Gateway with erc20 token on ZEVM using arbitrary call +func (r *E2ERunner) V2ERC20WithdrawAndArbitraryCall( + receiver ethcommon.Address, + amount *big.Int, + payload []byte, + revertOptions gatewayzevm.RevertOptions, +) *ethtypes.Transaction { + // this function take more gas than default 500k + // so we need to increase the gas limit + previousGasLimit := r.ZEVMAuth.GasLimit + r.ZEVMAuth.GasLimit = 10000000 + defer func() { + r.ZEVMAuth.GasLimit = previousGasLimit + }() + + tx, err := r.GatewayZEVM.WithdrawAndCall0( + r.ZEVMAuth, + receiver.Bytes(), + amount, + r.ERC20ZRC20Addr, + payload, + gatewayzevm.CallOptions{GasLimit: gasLimit, IsArbitraryCall: true}, + revertOptions, + ) + require.NoError(r, err) + + return tx +} + +// V2ERC20WithdrawAndCall calls WithdrawAndCall of Gateway with erc20 token on ZEVM using authenticated call func (r *E2ERunner) V2ERC20WithdrawAndCall( receiver ethcommon.Address, amount *big.Int, @@ -135,13 +164,13 @@ func (r *E2ERunner) V2ERC20WithdrawAndCall( r.ZEVMAuth.GasLimit = previousGasLimit }() - tx, err := r.GatewayZEVM.WithdrawAndCall( + tx, err := r.GatewayZEVM.WithdrawAndCall0( r.ZEVMAuth, receiver.Bytes(), amount, r.ERC20ZRC20Addr, payload, - gasLimit, + gatewayzevm.CallOptions{GasLimit: gasLimit, IsArbitraryCall: false}, revertOptions, ) require.NoError(r, err) @@ -149,18 +178,18 @@ func (r *E2ERunner) V2ERC20WithdrawAndCall( return tx } -// V2ZEVMToEMVCall calls Call of Gateway on ZEVM -func (r *E2ERunner) V2ZEVMToEMVCall( +// V2ZEVMToEMVArbitraryCall calls Call of Gateway on ZEVM using arbitrary call +func (r *E2ERunner) V2ZEVMToEMVArbitraryCall( receiver ethcommon.Address, payload []byte, revertOptions gatewayzevm.RevertOptions, ) *ethtypes.Transaction { - tx, err := r.GatewayZEVM.Call0( + tx, err := r.GatewayZEVM.Call( r.ZEVMAuth, receiver.Bytes(), r.ETHZRC20Addr, payload, - gasLimit, + gatewayzevm.CallOptions{GasLimit: gasLimit, IsArbitraryCall: true}, revertOptions, ) require.NoError(r, err) @@ -168,8 +197,8 @@ func (r *E2ERunner) V2ZEVMToEMVCall( return tx } -// V2ZEVMToEMVCall calls authenticated Call of Gateway on ZEVM -func (r *E2ERunner) V2ZEVMToEMVAuthenticatedCall( +// V2ZEVMToEMVCall calls authenticated Call of Gateway on ZEVM using authenticated call +func (r *E2ERunner) V2ZEVMToEMVCall( receiver ethcommon.Address, payload []byte, revertOptions gatewayzevm.RevertOptions, @@ -190,8 +219,8 @@ func (r *E2ERunner) V2ZEVMToEMVAuthenticatedCall( return tx } -// V2ZEVMToEMVCall calls authenticated Call of Gateway on ZEVM through contract -func (r *E2ERunner) V2ZEVMToEMVAuthenticatedCallThroughContract( +// V2ZEVMToEMVCallThroughContract calls authenticated Call of Gateway on ZEVM through contract using authenticated call +func (r *E2ERunner) V2ZEVMToEMVCallThroughContract( gatewayZEVMCaller *gatewayzevmcaller.GatewayZEVMCaller, receiver ethcommon.Address, payload []byte, diff --git a/e2e/runner/zeta.go b/e2e/runner/zeta.go index cec59219cd..1df7e676e3 100644 --- a/e2e/runner/zeta.go +++ b/e2e/runner/zeta.go @@ -6,6 +6,7 @@ import ( "time" "github.com/cenkalti/backoff/v4" + query "github.com/cosmos/cosmos-sdk/types/query" ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/require" @@ -13,10 +14,14 @@ import ( connectorzevm "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/zetaconnectorzevm.sol" "github.com/zeta-chain/node/e2e/utils" + "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/retry" "github.com/zeta-chain/node/x/crosschain/types" + observertypes "github.com/zeta-chain/node/x/observer/types" ) +// WaitForBlocks waits for a specific number of blocks to be generated +// The parameter n is the number of blocks to wait for func (r *E2ERunner) WaitForBlocks(n int64) { height, err := r.CctxClient.LastZetaHeight(r.Ctx, &types.QueryLastZetaHeightRequest{}) if err != nil { @@ -31,6 +36,32 @@ func (r *E2ERunner) WaitForBlocks(n int64) { err = retry.DoWithBackoff(call, boWithMaxRetries) require.NoError(r, err, "failed to wait for %d blocks", n) } + +// WaitForTSSGeneration waits for a specific number of TSS to be generated +// The parameter n is the number of TSS to wait for +func (r *E2ERunner) WaitForTSSGeneration(tssNumber int64) { + call := func() error { + return retry.Retry(r.checkNumberOfTSSGenerated(tssNumber)) + } + bo := backoff.NewConstantBackOff(time.Second * 5) + boWithMaxRetries := backoff.WithMaxRetries(bo, 10) + err := retry.DoWithBackoff(call, boWithMaxRetries) + require.NoError(r, err, "failed to wait for %d tss generation", tssNumber) +} + +// checkNumberOfTSSGenerated checks the number of TSS generated +// if the number of tss is less that the `tssNumber` provided we return an error +func (r *E2ERunner) checkNumberOfTSSGenerated(tssNumber int64) error { + tssList, err := r.ObserverClient.TssHistory(r.Ctx, &observertypes.QueryTssHistoryRequest{}) + if err != nil { + return err + } + if int64(len(tssList.TssList)) < tssNumber { + return fmt.Errorf("waiting for %d tss generation, number of TSS :%d", tssNumber, len(tssList.TssList)) + } + return nil +} + func (r *E2ERunner) waitForBlock(n int64) error { height, err := r.CctxClient.LastZetaHeight(r.Ctx, &types.QueryLastZetaHeightRequest{}) if err != nil { @@ -67,12 +98,47 @@ func (r *E2ERunner) WaitForMinedCCTX(txHash ethcommon.Hash) { } // WaitForMinedCCTXFromIndex waits for a cctx to be mined from its index -func (r *E2ERunner) WaitForMinedCCTXFromIndex(index string) { +func (r *E2ERunner) WaitForMinedCCTXFromIndex(index string) *types.CrossChainTx { r.Lock() defer r.Unlock() cctx := utils.WaitCCTXMinedByIndex(r.Ctx, index, r.CctxClient, r.Logger, r.CctxTimeout) utils.RequireCCTXStatus(r, cctx, types.CctxStatus_OutboundMined) + + return cctx +} + +// WaitForSpecificCCTX scans for cctx by filters and ensures it's mined +func (r *E2ERunner) WaitForSpecificCCTX( + filter func(*types.CrossChainTx) bool, + timeout time.Duration, +) *types.CrossChainTx { + var ( + ctx = r.Ctx + start = time.Now() + reqQuery = &types.QueryAllCctxRequest{ + Pagination: &query.PageRequest{Reverse: true}, + } + ) + + for time.Since(start) < timeout { + res, err := r.CctxClient.CctxAll(ctx, reqQuery) + require.NoError(r, err) + + for i := range res.CrossChainTx { + tx := res.CrossChainTx[i] + if filter(tx) { + return r.WaitForMinedCCTXFromIndex(tx.Index) + } + } + + time.Sleep(time.Second) + } + + r.Logger.Error("WaitForSpecificCCTX: No CCTX found. Timed out") + r.FailNow() + + return nil } // SendZetaOnEvm sends ZETA to an address on EVM @@ -250,3 +316,14 @@ func (r *E2ERunner) WithdrawERC20(amount *big.Int) *ethtypes.Transaction { return tx } + +// skipChainOperations checks if the chain operations should be skipped for E2E +func (r *E2ERunner) skipChainOperations(chainID int64) bool { + skip := r.IsRunningUpgrade() && chains.IsTONChain(chainID, nil) + + if skip { + r.Logger.Print("Skipping chain operations for chain %d", chainID) + } + + return skip +} diff --git a/e2e/txserver/zeta_tx_server.go b/e2e/txserver/zeta_tx_server.go index 9ba7e5315a..a79c5af098 100644 --- a/e2e/txserver/zeta_tx_server.go +++ b/e2e/txserver/zeta_tx_server.go @@ -408,6 +408,7 @@ func (zts ZetaTxServer) DeploySystemContracts( // returns the addresses of erc20 zrc20 func (zts ZetaTxServer) DeployZRC20s( accountOperational, accountAdmin, erc20Addr string, + skipChain func(chainID int64) bool, ) (string, error) { // retrieve account accOperational, err := zts.clientCtx.Keyring.Key(accountOperational) @@ -441,8 +442,31 @@ func (zts ZetaTxServer) DeployZRC20s( deployerAddr = addrOperational.String() } + deploy := func(msg *fungibletypes.MsgDeployFungibleCoinZRC20) (string, error) { + // noop + if skipChain(msg.ForeignChainId) { + return "", nil + } + + res, err := zts.BroadcastTx(deployerAccount, msg) + if err != nil { + return "", fmt.Errorf("failed to deploy eth zrc20: %w", err) + } + + addr, err := fetchZRC20FromDeployResponse(res) + if err != nil { + return "", fmt.Errorf("unable to fetch zrc20 from deploy response: %w", err) + } + + if err := zts.InitializeLiquidityCap(addr); err != nil { + return "", fmt.Errorf("unable to initialize liquidity cap: %w", err) + } + + return addr, nil + } + // deploy eth zrc20 - res, err := zts.BroadcastTx(deployerAccount, fungibletypes.NewMsgDeployFungibleCoinZRC20( + _, err = deploy(fungibletypes.NewMsgDeployFungibleCoinZRC20( deployerAddr, "", chains.GoerliLocalnet.ChainId, @@ -455,16 +479,9 @@ func (zts ZetaTxServer) DeployZRC20s( if err != nil { return "", fmt.Errorf("failed to deploy eth zrc20: %s", err.Error()) } - zrc20, err := fetchZRC20FromDeployResponse(res) - if err != nil { - return "", err - } - if err := zts.InitializeLiquidityCap(zrc20); err != nil { - return "", err - } // deploy btc zrc20 - res, err = zts.BroadcastTx(deployerAccount, fungibletypes.NewMsgDeployFungibleCoinZRC20( + _, err = deploy(fungibletypes.NewMsgDeployFungibleCoinZRC20( deployerAddr, "", chains.BitcoinRegtest.ChainId, @@ -477,16 +494,9 @@ func (zts ZetaTxServer) DeployZRC20s( if err != nil { return "", fmt.Errorf("failed to deploy btc zrc20: %s", err.Error()) } - zrc20, err = fetchZRC20FromDeployResponse(res) - if err != nil { - return "", err - } - if err := zts.InitializeLiquidityCap(zrc20); err != nil { - return "", err - } // deploy sol zrc20 - res, err = zts.BroadcastTx(deployerAccount, fungibletypes.NewMsgDeployFungibleCoinZRC20( + _, err = deploy(fungibletypes.NewMsgDeployFungibleCoinZRC20( deployerAddr, "", chains.SolanaLocalnet.ChainId, @@ -499,16 +509,24 @@ func (zts ZetaTxServer) DeployZRC20s( if err != nil { return "", fmt.Errorf("failed to deploy sol zrc20: %s", err.Error()) } - zrc20, err = fetchZRC20FromDeployResponse(res) + + // deploy ton zrc20 + _, err = deploy(fungibletypes.NewMsgDeployFungibleCoinZRC20( + deployerAddr, + "", + chains.TONLocalnet.ChainId, + 9, + "TON", + "TON", + coin.CoinType_Gas, + 100_000, + )) if err != nil { - return "", err - } - if err := zts.InitializeLiquidityCap(zrc20); err != nil { - return "", err + return "", fmt.Errorf("failed to deploy ton zrc20: %w", err) } // deploy erc20 zrc20 - res, err = zts.BroadcastTx(deployerAccount, fungibletypes.NewMsgDeployFungibleCoinZRC20( + erc20zrc20Addr, err := deploy(fungibletypes.NewMsgDeployFungibleCoinZRC20( deployerAddr, erc20Addr, chains.GoerliLocalnet.ChainId, @@ -522,15 +540,6 @@ func (zts ZetaTxServer) DeployZRC20s( return "", fmt.Errorf("failed to deploy erc20 zrc20: %s", err.Error()) } - // fetch the erc20 zrc20 contract address and remove the quotes - erc20zrc20Addr, err := fetchZRC20FromDeployResponse(res) - if err != nil { - return "", err - } - if err := zts.InitializeLiquidityCap(erc20zrc20Addr); err != nil { - return "", err - } - return erc20zrc20Addr, nil } diff --git a/e2e/utils/require.go b/e2e/utils/require.go index e610219e15..3dedb7dd26 100644 --- a/e2e/utils/require.go +++ b/e2e/utils/require.go @@ -25,15 +25,27 @@ func RequireCCTXStatus( // RequireTxSuccessful checks if the receipt status is successful. // Currently, it accepts eth receipt, but we can make this more generic by using type assertion. func RequireTxSuccessful(t require.TestingT, receipt *ethtypes.Receipt, msgAndArgs ...any) { - msg := "receipt status is not successful" - require.Equal(t, ethtypes.ReceiptStatusSuccessful, receipt.Status, msg+errSuffix(msgAndArgs...)) + msg := "receipt status is not successful: %s" + require.Equal( + t, + ethtypes.ReceiptStatusSuccessful, + receipt.Status, + msg+errSuffix(msgAndArgs...), + receipt.TxHash.String(), + ) } // RequiredTxFailed checks if the receipt status is failed. // Currently, it accepts eth receipt, but we can make this more generic by using type assertion. func RequiredTxFailed(t require.TestingT, receipt *ethtypes.Receipt, msgAndArgs ...any) { - msg := "receipt status is not successful" - require.Equal(t, ethtypes.ReceiptStatusFailed, receipt.Status, msg+errSuffix(msgAndArgs...)) + msg := "receipt status is not successful: %s" + require.Equal( + t, + ethtypes.ReceiptStatusFailed, + receipt.Status, + msg+errSuffix(msgAndArgs...), + receipt.TxHash.String(), + ) } func errSuffix(msgAndArgs ...any) string { diff --git a/go.mod b/go.mod index 7625fc2047..524a63d8be 100644 --- a/go.mod +++ b/go.mod @@ -16,20 +16,20 @@ require ( github.com/cenkalti/backoff/v4 v4.3.0 github.com/cockroachdb/errors v1.11.1 github.com/coinbase/rosetta-sdk-go v0.7.9 - github.com/cometbft/cometbft v0.37.4 + github.com/cometbft/cometbft v0.37.5 github.com/cometbft/cometbft-db v0.12.0 github.com/cosmos/btcutil v1.0.5 - github.com/cosmos/cosmos-sdk v0.47.10 - github.com/cosmos/gogoproto v1.4.10 + github.com/cosmos/cosmos-sdk v0.47.14 + github.com/cosmos/gogoproto v1.7.0 github.com/cosmos/ibc-go/v7 v7.4.0 - github.com/davecgh/go-spew v1.1.1 + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/emicklei/proto v1.11.1 github.com/ethereum/go-ethereum v1.10.26 - github.com/fatih/color v1.13.0 + github.com/fatih/color v1.14.1 github.com/frumioj/crypto11 v1.2.5-0.20210823151709-946ce662cc0e github.com/gagliardetto/solana-go v1.10.0 github.com/golang/mock v1.6.0 - github.com/golang/protobuf v1.5.3 + github.com/golang/protobuf v1.5.4 github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.5.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0 @@ -49,26 +49,26 @@ require ( github.com/prometheus/client_golang v1.14.0 github.com/rakyll/statik v0.1.7 github.com/rs/cors v1.8.3 - github.com/rs/zerolog v1.32.0 + github.com/rs/zerolog v1.33.0 github.com/samber/lo v1.46.0 github.com/spf13/afero v1.11.0 - github.com/spf13/cast v1.5.1 + github.com/spf13/cast v1.6.0 github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.16.0 + github.com/spf13/viper v1.18.2 github.com/stretchr/testify v1.9.0 - github.com/zeta-chain/ethermint v0.0.0-20240927155358-f34e2a4a86f1 + github.com/zeta-chain/ethermint v0.0.0-20241010181243-044e22bdb7e7 github.com/zeta-chain/keystone/keys v0.0.0-20240826165841-3874f358c138 - github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20240924201108-3a274ce7bad0 + github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20241021075719-d40d2e28467c gitlab.com/thorchain/tss/go-tss v1.6.5 go.nhat.io/grpcmock v0.25.0 golang.org/x/crypto v0.23.0 - golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 golang.org/x/net v0.25.0 golang.org/x/sync v0.7.0 - google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 - google.golang.org/grpc v1.60.1 - google.golang.org/protobuf v1.32.0 + google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 + google.golang.org/grpc v1.62.1 + google.golang.org/protobuf v1.33.0 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 @@ -77,15 +77,15 @@ require ( ) require ( - cloud.google.com/go v0.111.0 // indirect + cloud.google.com/go v0.112.0 // indirect cloud.google.com/go/compute v1.23.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v1.1.5 // indirect - cloud.google.com/go/storage v1.35.1 // indirect + cloud.google.com/go/storage v1.36.0 // indirect cosmossdk.io/api v0.3.1 // indirect cosmossdk.io/core v0.5.1 // indirect cosmossdk.io/depinject v1.0.0-alpha.4 // indirect - cosmossdk.io/log v1.3.1 // indirect + cosmossdk.io/log v1.4.1 // indirect filippo.io/edwards25519 v1.0.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/ChainSafe/go-schnorrkel v1.0.0 // indirect @@ -116,7 +116,7 @@ require ( github.com/confio/ics23/go v0.9.0 // indirect github.com/containerd/cgroups v1.1.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect - github.com/cosmos/cosmos-proto v1.0.0-beta.4 // indirect + github.com/cosmos/cosmos-proto v1.0.0-beta.5 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect github.com/cosmos/iavl v0.20.1 // indirect @@ -140,9 +140,9 @@ require ( github.com/dvsekhvalnov/jose2go v1.6.0 // indirect github.com/edsrzf/mmap-go v1.0.0 // indirect github.com/elastic/gosigar v0.14.2 // indirect - github.com/felixge/httpsnoop v1.0.2 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/flynn/noise v1.0.0 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/gagliardetto/binary v0.8.0 // indirect github.com/gagliardetto/treeout v0.1.4 // indirect github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect @@ -150,7 +150,7 @@ require ( github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect - github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/logr v1.3.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect @@ -159,7 +159,7 @@ require ( github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gogo/googleapis v1.4.1 // indirect github.com/gogo/protobuf v1.3.3 // indirect - github.com/golang/glog v1.1.2 // indirect + github.com/golang/glog v1.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.2 // indirect @@ -202,7 +202,7 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.16.7 // indirect + github.com/klauspost/compress v1.17.0 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/koron/go-ssdp v0.0.4 // indirect github.com/kr/pretty v0.3.1 // indirect @@ -258,9 +258,9 @@ require ( github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/otiai10/primes v0.0.0-20180210170552-f6d2a1ba97c4 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect - github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/polydawn/refmt v0.89.0 // indirect github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.42.0 // indirect @@ -275,11 +275,10 @@ require ( github.com/sergi/go-diff v1.3.1 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/status-im/keycard-go v0.2.0 // indirect github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect github.com/stretchr/objx v0.5.2 // indirect - github.com/subosito/gotenv v1.4.2 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/swaggest/assertjson v1.9.0 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tendermint/btcd v0.1.1 // indirect @@ -304,9 +303,9 @@ require ( go.nhat.io/matcher/v2 v2.0.0 // indirect go.nhat.io/wait v0.1.0 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/otel v1.19.0 // indirect - go.opentelemetry.io/otel/metric v1.19.0 // indirect - go.opentelemetry.io/otel/trace v1.19.0 // indirect + go.opentelemetry.io/otel v1.21.0 // indirect + go.opentelemetry.io/otel/metric v1.21.0 // indirect + go.opentelemetry.io/otel/trace v1.21.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/dig v1.17.0 // indirect go.uber.org/fx v1.19.2 // indirect @@ -314,17 +313,17 @@ require ( go.uber.org/ratelimit v0.2.0 // indirect go.uber.org/zap v1.24.0 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/oauth2 v0.15.0 // indirect - golang.org/x/sys v0.20.0 // indirect + golang.org/x/oauth2 v0.16.0 // indirect + golang.org/x/sys v0.22.0 // indirect golang.org/x/term v0.20.0 // indirect golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect gonum.org/v1/gonum v0.13.0 // indirect - google.golang.org/api v0.152.0 // indirect + google.golang.org/api v0.155.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 // indirect + google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect lukechampine.com/blake3 v1.2.1 // indirect @@ -342,7 +341,12 @@ require ( require ( github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect github.com/oasisprotocol/curve25519-voi v0.0.0-20220328075252-7dd334e3daae // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/snksoft/crc v1.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect ) replace ( diff --git a/go.sum b/go.sum index dd2ce7f425..04150048bc 100644 --- a/go.sum +++ b/go.sum @@ -57,8 +57,9 @@ cloud.google.com/go v0.110.7/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5x cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk= cloud.google.com/go v0.110.9/go.mod h1:rpxevX/0Lqvlbc88b7Sc1SPNdyK1riNBTUU6JXhYNpM= cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic= -cloud.google.com/go v0.111.0 h1:YHLKNupSD1KqjDbQ3+LVdQ81h/UJbJyZG203cEfnQgM= cloud.google.com/go v0.111.0/go.mod h1:0mibmpKP1TyOOFYQY5izo0LnT+ecvOQ0Sg3OdmMiNRU= +cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= +cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= @@ -1057,8 +1058,8 @@ cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= -cloud.google.com/go/storage v1.35.1 h1:B59ahL//eDfx2IIKFBeT5Atm9wnNmj3+8xG/W4WB//w= -cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= +cloud.google.com/go/storage v1.36.0 h1:P0mOkAcaJxhCTvAkMhxMfrTKiNcub4YmmPBtlhAyTr8= +cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= @@ -1213,8 +1214,9 @@ cosmossdk.io/depinject v1.0.0-alpha.4/go.mod h1:HeDk7IkR5ckZ3lMGs/o91AVUc7E596vM cosmossdk.io/errors v1.0.0-beta.7/go.mod h1:mz6FQMJRku4bY7aqS/Gwfcmr/ue91roMEKAmDUDpBfE= cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= -cosmossdk.io/log v1.3.1 h1:UZx8nWIkfbbNEWusZqzAx3ZGvu54TZacWib3EzUYmGI= cosmossdk.io/log v1.3.1/go.mod h1:2/dIomt8mKdk6vl3OWJcPk2be3pGOS8OQaLUM/3/tCM= +cosmossdk.io/log v1.4.1 h1:wKdjfDRbDyZRuWa8M+9nuvpVYxrEOwbD/CA8hvhU8QM= +cosmossdk.io/log v1.4.1/go.mod h1:k08v0Pyq+gCP6phvdI6RCGhLf/r425UT6Rk/m+o74rU= cosmossdk.io/math v1.0.0-beta.3/go.mod h1:3LYasri3Zna4XpbrTNdKsWmD5fHHkaNAod/mNT9XdE4= cosmossdk.io/math v1.0.0-beta.4/go.mod h1:An0MllWJY6PxibUpnwGk8jOm+a/qIxlKmL5Zyp9NnaM= cosmossdk.io/math v1.0.0-beta.6/go.mod h1:gUVtWwIzfSXqcOT+lBVz2jyjfua8DoBdzRsIyaUAT/8= @@ -1363,8 +1365,9 @@ github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOp github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= -github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= @@ -1695,6 +1698,8 @@ github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230428030218-4003588d1b74/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= +github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= github.com/cockroachdb/apd/v3 v3.1.0/go.mod h1:6qgPBMXjATAdD/VefbRP9NoSLKjbB4LCoA7gN4LpHs4= @@ -1733,8 +1738,9 @@ github.com/coinbase/rosetta-sdk-go v0.7.9 h1:lqllBjMnazTjIqYrOGv8h8jxjg9+hJazIGZ github.com/coinbase/rosetta-sdk-go v0.7.9/go.mod h1:0/knutI7XGVqXmmH4OQD8OckFrbQ8yMsUZTG7FXCR2M= github.com/coinbase/rosetta-sdk-go/types v1.0.0/go.mod h1:eq7W2TMRH22GTW0N0beDnN931DW0/WOI1R2sdHNHG4c= github.com/cometbft/cometbft v0.34.27-alpha.1/go.mod h1:hct3hasQ2hIF3HoD7foVw4RaqTNSSeJ/lgcrVK6uDvs= -github.com/cometbft/cometbft v0.37.4 h1:xyvvEqlyfK8MgNIIKVJaMsuIp03wxOcFmVkT26+Ikpg= github.com/cometbft/cometbft v0.37.4/go.mod h1:Cmg5Hp4sNpapm7j+x0xRyt2g0juQfmB752ous+pA0G8= +github.com/cometbft/cometbft v0.37.5 h1:/U/TlgMh4NdnXNo+YU9T2NMCWyhXNDF34Mx582jlvq0= +github.com/cometbft/cometbft v0.37.5/go.mod h1:QC+mU0lBhKn8r9qvmnq53Dmf3DWBt4VtkcKw2C81wxY= github.com/cometbft/cometbft-db v0.7.0/go.mod h1:yiKJIm2WKrt6x8Cyxtq9YTEcIMPcEe4XPxhgX59Fzf0= github.com/cometbft/cometbft-db v0.12.0 h1:v77/z0VyfSU7k682IzZeZPFZrQAKiQwkqGN0QzAjMi0= github.com/cometbft/cometbft-db v0.12.0/go.mod h1:aX2NbCrjNVd2ZajYxt1BsiFf/Z+TQ2MN0VxdicheYuw= @@ -1891,12 +1897,14 @@ github.com/cosmos/cosmos-db v0.0.0-20221226095112-f3c38ecb5e32/go.mod h1:kwMlEC4 github.com/cosmos/cosmos-proto v1.0.0-alpha7/go.mod h1:dosO4pSAbJF8zWCzCoTWP7nNsjcvSUBQmniFxDg5daw= github.com/cosmos/cosmos-proto v1.0.0-alpha8/go.mod h1:6/p+Bc4O8JKeZqe0VqUGTX31eoYqemTT4C1hLCWsO7I= github.com/cosmos/cosmos-proto v1.0.0-beta.1/go.mod h1:8k2GNZghi5sDRFw/scPL8gMSowT1vDA+5ouxL8GjaUE= -github.com/cosmos/cosmos-proto v1.0.0-beta.4 h1:aEL7tU/rLOmxZQ9z4i7mzxcLbSCY48OdY7lIWTLG7oU= github.com/cosmos/cosmos-proto v1.0.0-beta.4/go.mod h1:oeB+FyVzG3XrQJbJng0EnV8Vljfk9XvTIpGILNU/9Co= +github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA= +github.com/cosmos/cosmos-proto v1.0.0-beta.5/go.mod h1:hQGLpiIUloJBMdQMMWb/4wRApmI9hjHH05nefC0Ojec= github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20221207001918-ed5124f932fd/go.mod h1:dmCp0cYz6/S5KWKJ9QzePRwWNEbcSu+jbBTRgnzPnPo= github.com/cosmos/cosmos-sdk v0.47.0-rc2.0.20230220103612-f094a0c33410/go.mod h1:SNeHakoKi9YlfhI53+ijEZZIHp90NrB1By4NgWN0KZ0= -github.com/cosmos/cosmos-sdk v0.47.10 h1:Wxf5yEN3jZbG4fftxAMKB6rpd8ME0mxuCVihpz65dt0= github.com/cosmos/cosmos-sdk v0.47.10/go.mod h1:UWpgWkhcsBIATS68uUC0del7IiBN4hPv/vqg8Zz23uw= +github.com/cosmos/cosmos-sdk v0.47.14 h1:vD9JyIdlbVaXMOE/BLamViQvylfUq0E0FpqdPVv/fWw= +github.com/cosmos/cosmos-sdk v0.47.14/go.mod h1:GrDj/zd9Tiuy8ZpG9PbUbhghCVU7lwyH0GS7CpxHpyM= github.com/cosmos/cosmos-sdk/db v1.0.0-beta.1.0.20220726092710-f848e4300a8a/go.mod h1:c8IO23vgNxueCCJlSI9awQtcxsvc+buzaeThB85qfBU= github.com/cosmos/cosmos-sdk/ics23/go v0.8.0 h1:iKclrn3YEOwk4jQHT2ulgzuXyxmzmPczUalMwW4XH9k= github.com/cosmos/cosmos-sdk/ics23/go v0.8.0/go.mod h1:2a4dBq88TUoqoWAU5eu0lGvpFP3wWDPgdHPargtyw30= @@ -1909,8 +1917,9 @@ github.com/cosmos/gogoproto v1.4.1/go.mod h1:Ac9lzL4vFpBMcptJROQ6dQ4M3pOEK5Z/l0Q github.com/cosmos/gogoproto v1.4.2/go.mod h1:cLxOsn1ljAHSV527CHOtaIP91kK6cCrZETRBrkzItWU= github.com/cosmos/gogoproto v1.4.3/go.mod h1:0hLIG5TR7IvV1fme1HCFKjfzW9X2x0Mo+RooWXCnOWU= github.com/cosmos/gogoproto v1.4.4/go.mod h1:/yl6/nLwsZcZ2JY3OrqjRqvqCG9InUMcXRfRjQiF9DU= -github.com/cosmos/gogoproto v1.4.10 h1:QH/yT8X+c0F4ZDacDv3z+xE3WU1P1Z3wQoLMBRJoKuI= github.com/cosmos/gogoproto v1.4.10/go.mod h1:3aAZzeRWpAwr+SS/LLkICX2/kDFyaYVzckBDzygIxek= +github.com/cosmos/gogoproto v1.7.0 h1:79USr0oyXAbxg3rspGh/m4SWNyoz/GLaAh0QlCe2fro= +github.com/cosmos/gogoproto v1.7.0/go.mod h1:yWChEv5IUEYURQasfyBW5ffkMHR/90hiHgbNgrtp4j0= github.com/cosmos/gorocksdb v1.2.0/go.mod h1:aaKvKItm514hKfNJpUJXnnOWeBnk2GL4+Qw9NHizILw= github.com/cosmos/iavl v0.19.4/go.mod h1:X9PKD3J0iFxdmgNLa7b2LYWdsGd90ToV5cAONApkEPw= github.com/cosmos/iavl v0.20.0-alpha4/go.mod h1:WO7FyvaZJoH65+HFOsDir7xU9FWk2w9cHXNW1XHcl7A= @@ -1963,8 +1972,9 @@ github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2 github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= @@ -2108,6 +2118,8 @@ github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6Ni github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= github.com/envoyproxy/protoc-gen-validate v1.0.1/go.mod h1:0vj8bNkYbSTNS2PIyH87KZaeN4x9zpL9Qt8fQC7d+vs= github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= +github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= +github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/esimonov/ifshort v1.0.4/go.mod h1:Pe8zjlRrJ80+q2CxHLfEOfTwxCZ4O+MuhcHcfgNWTk0= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA9o9VY= @@ -2125,13 +2137,15 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= +github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o= github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/firefart/nonamedreturns v1.0.1/go.mod h1:D3dpIBojGGNh5UfElmwPu73SwDCm+VKhHYqwlNOk2uQ= github.com/firefart/nonamedreturns v1.0.4/go.mod h1:TDhe/tjI1BXo48CmYbUduTV7BdIga8MAO/xbKdcVsGI= github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY= @@ -2155,16 +2169,17 @@ github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2 github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= -github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= -github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/frumioj/crypto11 v1.2.5-0.20210823151709-946ce662cc0e h1:HRagc2sBsKLDvVVXQMaCEU8ueRFAl3txucwykhQPbGc= github.com/frumioj/crypto11 v1.2.5-0.20210823151709-946ce662cc0e/go.mod h1:/1u7qgWwAI7wja4BdNu5Vd5gqMtmtoiACHlhl46uY1E= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= github.com/fullstorydev/grpcurl v1.6.0/go.mod h1:ZQ+ayqbKMJNhzLmbpCiurTVlaK2M/3nqZCxaQ2Ze/sM= github.com/fzipp/gocyclo v0.5.1/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= @@ -2255,8 +2270,9 @@ github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= @@ -2381,8 +2397,9 @@ github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgR github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= -github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= +github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= +github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -2421,8 +2438,9 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -2993,8 +3011,9 @@ github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrD github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= +github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= @@ -3555,8 +3574,8 @@ github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZO github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI= github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= -github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= -github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= @@ -3593,8 +3612,9 @@ github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pointlander/compress v1.1.1-0.20190518213731-ff44bd196cc3/go.mod h1:q5NXNGzqj5uPnVuhGkZfmgHqNUhf15VLi6L9kW0VEc0= github.com/pointlander/jetset v1.0.1-0.20190518214125-eee7eff80bd4/go.mod h1:RdR1j20Aj5pB6+fw6Y9Ur7lMHpegTEjY1vc19hEZL40= github.com/pointlander/peg v1.0.1/go.mod h1:5hsGDQR2oZI4QoWz0/Kdg3VSVEC31iJw/b7WjqCBGRI= @@ -3735,8 +3755,9 @@ github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Qc0U= github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= -github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= @@ -3758,6 +3779,10 @@ github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43 github.com/sagikazarmark/crypt v0.5.0/go.mod h1:l+nzl7KWh51rpzp2h7t4MZWyiEWdhNpOAnclKvg+mdA= github.com/sagikazarmark/crypt v0.6.0/go.mod h1:U8+INwJo3nBv1m6A/8OBXAq7Jnpspk5AxSgDyEQcea8= github.com/sagikazarmark/crypt v0.8.0/go.mod h1:TmKwZAo97S4Fy4sfMH/HX/cQP5D+ijra2NyLpNNmttY= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/samber/lo v1.46.0 h1:w8G+oaCPgz1PoCJztqymCFaKwXt+5cCXn51uPxExFfQ= github.com/samber/lo v1.46.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= @@ -3845,6 +3870,8 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4lqBjiZI= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE= github.com/sourcegraph/go-diff v0.5.3/go.mod h1:v9JDtjCE4HHHCZGId75rg8gkKKa98RVjBcBGsVmMmak= github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= @@ -3863,8 +3890,8 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= -github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= @@ -3880,7 +3907,6 @@ github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUq github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -3899,8 +3925,8 @@ github.com/spf13/viper v1.11.0/go.mod h1:djo0X/bA5+tYVoCn+C7cAYJGcVn/qYLFTG8gdUs github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw= github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As= -github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= -github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= @@ -3951,8 +3977,8 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69 github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= github.com/subosito/gotenv v1.4.0/go.mod h1:mZd6rFysKEcUhUHXJk0C/08wAgyDBFuwEYL7vWWGaGo= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= -github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/swaggest/assertjson v1.9.0 h1:dKu0BfJkIxv/xe//mkCrK5yZbs79jL7OVf9Ija7o2xQ= github.com/swaggest/assertjson v1.9.0/go.mod h1:b+ZKX2VRiUjxfUIal0HDN85W0nHPAYUbYH5WkkSsFsU= @@ -4172,8 +4198,8 @@ github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPS github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= -github.com/zeta-chain/ethermint v0.0.0-20240927155358-f34e2a4a86f1 h1:o0Sh6Y2PKcG634hWqRWmWqBteSuoQUDxIR04OX9Llr8= -github.com/zeta-chain/ethermint v0.0.0-20240927155358-f34e2a4a86f1/go.mod h1:NeQEwcKBpKAUxIsii2F+jfyOD94jN/3fzPMv/1kVF9M= +github.com/zeta-chain/ethermint v0.0.0-20241010181243-044e22bdb7e7 h1:eW5aAW9Ag4GDMa7qzsQm6EWC6SENQUokHUpCdS+WSSg= +github.com/zeta-chain/ethermint v0.0.0-20241010181243-044e22bdb7e7/go.mod h1:bY9wUmkSjTJ65U7LF3e9Pc2737NqxCXGN+b/U2Rm5rU= github.com/zeta-chain/go-ethereum v1.10.26-spc h1:NvY4rR9yw52wfxWt7YoFsWbaIwVMyOtTsWKqGAXk+sE= github.com/zeta-chain/go-ethereum v1.10.26-spc/go.mod h1:/6CsT5Ceen2WPLI/oCA3xMcZ5sWMF/D46SjM/ayY0Oo= github.com/zeta-chain/go-libp2p v0.0.0-20240710192637-567fbaacc2b4 h1:FmO3HfVdZ7LzxBUfg6sVzV7ilKElQU2DZm8PxJ7KcYI= @@ -4182,8 +4208,8 @@ github.com/zeta-chain/go-tss v0.0.0-20240916173049-89fee4b0ae7f h1:XqUvw9a3EnDa2 github.com/zeta-chain/go-tss v0.0.0-20240916173049-89fee4b0ae7f/go.mod h1:B1FDE6kHs8hozKSX1/iXgCdvlFbS6+FeAupoBHDK0Cc= github.com/zeta-chain/keystone/keys v0.0.0-20240826165841-3874f358c138 h1:vck/FcIIpFOvpBUm0NO17jbEtmSz/W/a5Y4jRuSJl6I= github.com/zeta-chain/keystone/keys v0.0.0-20240826165841-3874f358c138/go.mod h1:U494OsZTWsU75hqoriZgMdSsgSGP1mUL1jX+wN/Aez8= -github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20240924201108-3a274ce7bad0 h1:GbfO2dyjSAWqBH0xDIttwPB2fE5A+zw0UUbEoW3S3wU= -github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20240924201108-3a274ce7bad0/go.mod h1:SjT7QirtJE8stnAe1SlNOanxtfSfijJm3MGJ+Ax7w7w= +github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20241021075719-d40d2e28467c h1:ZoFxMMZtivRLquXVq1sEVlT45UnTPMO1MSXtc88nDv4= +github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20241021075719-d40d2e28467c/go.mod h1:SjT7QirtJE8stnAe1SlNOanxtfSfijJm3MGJ+Ax7w7w= github.com/zeta-chain/tss-lib v0.0.0-20240916163010-2e6b438bd901 h1:9whtN5fjYHfk4yXIuAsYP2EHxImwDWDVUOnZJ2pfL3w= github.com/zeta-chain/tss-lib v0.0.0-20240916163010-2e6b438bd901/go.mod h1:d2iTC62s9JwKiCMPhcDDXbIZmuzAyJ4lwso0H5QyRbk= github.com/zondax/hid v0.9.1/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= @@ -4260,17 +4286,22 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.2 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.29.0/go.mod h1:LsankqVDx4W+RhZNA5uWarULII/MBhF5qwCYxTuyXjs= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.33.0/go.mod h1:y/SlJpJQPd2UzfBCj0E9Flk9FDCtTyqUmaCB41qFrWI= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.36.3/go.mod h1:Dts42MGkzZne2yCru741+bFiTMWkIj/LLRizad7b9tw= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.29.0/go.mod h1:vHItvsnJtp7ES++nFLLFBzUWny7fJQSvTlxFcqQGUr4= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.29.0/go.mod h1:tLYsuf2v8fZreBVwp9gVMhefZlLFZaUiNVSq8QxXRII= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs= go.opentelemetry.io/otel v1.4.0/go.mod h1:jeAqMFKy2uLIxCtKxoFj0FAL5zAPKQagc3+GtBWakzk= go.opentelemetry.io/otel v1.4.1/go.mod h1:StM6F/0fSwpd8dKWDCdRr7uRvEPYdW0hBSlbdTiUde4= go.opentelemetry.io/otel v1.8.0/go.mod h1:2pkj+iMj0o03Y+cW6/m8Y4WkRdYN3AvCXCnzRMp9yvM= go.opentelemetry.io/otel v1.11.0/go.mod h1:H2KtuEphyMvlhZ+F7tg9GRhAOe60moNx61Ex+WmiKkk= -go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= +go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= +go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= go.opentelemetry.io/otel/exporters/jaeger v1.4.1/go.mod h1:ZW7vkOu9nC1CxsD8bHNHCia5JUbwP39vxgd1q4Z5rCI= go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4= @@ -4285,14 +4316,16 @@ go.opentelemetry.io/otel/internal/metric v0.27.0/go.mod h1:n1CVxRqKqYZtqyTh9U/on go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= go.opentelemetry.io/otel/metric v0.27.0/go.mod h1:raXDJ7uP2/Jc0nVZWQjJtzoyssOYWu/+pjZqRzfvZ7g= go.opentelemetry.io/otel/metric v0.32.3/go.mod h1:pgiGmKohxHyTPHGOff+vrtIH39/R9fiO/WoenUQ3kcc= -go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= +go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= +go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs= go.opentelemetry.io/otel/sdk v1.4.1/go.mod h1:NBwHDgDIBYjwK2WNu1OPgsIc2IJzmBXNnvIJxJc8BpE= -go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= +go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= +go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= @@ -4301,8 +4334,9 @@ go.opentelemetry.io/otel/trace v1.4.0/go.mod h1:uc3eRsqDfWs9R7b92xbQbU42/eTNz4N+ go.opentelemetry.io/otel/trace v1.4.1/go.mod h1:iYEVbroFCNut9QkwEczV9vMRPHNKSSwYZjulEtsmhFc= go.opentelemetry.io/otel/trace v1.8.0/go.mod h1:0Bt3PXY8w+3pheS3hQUt+wow8b1ojPaTBoTCh2zIFI4= go.opentelemetry.io/otel/trace v1.11.0/go.mod h1:nyYjis9jy0gytE9LXGU+/m1sHTKbRY0fX0hulNNDP1U= -go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= +go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= +go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= go.opentelemetry.io/proto/otlp v0.12.0/go.mod h1:TsIjwGWIx5VFYv9KGVlOpxoBl5Dy+63SUguV7GGvlSQ= @@ -4452,8 +4486,9 @@ golang.org/x/exp v0.0.0-20221019170559-20944726eadf/go.mod h1:cyybsKvd6eL0RnXn6p golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20230131160201-f062dba9d201/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb h1:xIApU0ow1zwMa2uL1VDNeQlNVFTWMQxZUZCMDy0Q4Us= golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20220613132600-b0d781184e0d/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= @@ -4672,8 +4707,8 @@ golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= -golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= -golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= +golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= +golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -4905,8 +4940,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -5226,8 +5261,8 @@ google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvy google.golang.org/api v0.128.0/go.mod h1:Y611qgqaE92On/7g65MQgxYul3c0rEB894kniWLY750= google.golang.org/api v0.139.0/go.mod h1:CVagp6Eekz9CjGZ718Z+sloknzkDJE7Vc1Ckj9+viBk= google.golang.org/api v0.149.0/go.mod h1:Mwn1B7JTXrzXtnvmzQE2BD6bYZQ8DShKZDZbeN9I7qI= -google.golang.org/api v0.152.0 h1:t0r1vPnfMc260S2Ci+en7kfCZaLOPs5KI0sVV/6jZrY= -google.golang.org/api v0.152.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY= +google.golang.org/api v0.155.0 h1:vBmGhCYs0djJttDNynWo44zosHlPvHmA0XiN2zP2DtA= +google.golang.org/api v0.155.0/go.mod h1:GI5qK5f40kCpHfPn6+YzGAByIKWv8ujFnmoWm7Igduk= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -5428,8 +5463,9 @@ google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405/go.mod h1:3WDQMjmJ google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f/go.mod h1:nWSwAFPb+qfNJXsoeO3Io7zf4tMSfN8EA8RlDA04GhY= google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3/go.mod h1:5RBcpGRxr25RbDzY5w+dmaqpSEvl8Gwl1x2CICf60ic= -google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 h1:nz5NESFLZbJGPFxDT/HCn+V1mZ8JGNoY4nUpmW/Y2eg= google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0= +google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= +google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8= google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= @@ -5448,8 +5484,9 @@ google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go. google.golang.org/genproto/googleapis/api v0.0.0-20231030173426-d783a09b4405/go.mod h1:oT32Z4o8Zv2xPQTg0pbVaPr0MPOH6f14RgXt7zfIpwg= google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f/go.mod h1:Uy9bTZJqmfrw2rIBxgGLnamc78euZULUBrLZ9XTITKI= -google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 h1:s1w3X6gQxwrLEpxnLd/qXTVLgQE2yXwaOaoa6IlY/+o= google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0/go.mod h1:CAny0tYF+0/9rmDB9fahA9YLzX3+AEVl1qXbv5hhj6c= +google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU= +google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA= google.golang.org/genproto/googleapis/bytestream v0.0.0-20230807174057-1744710a1577/go.mod h1:NjCQG/D8JandXxM57PZbAJL1DCNL6EypA0vPPwfsc7c= google.golang.org/genproto/googleapis/bytestream v0.0.0-20231030173426-d783a09b4405/go.mod h1:GRUCuLdzVqZte8+Dl/D4N25yLzcGqqWaYkeVOwulFqw= @@ -5473,8 +5510,9 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go. google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= google.golang.org/genproto/googleapis/rpc v0.0.0-20231211222908-989df2bf70f3/go.mod h1:eJVxU6o+4G1PSczBr85xmyvSNYAKvAYgkub40YGomFM= google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 h1:gphdwh0npgs8elJ4T6J+DQJHPVF7RsuJHCfwztUb4J4= google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -5539,8 +5577,9 @@ google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSs google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/grpc v1.60.0/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= -google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= +google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= +google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -5565,8 +5604,9 @@ google.golang.org/protobuf v1.29.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pkg/chains/chain.go b/pkg/chains/chain.go index f5f6ee605d..665251eb39 100644 --- a/pkg/chains/chain.go +++ b/pkg/chains/chain.go @@ -7,6 +7,7 @@ import ( "github.com/btcsuite/btcd/chaincfg" ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/tonkeeper/tongo/ton" ) // Validate checks whether the chain is valid @@ -93,6 +94,10 @@ func (chain Chain) IsBitcoinChain() bool { return chain.Consensus == Consensus_bitcoin } +func (chain Chain) IsTONChain() bool { + return chain.Consensus == Consensus_catchain_consensus +} + // DecodeAddressFromChainID decode the address string to bytes // additionalChains is a list of additional chains to search from // in practice, it is used in the protocol to dynamically support new chains without doing an upgrade @@ -104,6 +109,14 @@ func DecodeAddressFromChainID(chainID int64, addr string, additionalChains []Cha return []byte(addr), nil case IsSolanaChain(chainID, additionalChains): return []byte(addr), nil + case IsTONChain(chainID, additionalChains): + // e.g. `0:55798cb7b87168251a7c39f6806b8c202f6caa0f617a76f4070b3fdacfd056a1` + acc, err := ton.ParseAccountID(addr) + if err != nil { + return nil, fmt.Errorf("invalid TON address %q: %w", addr, err) + } + + return []byte(acc.ToRaw()), nil default: return nil, fmt.Errorf("chain (%d) not supported", chainID) } @@ -132,6 +145,11 @@ func IsSolanaChain(chainID int64, additionalChains []Chain) bool { return ChainIDInChainList(chainID, ChainListByNetwork(Network_solana, additionalChains)) } +// IsTONChain returns true is the chain is TON chain +func IsTONChain(chainID int64, additionalChains []Chain) bool { + return ChainIDInChainList(chainID, ChainListByNetwork(Network_ton, additionalChains)) +} + // IsEthereumChain returns true if the chain is an Ethereum chain // additionalChains is a list of additional chains to search from // in practice, it is used in the protocol to dynamically support new chains without doing an upgrade diff --git a/pkg/chains/chain_test.go b/pkg/chains/chain_test.go index 349651e7b9..4da81c7eee 100644 --- a/pkg/chains/chain_test.go +++ b/pkg/chains/chain_test.go @@ -73,7 +73,7 @@ func TestChain_Validate(t *testing.T) { chain: chains.Chain{ ChainId: 42, Name: "foo", - Network: chains.Network_solana + 1, + Network: chains.Network_ton + 1, NetworkType: chains.NetworkType_testnet, Vm: chains.Vm_evm, Consensus: chains.Consensus_op_stack, @@ -101,7 +101,7 @@ func TestChain_Validate(t *testing.T) { Name: "foo", Network: chains.Network_base, NetworkType: chains.NetworkType_devnet, - Vm: chains.Vm_svm + 1, + Vm: chains.Vm_tvm + 1, Consensus: chains.Consensus_op_stack, IsExternal: true, }, @@ -115,7 +115,7 @@ func TestChain_Validate(t *testing.T) { Network: chains.Network_base, NetworkType: chains.NetworkType_devnet, Vm: chains.Vm_evm, - Consensus: chains.Consensus_solana_consensus + 1, + Consensus: chains.Consensus_catchain_consensus + 1, IsExternal: true, }, errStr: "invalid consensus", @@ -301,6 +301,19 @@ func TestDecodeAddressFromChainID(t *testing.T) { addr: "DCAK36VfExkPdAkYUQg6ewgxyinvcEyPLyHjRbmveKFw", want: []byte("DCAK36VfExkPdAkYUQg6ewgxyinvcEyPLyHjRbmveKFw"), }, + { + name: "TON", + chainID: chains.TONMainnet.ChainId, + addr: "0:55798cb7b87168251a7c39f6806b8c202f6caa0f617a76f4070b3fdacfd056a1", + want: []byte("0:55798cb7b87168251a7c39f6806b8c202f6caa0f617a76f4070b3fdacfd056a1"), + }, + { + name: "TON", + chainID: chains.TONMainnet.ChainId, + // human friendly address should be always represented in raw format + addr: "EQB3ncyBUTjZUA5EnFKR5_EnOMI9V1tTEAAPaiU71gc4TiUt", + want: []byte("0:779dcc815138d9500e449c5291e7f12738c23d575b5310000f6a253bd607384e"), + }, { name: "Non-supported chain", chainID: 9999, diff --git a/pkg/chains/chains.go b/pkg/chains/chains.go index addd6a14df..70692ccc9c 100644 --- a/pkg/chains/chains.go +++ b/pkg/chains/chains.go @@ -113,6 +113,18 @@ var ( Name: "solana_mainnet", } + TONMainnet = Chain{ + // T[20] O[15] N[14] mainnet[0] :) + ChainId: 2015140, + Network: Network_ton, + NetworkType: NetworkType_mainnet, + Vm: Vm_tvm, + Consensus: Consensus_catchain_consensus, + IsExternal: true, + CctxGateway: CCTXGateway_observers, + Name: "ton_mainnet", + } + /** * Testnet chains */ @@ -249,6 +261,17 @@ var ( Name: "solana_devnet", } + TONTestnet = Chain{ + ChainId: 2015141, + Network: Network_ton, + NetworkType: NetworkType_testnet, + Vm: Vm_tvm, + Consensus: Consensus_catchain_consensus, + IsExternal: true, + CctxGateway: CCTXGateway_observers, + Name: "ton_testnet", + } + /** * Devnet chains */ @@ -325,6 +348,17 @@ var ( Name: "solana_localnet", } + TONLocalnet = Chain{ + ChainId: 2015142, + Network: Network_ton, + NetworkType: NetworkType_privnet, + Vm: Vm_tvm, + Consensus: Consensus_catchain_consensus, + IsExternal: true, + CctxGateway: CCTXGateway_observers, + Name: "ton_localnet", + } + /** * Deprecated chains */ @@ -392,6 +426,9 @@ func DefaultChainsList() []Chain { SolanaMainnet, SolanaDevnet, SolanaLocalnet, + TONMainnet, + TONTestnet, + TONLocalnet, } } @@ -449,17 +486,6 @@ func ChainListByGateway(gateway CCTXGateway, additionalChains []Chain) []Chain { return chainList } -// ChainListForHeaderSupport returns a list of chains that support headers -func ChainListForHeaderSupport(additionalChains []Chain) []Chain { - var chainList []Chain - for _, chain := range CombineDefaultChainsList(additionalChains) { - if chain.Consensus == Consensus_ethereum || chain.Consensus == Consensus_bitcoin { - chainList = append(chainList, chain) - } - } - return chainList -} - // ZetaChainFromCosmosChainID returns a ZetaChain chain object from a Cosmos chain ID func ZetaChainFromCosmosChainID(chainID string) (Chain, error) { ethChainID, err := CosmosToEthChainID(chainID) diff --git a/pkg/chains/chains.pb.go b/pkg/chains/chains.pb.go index 217ad55328..ad5ec2f77a 100644 --- a/pkg/chains/chains.pb.go +++ b/pkg/chains/chains.pb.go @@ -156,6 +156,7 @@ const ( Network_optimism Network = 5 Network_base Network = 6 Network_solana Network = 7 + Network_ton Network = 8 ) var Network_name = map[int32]string{ @@ -167,6 +168,7 @@ var Network_name = map[int32]string{ 5: "optimism", 6: "base", 7: "solana", + 8: "ton", } var Network_value = map[string]int32{ @@ -178,6 +180,7 @@ var Network_value = map[string]int32{ "optimism": 5, "base": 6, "solana": 7, + "ton": 8, } func (x Network) String() string { @@ -229,18 +232,21 @@ const ( Vm_no_vm Vm = 0 Vm_evm Vm = 1 Vm_svm Vm = 2 + Vm_tvm Vm = 3 ) var Vm_name = map[int32]string{ 0: "no_vm", 1: "evm", 2: "svm", + 3: "tvm", } var Vm_value = map[string]int32{ "no_vm": 0, "evm": 1, "svm": 2, + "tvm": 3, } func (x Vm) String() string { @@ -257,11 +263,12 @@ func (Vm) EnumDescriptor() ([]byte, []int) { type Consensus int32 const ( - Consensus_ethereum Consensus = 0 - Consensus_tendermint Consensus = 1 - Consensus_bitcoin Consensus = 2 - Consensus_op_stack Consensus = 3 - Consensus_solana_consensus Consensus = 4 + Consensus_ethereum Consensus = 0 + Consensus_tendermint Consensus = 1 + Consensus_bitcoin Consensus = 2 + Consensus_op_stack Consensus = 3 + Consensus_solana_consensus Consensus = 4 + Consensus_catchain_consensus Consensus = 5 ) var Consensus_name = map[int32]string{ @@ -270,14 +277,16 @@ var Consensus_name = map[int32]string{ 2: "bitcoin", 3: "op_stack", 4: "solana_consensus", + 5: "catchain_consensus", } var Consensus_value = map[string]int32{ - "ethereum": 0, - "tendermint": 1, - "bitcoin": 2, - "op_stack": 3, - "solana_consensus": 4, + "ethereum": 0, + "tendermint": 1, + "bitcoin": 2, + "op_stack": 3, + "solana_consensus": 4, + "catchain_consensus": 5, } func (x Consensus) String() string { @@ -455,56 +464,57 @@ func init() { } var fileDescriptor_236b85e7bff6130d = []byte{ - // 770 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0xcd, 0x8e, 0xe4, 0x34, - 0x10, 0xee, 0x24, 0xfd, 0x5b, 0x3d, 0x3f, 0x5e, 0xef, 0x00, 0x61, 0x25, 0x9a, 0x01, 0x09, 0x68, - 0x8d, 0xa0, 0x47, 0xc0, 0x91, 0x03, 0x68, 0x47, 0x2c, 0x42, 0x88, 0x3d, 0x84, 0xd5, 0x0a, 0x71, - 0x69, 0xdc, 0xee, 0x22, 0x6d, 0x75, 0x6c, 0x47, 0xb1, 0x3b, 0xbb, 0xcd, 0x53, 0xf0, 0x10, 0x1c, - 0x90, 0x78, 0x11, 0x8e, 0x7b, 0xe4, 0x88, 0x66, 0x1e, 0x04, 0x64, 0xc7, 0x49, 0x0f, 0x97, 0x9d, - 0x39, 0xc5, 0xfe, 0xf2, 0x7d, 0x55, 0x5f, 0x55, 0xd9, 0x86, 0x8b, 0x5f, 0xd1, 0x32, 0xbe, 0x61, - 0x42, 0x5d, 0xfa, 0x95, 0xae, 0xf0, 0xb2, 0xdc, 0xe6, 0x97, 0x1e, 0x32, 0xe1, 0xb3, 0x28, 0x2b, - 0x6d, 0x35, 0x7d, 0xa7, 0xe3, 0x2e, 0x5a, 0xee, 0xa2, 0xdc, 0xe6, 0x8b, 0x86, 0xf4, 0xe8, 0x2c, - 0xd7, 0xb9, 0xf6, 0xcc, 0x4b, 0xb7, 0x6a, 0x44, 0xef, 0xff, 0x9b, 0xc0, 0xe0, 0xca, 0x11, 0xe8, - 0xdb, 0x30, 0xf6, 0xcc, 0xa5, 0x58, 0xa7, 0xf1, 0x79, 0x34, 0x4f, 0xb2, 0x91, 0xdf, 0x7f, 0xbb, - 0xa6, 0xdf, 0x01, 0x34, 0xbf, 0x14, 0x93, 0x98, 0x46, 0xe7, 0xd1, 0xfc, 0xe4, 0xb3, 0xf9, 0xe2, - 0xb5, 0xe9, 0x16, 0x3e, 0xe8, 0x53, 0x26, 0xf1, 0x71, 0x9c, 0x46, 0xd9, 0x84, 0xb7, 0x5b, 0xfa, - 0x15, 0x8c, 0x14, 0xda, 0x17, 0xba, 0xda, 0xa6, 0x89, 0x8f, 0xf4, 0xe1, 0x1d, 0x91, 0x9e, 0x36, - 0xec, 0xac, 0x95, 0xd1, 0xef, 0xe1, 0x28, 0x2c, 0x97, 0x76, 0x5f, 0x62, 0xda, 0xf7, 0x61, 0x2e, - 0xee, 0x17, 0xe6, 0xd9, 0xbe, 0xc4, 0x6c, 0xaa, 0x0e, 0x1b, 0xfa, 0x29, 0xc4, 0xb5, 0x4c, 0x07, - 0x3e, 0xc8, 0x7b, 0x77, 0x04, 0x79, 0x2e, 0xb3, 0xb8, 0x96, 0xf4, 0x09, 0x4c, 0xb8, 0x56, 0x06, - 0x95, 0xd9, 0x99, 0x74, 0x78, 0xbf, 0x7e, 0xb4, 0xfc, 0xec, 0x20, 0xa5, 0xef, 0xc2, 0x54, 0x98, - 0x25, 0xbe, 0xb4, 0x58, 0x29, 0x56, 0xa4, 0xa3, 0xf3, 0x68, 0x3e, 0xce, 0x40, 0x98, 0xaf, 0x03, - 0xe2, 0x4a, 0xe5, 0xdc, 0xbe, 0x5c, 0xe6, 0xcc, 0xe2, 0x0b, 0xb6, 0x4f, 0xc7, 0xf7, 0x2a, 0xf5, - 0xea, 0xea, 0xd9, 0x8f, 0xdf, 0x34, 0x8a, 0x6c, 0xea, 0xf4, 0x61, 0x43, 0x29, 0xf4, 0xfd, 0x08, - 0x27, 0xe7, 0xd1, 0x7c, 0x92, 0xf9, 0xf5, 0xc5, 0x17, 0x70, 0x9c, 0x21, 0x47, 0x51, 0xe3, 0x0f, - 0x96, 0xd9, 0x9d, 0xa1, 0x53, 0x18, 0xf1, 0x0a, 0x99, 0xc5, 0x35, 0xe9, 0xb9, 0x8d, 0xd9, 0x71, - 0x8e, 0xc6, 0x90, 0x88, 0x02, 0x0c, 0x7f, 0x61, 0xa2, 0xc0, 0x35, 0x89, 0x1f, 0xf5, 0xff, 0xf8, - 0x7d, 0x16, 0x5d, 0xfc, 0x99, 0xc0, 0xa4, 0x9b, 0x34, 0x9d, 0xc0, 0x00, 0x65, 0x69, 0xf7, 0xa4, - 0x47, 0x4f, 0x61, 0x8a, 0x76, 0xb3, 0x94, 0x4c, 0x28, 0x85, 0x96, 0x44, 0x94, 0xc0, 0x91, 0xb3, - 0xda, 0x21, 0xb1, 0xa3, 0xac, 0x2c, 0xef, 0x80, 0x84, 0x3e, 0x84, 0xd3, 0x52, 0x17, 0xfb, 0x5c, - 0xab, 0x0e, 0xec, 0x7b, 0x96, 0x39, 0xb0, 0x06, 0x94, 0xc2, 0x49, 0xae, 0xb1, 0x2a, 0xc4, 0xd2, - 0xa2, 0xb1, 0x0e, 0x1b, 0x3a, 0x4c, 0xee, 0xe4, 0x8a, 0x1d, 0xb0, 0x51, 0x2b, 0x6c, 0x01, 0xe8, - 0x1c, 0xb4, 0xc8, 0xb4, 0x75, 0xd0, 0x02, 0x47, 0xce, 0x81, 0xc1, 0x52, 0x17, 0xe2, 0xc0, 0x3a, - 0x76, 0x60, 0x48, 0x58, 0x68, 0xce, 0x0a, 0x07, 0x9e, 0xb4, 0xd2, 0x0a, 0x73, 0x47, 0x24, 0xa7, - 0x2e, 0x3a, 0x93, 0x7a, 0xdf, 0xe9, 0x08, 0x3d, 0x03, 0xa2, 0x4b, 0x2b, 0xa4, 0x30, 0xb2, 0xb3, - 0xff, 0xe0, 0x7f, 0x68, 0xc8, 0x45, 0xa8, 0x53, 0xaf, 0x98, 0xc1, 0x8e, 0xf7, 0xb0, 0x43, 0x5a, - 0xce, 0x99, 0x2b, 0xd2, 0xe8, 0x82, 0xa9, 0x43, 0x0f, 0xdf, 0xa0, 0x0f, 0xe0, 0x38, 0x60, 0x6b, - 0xac, 0x1d, 0xf4, 0xa6, 0xaf, 0xa1, 0x81, 0x3a, 0xbb, 0x6f, 0x85, 0x69, 0x21, 0x8c, 0xc2, 0x2d, - 0xa0, 0x23, 0x48, 0xd0, 0x6e, 0x48, 0x8f, 0x8e, 0xa1, 0xef, 0xba, 0x42, 0x22, 0x07, 0xad, 0x2c, - 0x27, 0xb1, 0x9b, 0x79, 0x98, 0x03, 0x49, 0x3c, 0x6a, 0x38, 0xe9, 0xd3, 0x23, 0x18, 0xb7, 0xc6, - 0xc9, 0xc0, 0xc9, 0x9c, 0x3d, 0x32, 0x74, 0x87, 0xa2, 0xc9, 0x47, 0x46, 0x21, 0xcd, 0x13, 0x98, - 0xde, 0xba, 0x6c, 0x2e, 0x5c, 0x6b, 0xd8, 0x9f, 0xa7, 0xb6, 0x43, 0x91, 0x4f, 0x54, 0x89, 0xba, - 0x39, 0x0e, 0x00, 0xc3, 0x50, 0x43, 0x12, 0xe2, 0x7c, 0x04, 0xf1, 0x73, 0xe9, 0x0e, 0x95, 0xd2, - 0xcb, 0x5a, 0x92, 0x9e, 0x37, 0x5d, 0xcb, 0xc6, 0xaa, 0xa9, 0x65, 0x77, 0x0a, 0x7f, 0x86, 0x49, - 0x77, 0xbd, 0x9c, 0x4f, 0xb4, 0x1b, 0xac, 0x70, 0xe7, 0x24, 0x27, 0x00, 0x16, 0xd5, 0x1a, 0x2b, - 0x29, 0x54, 0x48, 0xb9, 0x12, 0x96, 0x6b, 0xa1, 0x48, 0xdc, 0x94, 0xb4, 0x34, 0x96, 0xf1, 0x2d, - 0x49, 0xdc, 0x64, 0x42, 0xe3, 0xba, 0x0b, 0x4a, 0xfa, 0x21, 0xc3, 0xc7, 0x30, 0xbd, 0x75, 0xa9, - 0x9a, 0xa6, 0x79, 0x4b, 0xc7, 0x30, 0xd1, 0x2b, 0x83, 0x55, 0x8d, 0x95, 0x21, 0x51, 0xc3, 0x7e, - 0xfc, 0xe5, 0x5f, 0xd7, 0xb3, 0xe8, 0xd5, 0xf5, 0x2c, 0xfa, 0xe7, 0x7a, 0x16, 0xfd, 0x76, 0x33, - 0xeb, 0xbd, 0xba, 0x99, 0xf5, 0xfe, 0xbe, 0x99, 0xf5, 0x7e, 0xfa, 0x20, 0x17, 0x76, 0xb3, 0x5b, - 0x2d, 0xb8, 0x96, 0xfe, 0x41, 0xff, 0xa4, 0x79, 0xdb, 0x95, 0x5e, 0xdf, 0x7e, 0xd7, 0x57, 0x43, - 0xff, 0x38, 0x7f, 0xfe, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x86, 0x0b, 0xc3, 0x03, 0xff, 0x05, - 0x00, 0x00, + // 789 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x55, 0xcd, 0x8e, 0x23, 0x35, + 0x10, 0x4e, 0x77, 0xe7, 0xb7, 0x32, 0x3f, 0x5e, 0xef, 0xb0, 0x34, 0x2b, 0x11, 0x06, 0x24, 0x50, + 0x34, 0x82, 0x0c, 0x3f, 0x47, 0x0e, 0xa0, 0x1d, 0xb1, 0x08, 0x21, 0xf6, 0xd0, 0xac, 0x56, 0x88, + 0x4b, 0xe4, 0x38, 0x45, 0xc7, 0x4a, 0xdb, 0x6e, 0xb5, 0x9d, 0xde, 0x09, 0x4f, 0xc1, 0x43, 0x70, + 0x40, 0xe2, 0x45, 0x38, 0xee, 0x91, 0x23, 0x9a, 0x79, 0x10, 0x90, 0xdd, 0xee, 0xce, 0x70, 0xd9, + 0x9d, 0x53, 0xec, 0x2f, 0xdf, 0x57, 0xf5, 0x55, 0x95, 0xed, 0x86, 0x8b, 0x5f, 0xd1, 0x32, 0xbe, + 0x61, 0x42, 0x5d, 0xfa, 0x95, 0xae, 0xf0, 0xb2, 0xdc, 0xe6, 0x97, 0x1e, 0x32, 0xe1, 0x67, 0x51, + 0x56, 0xda, 0x6a, 0xfa, 0x6e, 0xc7, 0x5d, 0xb4, 0xdc, 0x45, 0xb9, 0xcd, 0x17, 0x0d, 0xe9, 0xf1, + 0x59, 0xae, 0x73, 0xed, 0x99, 0x97, 0x6e, 0xd5, 0x88, 0x3e, 0xf8, 0x37, 0x81, 0xc1, 0x95, 0x23, + 0xd0, 0x77, 0x60, 0xec, 0x99, 0x4b, 0xb1, 0x4e, 0xe3, 0xf3, 0x68, 0x9e, 0x64, 0x23, 0xbf, 0xff, + 0x6e, 0x4d, 0xbf, 0x07, 0x68, 0xfe, 0x52, 0x4c, 0x62, 0x1a, 0x9d, 0x47, 0xf3, 0x93, 0xcf, 0xe7, + 0x8b, 0xd7, 0xa6, 0x5b, 0xf8, 0xa0, 0xcf, 0x98, 0xc4, 0x27, 0x71, 0x1a, 0x65, 0x13, 0xde, 0x6e, + 0xe9, 0xd7, 0x30, 0x52, 0x68, 0x5f, 0xea, 0x6a, 0x9b, 0x26, 0x3e, 0xd2, 0x47, 0x6f, 0x88, 0xf4, + 0xac, 0x61, 0x67, 0xad, 0x8c, 0xfe, 0x00, 0x47, 0x61, 0xb9, 0xb4, 0xfb, 0x12, 0xd3, 0xbe, 0x0f, + 0x73, 0x71, 0xbf, 0x30, 0xcf, 0xf7, 0x25, 0x66, 0x53, 0x75, 0xd8, 0xd0, 0xcf, 0x20, 0xae, 0x65, + 0x3a, 0xf0, 0x41, 0xde, 0x7f, 0x43, 0x90, 0x17, 0x32, 0x8b, 0x6b, 0x49, 0x9f, 0xc2, 0x84, 0x6b, + 0x65, 0x50, 0x99, 0x9d, 0x49, 0x87, 0xf7, 0xeb, 0x47, 0xcb, 0xcf, 0x0e, 0x52, 0xfa, 0x1e, 0x4c, + 0x85, 0x59, 0xe2, 0xb5, 0xc5, 0x4a, 0xb1, 0x22, 0x1d, 0x9d, 0x47, 0xf3, 0x71, 0x06, 0xc2, 0x7c, + 0x13, 0x10, 0x57, 0x2a, 0xe7, 0xf6, 0x7a, 0x99, 0x33, 0x8b, 0x2f, 0xd9, 0x3e, 0x1d, 0xdf, 0xab, + 0xd4, 0xab, 0xab, 0xe7, 0x3f, 0x7d, 0xdb, 0x28, 0xb2, 0xa9, 0xd3, 0x87, 0x0d, 0xa5, 0xd0, 0xf7, + 0x23, 0x9c, 0x9c, 0x47, 0xf3, 0x49, 0xe6, 0xd7, 0x17, 0x5f, 0xc2, 0x71, 0x86, 0x1c, 0x45, 0x8d, + 0x3f, 0x5a, 0x66, 0x77, 0x86, 0x4e, 0x61, 0xc4, 0x2b, 0x64, 0x16, 0xd7, 0xa4, 0xe7, 0x36, 0x66, + 0xc7, 0x39, 0x1a, 0x43, 0x22, 0x0a, 0x30, 0xfc, 0x85, 0x89, 0x02, 0xd7, 0x24, 0x7e, 0xdc, 0xff, + 0xe3, 0xf7, 0x59, 0x74, 0xf1, 0x67, 0x02, 0x93, 0x6e, 0xd2, 0x74, 0x02, 0x03, 0x94, 0xa5, 0xdd, + 0x93, 0x1e, 0x3d, 0x85, 0x29, 0xda, 0xcd, 0x52, 0x32, 0xa1, 0x14, 0x5a, 0x12, 0x51, 0x02, 0x47, + 0xce, 0x6a, 0x87, 0xc4, 0x8e, 0xb2, 0xb2, 0xbc, 0x03, 0x12, 0xfa, 0x10, 0x4e, 0x4b, 0x5d, 0xec, + 0x73, 0xad, 0x3a, 0xb0, 0xef, 0x59, 0xe6, 0xc0, 0x1a, 0x50, 0x0a, 0x27, 0xb9, 0xc6, 0xaa, 0x10, + 0x4b, 0x8b, 0xc6, 0x3a, 0x6c, 0xe8, 0x30, 0xb9, 0x93, 0x2b, 0x76, 0xc0, 0x46, 0xad, 0xb0, 0x05, + 0xa0, 0x73, 0xd0, 0x22, 0xd3, 0xd6, 0x41, 0x0b, 0x1c, 0x39, 0x07, 0x06, 0x4b, 0x5d, 0x88, 0x03, + 0xeb, 0xd8, 0x81, 0x21, 0x61, 0xa1, 0x39, 0x2b, 0x1c, 0x78, 0xd2, 0x4a, 0x2b, 0xcc, 0x1d, 0x91, + 0x9c, 0xba, 0xe8, 0x4c, 0xea, 0x7d, 0xa7, 0x23, 0xf4, 0x0c, 0x88, 0x2e, 0xad, 0x90, 0xc2, 0xc8, + 0xce, 0xfe, 0x83, 0xff, 0xa1, 0x21, 0x17, 0xa1, 0x4e, 0xbd, 0x62, 0x06, 0x3b, 0xde, 0xc3, 0x0e, + 0x69, 0x39, 0x67, 0xae, 0x48, 0xa3, 0x0b, 0xa6, 0x0e, 0x3d, 0x7c, 0x8b, 0x3e, 0x80, 0xe3, 0x80, + 0xad, 0xb1, 0x76, 0xd0, 0x23, 0x5f, 0x43, 0x03, 0x75, 0x76, 0xdf, 0x0e, 0xd3, 0x52, 0x30, 0x0a, + 0xb7, 0x80, 0x8e, 0x20, 0x41, 0xbb, 0x21, 0x3d, 0x3a, 0x86, 0xbe, 0xeb, 0x0a, 0x89, 0x1c, 0xb4, + 0xb2, 0x9c, 0xc4, 0x6e, 0xe6, 0x61, 0x0e, 0x24, 0xf1, 0xa8, 0xe1, 0xa4, 0x4f, 0x8f, 0x60, 0xdc, + 0x1a, 0x27, 0x03, 0x27, 0x73, 0xf6, 0xc8, 0xd0, 0x1d, 0x8a, 0x26, 0x1f, 0x19, 0x39, 0xb2, 0xd5, + 0x8a, 0x8c, 0x43, 0xbe, 0xa7, 0x30, 0xbd, 0x73, 0xeb, 0x5c, 0xdc, 0xd6, 0xb9, 0x3f, 0x58, 0x6d, + 0xab, 0x22, 0x9f, 0xb1, 0x12, 0x75, 0x73, 0x2e, 0x00, 0x86, 0xa1, 0x98, 0x24, 0xc4, 0xf9, 0x14, + 0xe2, 0x17, 0xd2, 0x9d, 0x2e, 0xa5, 0x97, 0xb5, 0x24, 0x3d, 0xef, 0xbe, 0x96, 0x8d, 0x67, 0x53, + 0x4b, 0x12, 0xfb, 0xcc, 0xb5, 0xec, 0x14, 0xd7, 0x30, 0xe9, 0x2e, 0x9c, 0x73, 0x8e, 0x76, 0x83, + 0x15, 0xee, 0x9c, 0xf6, 0x04, 0xc0, 0xa2, 0x5a, 0x63, 0x25, 0x85, 0x0a, 0xb9, 0x57, 0xc2, 0x72, + 0x2d, 0x14, 0x89, 0x9b, 0x22, 0x97, 0xc6, 0x32, 0xbe, 0x25, 0x89, 0x9b, 0x55, 0x68, 0x65, 0x77, + 0x65, 0x49, 0x9f, 0x3e, 0x02, 0xca, 0x99, 0x6d, 0x1e, 0xc4, 0x03, 0x3e, 0x08, 0x99, 0x3f, 0x86, + 0xe9, 0x9d, 0xeb, 0xd7, 0xb4, 0xd7, 0x7b, 0x3e, 0x86, 0x89, 0x5e, 0x19, 0xac, 0x6a, 0xac, 0x0c, + 0x89, 0x1a, 0xf6, 0x93, 0xaf, 0xfe, 0xba, 0x99, 0x45, 0xaf, 0x6e, 0x66, 0xd1, 0x3f, 0x37, 0xb3, + 0xe8, 0xb7, 0xdb, 0x59, 0xef, 0xd5, 0xed, 0xac, 0xf7, 0xf7, 0xed, 0xac, 0xf7, 0xf3, 0x87, 0xb9, + 0xb0, 0x9b, 0xdd, 0x6a, 0xc1, 0xb5, 0xf4, 0x4f, 0xff, 0x27, 0xcd, 0x57, 0x40, 0xe9, 0xf5, 0xdd, + 0x2f, 0xc0, 0x6a, 0xe8, 0x9f, 0xf1, 0x2f, 0xfe, 0x0b, 0x00, 0x00, 0xff, 0xff, 0xb9, 0xaa, 0xd0, + 0x56, 0x29, 0x06, 0x00, 0x00, } func (m *Chain) Marshal() (dAtA []byte, err error) { diff --git a/pkg/chains/chains_test.go b/pkg/chains/chains_test.go index 90545bee1c..73c3521ffa 100644 --- a/pkg/chains/chains_test.go +++ b/pkg/chains/chains_test.go @@ -36,6 +36,7 @@ func TestChainListByNetworkType(t *testing.T) { chains.OptimismMainnet, chains.BaseMainnet, chains.SolanaMainnet, + chains.TONMainnet, }, }, { @@ -54,6 +55,7 @@ func TestChainListByNetworkType(t *testing.T) { chains.OptimismSepolia, chains.BaseSepolia, chains.SolanaDevnet, + chains.TONTestnet, }, }, { @@ -64,6 +66,7 @@ func TestChainListByNetworkType(t *testing.T) { chains.BitcoinRegtest, chains.GoerliLocalnet, chains.SolanaLocalnet, + chains.TONLocalnet, }, }, } @@ -132,6 +135,11 @@ func TestChainListByNetwork(t *testing.T) { chains.Network_solana, []chains.Chain{chains.SolanaMainnet, chains.SolanaDevnet, chains.SolanaLocalnet}, }, + { + "TON", + chains.Network_ton, + []chains.Chain{chains.TONMainnet, chains.TONTestnet, chains.TONLocalnet}, + }, } for _, lt := range listTests { @@ -168,6 +176,9 @@ func TestDefaultChainList(t *testing.T) { chains.SolanaMainnet, chains.SolanaDevnet, chains.SolanaLocalnet, + chains.TONMainnet, + chains.TONTestnet, + chains.TONLocalnet, }, chains.DefaultChainsList()) } @@ -202,6 +213,9 @@ func TestChainListByGateway(t *testing.T) { chains.SolanaMainnet, chains.SolanaDevnet, chains.SolanaLocalnet, + chains.TONMainnet, + chains.TONTestnet, + chains.TONLocalnet, }, }, { @@ -246,6 +260,9 @@ func TestExternalChainList(t *testing.T) { chains.SolanaMainnet, chains.SolanaDevnet, chains.SolanaLocalnet, + chains.TONMainnet, + chains.TONTestnet, + chains.TONLocalnet, }, chains.ExternalChainList([]chains.Chain{})) } diff --git a/pkg/chains/conversion.go b/pkg/chains/conversion.go index 3200b76693..5d03b77007 100644 --- a/pkg/chains/conversion.go +++ b/pkg/chains/conversion.go @@ -1,10 +1,8 @@ package chains import ( - "encoding/hex" "fmt" - "cosmossdk.io/errors" "github.com/btcsuite/btcd/chaincfg/chainhash" ethcommon "github.com/ethereum/go-ethereum/common" ) @@ -28,26 +26,3 @@ func StringToHash(chainID int64, hash string, additionalChains []Chain) ([]byte, } return nil, fmt.Errorf("cannot convert hash to bytes for chain %d", chainID) } - -// ParseAddressAndData parses the message string into an address and data -// message is hex encoded byte array -// [ contractAddress calldata ] -// [ 20B, variable] -func ParseAddressAndData(message string) (ethcommon.Address, []byte, error) { - if len(message) == 0 { - return ethcommon.Address{}, nil, nil - } - - data, err := hex.DecodeString(message) - if err != nil { - return ethcommon.Address{}, nil, errors.Wrap(err, "message should be a hex encoded string") - } - - if len(data) < 20 { - return ethcommon.Address{}, data, nil - } - - address := ethcommon.BytesToAddress(data[:20]) - data = data[20:] - return address, data, nil -} diff --git a/pkg/chains/utils_test.go b/pkg/chains/utils_test.go index 915fa2b8ca..7552e10967 100644 --- a/pkg/chains/utils_test.go +++ b/pkg/chains/utils_test.go @@ -1,7 +1,6 @@ package chains import ( - "encoding/hex" "testing" "github.com/btcsuite/btcd/chaincfg/chainhash" @@ -66,39 +65,3 @@ func TestStringToHash(t *testing.T) { }) } } - -func TestParseAddressAndData(t *testing.T) { - expectedShortMsgResult, err := hex.DecodeString("1a2b3c4d5e6f708192a3b4c5d6e7f808") - require.NoError(t, err) - tests := []struct { - name string - message string - expectAddr ethcommon.Address - expectData []byte - wantErr bool - }{ - { - "valid msg", - "95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5", - ethcommon.HexToAddress("95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5"), - []byte{}, - false, - }, - {"empty msg", "", ethcommon.Address{}, nil, false}, - {"invalid hex", "invalidHex", ethcommon.Address{}, nil, true}, - {"short msg", "1a2b3c4d5e6f708192a3b4c5d6e7f808", ethcommon.Address{}, expectedShortMsgResult, false}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - addr, data, err := ParseAddressAndData(tt.message) - if tt.wantErr { - require.Error(t, err) - } else { - require.NoError(t, err) - require.Equal(t, tt.expectAddr, addr) - require.Equal(t, tt.expectData, data) - } - }) - } -} diff --git a/pkg/contracts/testdappv2/TestDAppV2.abi b/pkg/contracts/testdappv2/TestDAppV2.abi index cbfdc1bfca..33270e31e3 100644 --- a/pkg/contracts/testdappv2/TestDAppV2.abi +++ b/pkg/contracts/testdappv2/TestDAppV2.abi @@ -1,4 +1,17 @@ [ + { + "inputs": [], + "name": "NO_MESSAGE_CALL", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -60,19 +73,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [], - "name": "expectedOnCallSender", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [ { @@ -127,32 +127,20 @@ { "inputs": [ { - "components": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "internalType": "struct TestDAppV2.MessageContext", - "name": "messageContext", - "type": "tuple" - }, - { - "internalType": "bytes", - "name": "message", - "type": "bytes" + "internalType": "address", + "name": "sender", + "type": "address" } ], - "name": "onCall", + "name": "getNoMessageIndex", "outputs": [ { - "internalType": "bytes", + "internalType": "string", "name": "", - "type": "bytes" + "type": "string" } ], - "stateMutability": "payable", + "stateMutability": "pure", "type": "function" }, { @@ -195,11 +183,42 @@ "type": "bytes" } ], - "name": "onCrossChainCall", + "name": "onCall", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "internalType": "struct TestDAppV2.MessageContext", + "name": "messageContext", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "message", + "type": "bytes" + } + ], + "name": "onCall", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "function" + }, { "inputs": [ { @@ -215,9 +234,9 @@ "type": "address" }, { - "internalType": "uint64", + "internalType": "uint256", "name": "amount", - "type": "uint64" + "type": "uint256" }, { "internalType": "bytes", @@ -254,19 +273,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [ - { - "internalType": "address", - "name": "_expectedOnCallSender", - "type": "address" - } - ], - "name": "setExpectedOnCallSender", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { diff --git a/pkg/contracts/testdappv2/TestDAppV2.bin b/pkg/contracts/testdappv2/TestDAppV2.bin index 0a7fe69007..b53d34a684 100644 --- a/pkg/contracts/testdappv2/TestDAppV2.bin +++ b/pkg/contracts/testdappv2/TestDAppV2.bin @@ -1 +1 @@ -608060405234801561001057600080fd5b5061150a806100206000396000f3fe6080604052600436106100c65760003560e01c8063a799911f1161007f578063de43156e11610059578063de43156e14610267578063e2842ed714610290578063f592cbfb146102cd578063f936ae851461030a576100cd565b8063a799911f146101f9578063c234fecf14610215578063c7a339a91461023e576100cd565b806336e980a0146100d25780634297a263146100fb57806359f4a777146101385780635ac1e07014610163578063676cc0541461018c5780639291fe26146101bc576100cd565b366100cd57005b600080fd5b3480156100de57600080fd5b506100f960048036038101906100f49190610de7565b610347565b005b34801561010757600080fd5b50610122600480360381019061011d9190610d02565b610371565b60405161012f9190611173565b60405180910390f35b34801561014457600080fd5b5061014d610389565b60405161015a91906110c4565b60405180910390f35b34801561016f57600080fd5b5061018a60048036038101906101859190610e90565b6103ad565b005b6101a660048036038101906101a19190610e30565b6104e7565b6040516101b39190611131565b60405180910390f35b3480156101c857600080fd5b506101e360048036038101906101de9190610de7565b61069c565b6040516101f09190611173565b60405180910390f35b610213600480360381019061020e9190610de7565b6106df565b005b34801561022157600080fd5b5061023c60048036038101906102379190610ca8565b610708565b005b34801561024a57600080fd5b5061026560048036038101906102609190610d78565b61074b565b005b34801561027357600080fd5b5061028e60048036038101906102899190610ed9565b61080e565b005b34801561029c57600080fd5b506102b760048036038101906102b29190610d02565b610907565b6040516102c49190611116565b60405180910390f35b3480156102d957600080fd5b506102f460048036038101906102ef9190610de7565b610927565b6040516103019190611116565b60405180910390f35b34801561031657600080fd5b50610331600480360381019061032c9190610d2f565b610977565b60405161033e91906110c4565b60405180910390f35b610350816109c0565b1561035a57600080fd5b61036381610a16565b61036e816000610a6a565b50565b60036020528060005260406000206000915090505481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6104088180606001906103c0919061118e565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610a16565b61046581806060019061041b919061118e565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506000610a6a565b8060000160208101906104789190610ca8565b600282806060019061048a919061118e565b60405161049892919061107f565b908152602001604051809103902060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b606060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168460000160208101906105339190610ca8565b73ffffffffffffffffffffffffffffffffffffffff1614610589576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161058090611153565b60405180910390fd5b6105d683838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610a16565b61062483838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505034610a6a565b8360000160208101906106379190610ca8565b6002848460405161064992919061107f565b908152602001604051809103902060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509392505050565b600060036000836040516020016106b39190611098565b604051602081830303815290604052805190602001208152602001908152602001600020549050919050565b6106e8816109c0565b156106f257600080fd5b6106fb81610a16565b6107058134610a6a565b50565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b610754816109c0565b1561075e57600080fd5b8273ffffffffffffffffffffffffffffffffffffffff166323b872dd3330856040518463ffffffff1660e01b815260040161079b939291906110df565b602060405180830381600087803b1580156107b557600080fd5b505af11580156107c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107ed9190610cd5565b6107f657600080fd5b6107ff81610a16565b6108098183610a6a565b505050565b61085b82828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506109c0565b1561086557600080fd5b6108b282828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610a16565b61090082828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505084610a6a565b5050505050565b60016020528060005260406000206000915054906101000a900460ff1681565b6000600160008360405160200161093e9190611098565b60405160208183030381529060405280519060200120815260200190815260200160002060009054906101000a900460ff169050919050565b6002818051602081018201805184825260208301602085012081835280955050505050506000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006040516020016109d1906110af565b60405160208183030381529060405280519060200120826040516020016109f89190611098565b60405160208183030381529060405280519060200120149050919050565b600180600083604051602001610a2c9190611098565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a81548160ff02191690831515021790555050565b806003600084604051602001610a809190611098565b604051602081830303815290604052805190602001208152602001908152602001600020819055505050565b6000610abf610aba84611216565b6111f1565b905082815260208101848484011115610adb57610ada6113ef565b5b610ae684828561132a565b509392505050565b6000610b01610afc84611247565b6111f1565b905082815260208101848484011115610b1d57610b1c6113ef565b5b610b2884828561132a565b509392505050565b600081359050610b3f81611461565b92915050565b600081519050610b5481611478565b92915050565b600081359050610b698161148f565b92915050565b60008083601f840112610b8557610b846113d1565b5b8235905067ffffffffffffffff811115610ba257610ba16113cc565b5b602083019150836001820283011115610bbe57610bbd6113e5565b5b9250929050565b600082601f830112610bda57610bd96113d1565b5b8135610bea848260208601610aac565b91505092915050565b600081359050610c02816114a6565b92915050565b600082601f830112610c1d57610c1c6113d1565b5b8135610c2d848260208601610aee565b91505092915050565b600060208284031215610c4c57610c4b6113db565b5b81905092915050565b600060808284031215610c6b57610c6a6113db565b5b81905092915050565b600060608284031215610c8a57610c896113db565b5b81905092915050565b600081359050610ca2816114bd565b92915050565b600060208284031215610cbe57610cbd6113f9565b5b6000610ccc84828501610b30565b91505092915050565b600060208284031215610ceb57610cea6113f9565b5b6000610cf984828501610b45565b91505092915050565b600060208284031215610d1857610d176113f9565b5b6000610d2684828501610b5a565b91505092915050565b600060208284031215610d4557610d446113f9565b5b600082013567ffffffffffffffff811115610d6357610d626113f4565b5b610d6f84828501610bc5565b91505092915050565b600080600060608486031215610d9157610d906113f9565b5b6000610d9f86828701610bf3565b9350506020610db086828701610c93565b925050604084013567ffffffffffffffff811115610dd157610dd06113f4565b5b610ddd86828701610c08565b9150509250925092565b600060208284031215610dfd57610dfc6113f9565b5b600082013567ffffffffffffffff811115610e1b57610e1a6113f4565b5b610e2784828501610c08565b91505092915050565b600080600060408486031215610e4957610e486113f9565b5b6000610e5786828701610c36565b935050602084013567ffffffffffffffff811115610e7857610e776113f4565b5b610e8486828701610b6f565b92509250509250925092565b600060208284031215610ea657610ea56113f9565b5b600082013567ffffffffffffffff811115610ec457610ec36113f4565b5b610ed084828501610c55565b91505092915050565b600080600080600060808688031215610ef557610ef46113f9565b5b600086013567ffffffffffffffff811115610f1357610f126113f4565b5b610f1f88828901610c74565b9550506020610f3088828901610b30565b9450506040610f4188828901610c93565b935050606086013567ffffffffffffffff811115610f6257610f616113f4565b5b610f6e88828901610b6f565b92509250509295509295909350565b610f86816112c6565b82525050565b610f95816112d8565b82525050565b6000610fa7838561129f565b9350610fb483858461132a565b82840190509392505050565b6000610fcb82611278565b610fd5818561128e565b9350610fe5818560208601611339565b610fee816113fe565b840191505092915050565b600061100482611283565b61100e81856112bb565b935061101e818560208601611339565b80840191505092915050565b60006110376016836112aa565b91506110428261140f565b602082019050919050565b600061105a6006836112bb565b915061106582611438565b600682019050919050565b61107981611320565b82525050565b600061108c828486610f9b565b91508190509392505050565b60006110a48284610ff9565b915081905092915050565b60006110ba8261104d565b9150819050919050565b60006020820190506110d96000830184610f7d565b92915050565b60006060820190506110f46000830186610f7d565b6111016020830185610f7d565b61110e6040830184611070565b949350505050565b600060208201905061112b6000830184610f8c565b92915050565b6000602082019050818103600083015261114b8184610fc0565b905092915050565b6000602082019050818103600083015261116c8161102a565b9050919050565b60006020820190506111886000830184611070565b92915050565b600080833560016020038436030381126111ab576111aa6113e0565b5b80840192508235915067ffffffffffffffff8211156111cd576111cc6113d6565b5b6020830192506001820236038313156111e9576111e86113ea565b5b509250929050565b60006111fb61120c565b9050611207828261136c565b919050565b6000604051905090565b600067ffffffffffffffff8211156112315761123061139d565b5b61123a826113fe565b9050602081019050919050565b600067ffffffffffffffff8211156112625761126161139d565b5b61126b826113fe565b9050602081019050919050565b600081519050919050565b600081519050919050565b600082825260208201905092915050565b600081905092915050565b600082825260208201905092915050565b600081905092915050565b60006112d182611300565b9050919050565b60008115159050919050565b6000819050919050565b60006112f9826112c6565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b82818337600083830152505050565b60005b8381101561135757808201518184015260208101905061133c565b83811115611366576000848401525b50505050565b611375826113fe565b810181811067ffffffffffffffff821117156113945761139361139d565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f756e61757468656e746963617465642073656e64657200000000000000000000600082015250565b7f7265766572740000000000000000000000000000000000000000000000000000600082015250565b61146a816112c6565b811461147557600080fd5b50565b611481816112d8565b811461148c57600080fd5b50565b611498816112e4565b81146114a357600080fd5b50565b6114af816112ee565b81146114ba57600080fd5b50565b6114c681611320565b81146114d157600080fd5b5056fea2646970667358221220e090c5cd81361f8bba5d13d4fb801ab148714dad774e12a4acf812e341dc93c564736f6c63430008070033 +6080604052348015600f57600080fd5b506115288061001f6000396000f3fe6080604052600436106100c65760003560e01c8063ad23b28b1161007f578063c9028a3611610059578063c9028a361461027b578063e2842ed7146102a4578063f592cbfb146102e1578063f936ae851461031e576100cd565b8063ad23b28b146101ea578063c7a339a914610227578063c85f843414610250576100cd565b806336e980a0146100d25780634297a263146100fb5780635bcfd61614610138578063676cc054146101615780639291fe2614610191578063a799911f146101ce576100cd565b366100cd57005b600080fd5b3480156100de57600080fd5b506100f960048036038101906100f49190610b86565b61035b565b005b34801561010757600080fd5b50610122600480360381019061011d9190610c05565b610385565b60405161012f9190610c4b565b60405180910390f35b34801561014457600080fd5b5061015f600480360381019061015a9190610d74565b61039d565b005b61017b60048036038101906101769190610e37565b610483565b6040516101889190610f16565b60405180910390f35b34801561019d57600080fd5b506101b860048036038101906101b39190610b86565b610595565b6040516101c59190610c4b565b60405180910390f35b6101e860048036038101906101e39190610b86565b6105d8565b005b3480156101f657600080fd5b50610211600480360381019061020c9190610f38565b610601565b60405161021e9190610fba565b60405180910390f35b34801561023357600080fd5b5061024e6004803603810190610249919061101a565b610661565b005b34801561025c57600080fd5b50610265610715565b6040516102729190610fba565b60405180910390f35b34801561028757600080fd5b506102a2600480360381019061029d91906110a8565b61074e565b005b3480156102b057600080fd5b506102cb60048036038101906102c69190610c05565b610888565b6040516102d8919061110c565b60405180910390f35b3480156102ed57600080fd5b5061030860048036038101906103039190610b86565b6108a8565b604051610315919061110c565b60405180910390f35b34801561032a57600080fd5b50610345600480360381019061034091906111c8565b6108f7565b6040516103529190611220565b60405180910390f35b61036481610940565b1561036e57600080fd5b61037781610996565b6103828160006109ea565b50565b60026020528060005260406000206000915090505481565b6103ea82828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610940565b156103f457600080fd5b600080838390501461044a5782828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610466565b6104658660200160208101906104609190610f38565b610601565b5b905061047181610996565b61047b81856109ea565b505050505050565b606060008084849050146104db5783838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506104f7565b6104f68560000160208101906104f19190610f38565b610601565b5b905061050281610996565b61050c81346109ea565b84600001602081019061051f9190610f38565b60018260405161052f9190611277565b908152602001604051809103902060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550604051806020016040528060008152509150509392505050565b600060026000836040516020016105ac91906112ca565b604051602081830303815290604052805190602001208152602001908152602001600020549050919050565b6105e181610940565b156105eb57600080fd5b6105f481610996565b6105fe81346109ea565b50565b60606040518060400160405280601681526020017f63616c6c65642077697468206e6f206d657373616765000000000000000000008152508260405160200161064b929190611329565b6040516020818303038152906040529050919050565b61066a81610940565b1561067457600080fd5b8273ffffffffffffffffffffffffffffffffffffffff166323b872dd3330856040518463ffffffff1660e01b81526004016106b193929190611351565b6020604051808303816000875af11580156106d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106f491906113b4565b6106fd57600080fd5b61070681610996565b61071081836109ea565b505050565b6040518060400160405280601681526020017f63616c6c65642077697468206e6f206d6573736167650000000000000000000081525081565b6107a981806060019061076191906113f0565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610996565b6108068180606001906107bc91906113f0565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505060006109ea565b8060000160208101906108199190610f38565b600182806060019061082b91906113f0565b604051610839929190611478565b908152602001604051809103902060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60006020528060005260406000206000915054906101000a900460ff1681565b6000806000836040516020016108be91906112ca565b60405160208183030381529060405280519060200120815260200190815260200160002060009054906101000a900460ff169050919050565b6001818051602081018201805184825260208301602085012081835280955050505050506000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000604051602001610951906114dd565b604051602081830303815290604052805190602001208260405160200161097891906112ca565b60405160208183030381529060405280519060200120149050919050565b6001600080836040516020016109ac91906112ca565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a81548160ff02191690831515021790555050565b806002600084604051602001610a0091906112ca565b604051602081830303815290604052805190602001208152602001908152602001600020819055505050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610a9382610a4a565b810181811067ffffffffffffffff82111715610ab257610ab1610a5b565b5b80604052505050565b6000610ac5610a2c565b9050610ad18282610a8a565b919050565b600067ffffffffffffffff821115610af157610af0610a5b565b5b610afa82610a4a565b9050602081019050919050565b82818337600083830152505050565b6000610b29610b2484610ad6565b610abb565b905082815260208101848484011115610b4557610b44610a45565b5b610b50848285610b07565b509392505050565b600082601f830112610b6d57610b6c610a40565b5b8135610b7d848260208601610b16565b91505092915050565b600060208284031215610b9c57610b9b610a36565b5b600082013567ffffffffffffffff811115610bba57610bb9610a3b565b5b610bc684828501610b58565b91505092915050565b6000819050919050565b610be281610bcf565b8114610bed57600080fd5b50565b600081359050610bff81610bd9565b92915050565b600060208284031215610c1b57610c1a610a36565b5b6000610c2984828501610bf0565b91505092915050565b6000819050919050565b610c4581610c32565b82525050565b6000602082019050610c606000830184610c3c565b92915050565b600080fd5b600060608284031215610c8157610c80610c66565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610cb582610c8a565b9050919050565b610cc581610caa565b8114610cd057600080fd5b50565b600081359050610ce281610cbc565b92915050565b610cf181610c32565b8114610cfc57600080fd5b50565b600081359050610d0e81610ce8565b92915050565b600080fd5b600080fd5b60008083601f840112610d3457610d33610a40565b5b8235905067ffffffffffffffff811115610d5157610d50610d14565b5b602083019150836001820283011115610d6d57610d6c610d19565b5b9250929050565b600080600080600060808688031215610d9057610d8f610a36565b5b600086013567ffffffffffffffff811115610dae57610dad610a3b565b5b610dba88828901610c6b565b9550506020610dcb88828901610cd3565b9450506040610ddc88828901610cff565b935050606086013567ffffffffffffffff811115610dfd57610dfc610a3b565b5b610e0988828901610d1e565b92509250509295509295909350565b600060208284031215610e2e57610e2d610c66565b5b81905092915050565b600080600060408486031215610e5057610e4f610a36565b5b6000610e5e86828701610e18565b935050602084013567ffffffffffffffff811115610e7f57610e7e610a3b565b5b610e8b86828701610d1e565b92509250509250925092565b600081519050919050565b600082825260208201905092915050565b60005b83811015610ed1578082015181840152602081019050610eb6565b60008484015250505050565b6000610ee882610e97565b610ef28185610ea2565b9350610f02818560208601610eb3565b610f0b81610a4a565b840191505092915050565b60006020820190508181036000830152610f308184610edd565b905092915050565b600060208284031215610f4e57610f4d610a36565b5b6000610f5c84828501610cd3565b91505092915050565b600081519050919050565b600082825260208201905092915050565b6000610f8c82610f65565b610f968185610f70565b9350610fa6818560208601610eb3565b610faf81610a4a565b840191505092915050565b60006020820190508181036000830152610fd48184610f81565b905092915050565b6000610fe782610caa565b9050919050565b610ff781610fdc565b811461100257600080fd5b50565b60008135905061101481610fee565b92915050565b60008060006060848603121561103357611032610a36565b5b600061104186828701611005565b935050602061105286828701610cff565b925050604084013567ffffffffffffffff81111561107357611072610a3b565b5b61107f86828701610b58565b9150509250925092565b60006080828403121561109f5761109e610c66565b5b81905092915050565b6000602082840312156110be576110bd610a36565b5b600082013567ffffffffffffffff8111156110dc576110db610a3b565b5b6110e884828501611089565b91505092915050565b60008115159050919050565b611106816110f1565b82525050565b600060208201905061112160008301846110fd565b92915050565b600067ffffffffffffffff82111561114257611141610a5b565b5b61114b82610a4a565b9050602081019050919050565b600061116b61116684611127565b610abb565b90508281526020810184848401111561118757611186610a45565b5b611192848285610b07565b509392505050565b600082601f8301126111af576111ae610a40565b5b81356111bf848260208601611158565b91505092915050565b6000602082840312156111de576111dd610a36565b5b600082013567ffffffffffffffff8111156111fc576111fb610a3b565b5b6112088482850161119a565b91505092915050565b61121a81610caa565b82525050565b60006020820190506112356000830184611211565b92915050565b600081905092915050565b600061125182610e97565b61125b818561123b565b935061126b818560208601610eb3565b80840191505092915050565b60006112838284611246565b915081905092915050565b600081905092915050565b60006112a482610f65565b6112ae818561128e565b93506112be818560208601610eb3565b80840191505092915050565b60006112d68284611299565b915081905092915050565b60008160601b9050919050565b60006112f9826112e1565b9050919050565b600061130b826112ee565b9050919050565b61132361131e82610caa565b611300565b82525050565b60006113358285611299565b91506113418284611312565b6014820191508190509392505050565b60006060820190506113666000830186611211565b6113736020830185611211565b6113806040830184610c3c565b949350505050565b611391816110f1565b811461139c57600080fd5b50565b6000815190506113ae81611388565b92915050565b6000602082840312156113ca576113c9610a36565b5b60006113d88482850161139f565b91505092915050565b600080fd5b600080fd5b600080fd5b6000808335600160200384360303811261140d5761140c6113e1565b5b80840192508235915067ffffffffffffffff82111561142f5761142e6113e6565b5b60208301925060018202360383131561144b5761144a6113eb565b5b509250929050565b600061145f838561123b565b935061146c838584610b07565b82840190509392505050565b6000611485828486611453565b91508190509392505050565b7f7265766572740000000000000000000000000000000000000000000000000000600082015250565b60006114c760068361128e565b91506114d282611491565b600682019050919050565b60006114e8826114ba565b915081905091905056fea26469706673582212205747e512af435680fec20f1d2e088f93d6c12052fee7e353f45e5e49a671377b64736f6c634300081a0033 diff --git a/pkg/contracts/testdappv2/TestDAppV2.go b/pkg/contracts/testdappv2/TestDAppV2.go index 50e4bbcecb..7414aaccf2 100644 --- a/pkg/contracts/testdappv2/TestDAppV2.go +++ b/pkg/contracts/testdappv2/TestDAppV2.go @@ -38,7 +38,7 @@ type TestDAppV2MessageContext struct { type TestDAppV2RevertContext struct { Sender common.Address Asset common.Address - Amount uint64 + Amount *big.Int RevertMessage []byte } @@ -51,8 +51,8 @@ type TestDAppV2zContext struct { // TestDAppV2MetaData contains all meta data concerning the TestDAppV2 contract. var TestDAppV2MetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"amountWithMessage\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"calledWithMessage\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contractIERC20\",\"name\":\"erc20\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"erc20Call\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"expectedOnCallSender\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"gasCall\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"getAmountWithMessage\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"getCalledWithMessage\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"internalType\":\"structTestDAppV2.MessageContext\",\"name\":\"messageContext\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"name\":\"onCall\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"origin\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainID\",\"type\":\"uint256\"}],\"internalType\":\"structTestDAppV2.zContext\",\"name\":\"_context\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"_zrc20\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"name\":\"onCrossChainCall\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"amount\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"revertMessage\",\"type\":\"bytes\"}],\"internalType\":\"structTestDAppV2.RevertContext\",\"name\":\"revertContext\",\"type\":\"tuple\"}],\"name\":\"onRevert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"senderWithMessage\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_expectedOnCallSender\",\"type\":\"address\"}],\"name\":\"setExpectedOnCallSender\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"simpleCall\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", - Bin: "0x608060405234801561001057600080fd5b5061150a806100206000396000f3fe6080604052600436106100c65760003560e01c8063a799911f1161007f578063de43156e11610059578063de43156e14610267578063e2842ed714610290578063f592cbfb146102cd578063f936ae851461030a576100cd565b8063a799911f146101f9578063c234fecf14610215578063c7a339a91461023e576100cd565b806336e980a0146100d25780634297a263146100fb57806359f4a777146101385780635ac1e07014610163578063676cc0541461018c5780639291fe26146101bc576100cd565b366100cd57005b600080fd5b3480156100de57600080fd5b506100f960048036038101906100f49190610de7565b610347565b005b34801561010757600080fd5b50610122600480360381019061011d9190610d02565b610371565b60405161012f9190611173565b60405180910390f35b34801561014457600080fd5b5061014d610389565b60405161015a91906110c4565b60405180910390f35b34801561016f57600080fd5b5061018a60048036038101906101859190610e90565b6103ad565b005b6101a660048036038101906101a19190610e30565b6104e7565b6040516101b39190611131565b60405180910390f35b3480156101c857600080fd5b506101e360048036038101906101de9190610de7565b61069c565b6040516101f09190611173565b60405180910390f35b610213600480360381019061020e9190610de7565b6106df565b005b34801561022157600080fd5b5061023c60048036038101906102379190610ca8565b610708565b005b34801561024a57600080fd5b5061026560048036038101906102609190610d78565b61074b565b005b34801561027357600080fd5b5061028e60048036038101906102899190610ed9565b61080e565b005b34801561029c57600080fd5b506102b760048036038101906102b29190610d02565b610907565b6040516102c49190611116565b60405180910390f35b3480156102d957600080fd5b506102f460048036038101906102ef9190610de7565b610927565b6040516103019190611116565b60405180910390f35b34801561031657600080fd5b50610331600480360381019061032c9190610d2f565b610977565b60405161033e91906110c4565b60405180910390f35b610350816109c0565b1561035a57600080fd5b61036381610a16565b61036e816000610a6a565b50565b60036020528060005260406000206000915090505481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6104088180606001906103c0919061118e565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610a16565b61046581806060019061041b919061118e565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506000610a6a565b8060000160208101906104789190610ca8565b600282806060019061048a919061118e565b60405161049892919061107f565b908152602001604051809103902060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b606060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168460000160208101906105339190610ca8565b73ffffffffffffffffffffffffffffffffffffffff1614610589576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161058090611153565b60405180910390fd5b6105d683838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610a16565b61062483838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505034610a6a565b8360000160208101906106379190610ca8565b6002848460405161064992919061107f565b908152602001604051809103902060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509392505050565b600060036000836040516020016106b39190611098565b604051602081830303815290604052805190602001208152602001908152602001600020549050919050565b6106e8816109c0565b156106f257600080fd5b6106fb81610a16565b6107058134610a6a565b50565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b610754816109c0565b1561075e57600080fd5b8273ffffffffffffffffffffffffffffffffffffffff166323b872dd3330856040518463ffffffff1660e01b815260040161079b939291906110df565b602060405180830381600087803b1580156107b557600080fd5b505af11580156107c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107ed9190610cd5565b6107f657600080fd5b6107ff81610a16565b6108098183610a6a565b505050565b61085b82828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506109c0565b1561086557600080fd5b6108b282828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610a16565b61090082828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505084610a6a565b5050505050565b60016020528060005260406000206000915054906101000a900460ff1681565b6000600160008360405160200161093e9190611098565b60405160208183030381529060405280519060200120815260200190815260200160002060009054906101000a900460ff169050919050565b6002818051602081018201805184825260208301602085012081835280955050505050506000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006040516020016109d1906110af565b60405160208183030381529060405280519060200120826040516020016109f89190611098565b60405160208183030381529060405280519060200120149050919050565b600180600083604051602001610a2c9190611098565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a81548160ff02191690831515021790555050565b806003600084604051602001610a809190611098565b604051602081830303815290604052805190602001208152602001908152602001600020819055505050565b6000610abf610aba84611216565b6111f1565b905082815260208101848484011115610adb57610ada6113ef565b5b610ae684828561132a565b509392505050565b6000610b01610afc84611247565b6111f1565b905082815260208101848484011115610b1d57610b1c6113ef565b5b610b2884828561132a565b509392505050565b600081359050610b3f81611461565b92915050565b600081519050610b5481611478565b92915050565b600081359050610b698161148f565b92915050565b60008083601f840112610b8557610b846113d1565b5b8235905067ffffffffffffffff811115610ba257610ba16113cc565b5b602083019150836001820283011115610bbe57610bbd6113e5565b5b9250929050565b600082601f830112610bda57610bd96113d1565b5b8135610bea848260208601610aac565b91505092915050565b600081359050610c02816114a6565b92915050565b600082601f830112610c1d57610c1c6113d1565b5b8135610c2d848260208601610aee565b91505092915050565b600060208284031215610c4c57610c4b6113db565b5b81905092915050565b600060808284031215610c6b57610c6a6113db565b5b81905092915050565b600060608284031215610c8a57610c896113db565b5b81905092915050565b600081359050610ca2816114bd565b92915050565b600060208284031215610cbe57610cbd6113f9565b5b6000610ccc84828501610b30565b91505092915050565b600060208284031215610ceb57610cea6113f9565b5b6000610cf984828501610b45565b91505092915050565b600060208284031215610d1857610d176113f9565b5b6000610d2684828501610b5a565b91505092915050565b600060208284031215610d4557610d446113f9565b5b600082013567ffffffffffffffff811115610d6357610d626113f4565b5b610d6f84828501610bc5565b91505092915050565b600080600060608486031215610d9157610d906113f9565b5b6000610d9f86828701610bf3565b9350506020610db086828701610c93565b925050604084013567ffffffffffffffff811115610dd157610dd06113f4565b5b610ddd86828701610c08565b9150509250925092565b600060208284031215610dfd57610dfc6113f9565b5b600082013567ffffffffffffffff811115610e1b57610e1a6113f4565b5b610e2784828501610c08565b91505092915050565b600080600060408486031215610e4957610e486113f9565b5b6000610e5786828701610c36565b935050602084013567ffffffffffffffff811115610e7857610e776113f4565b5b610e8486828701610b6f565b92509250509250925092565b600060208284031215610ea657610ea56113f9565b5b600082013567ffffffffffffffff811115610ec457610ec36113f4565b5b610ed084828501610c55565b91505092915050565b600080600080600060808688031215610ef557610ef46113f9565b5b600086013567ffffffffffffffff811115610f1357610f126113f4565b5b610f1f88828901610c74565b9550506020610f3088828901610b30565b9450506040610f4188828901610c93565b935050606086013567ffffffffffffffff811115610f6257610f616113f4565b5b610f6e88828901610b6f565b92509250509295509295909350565b610f86816112c6565b82525050565b610f95816112d8565b82525050565b6000610fa7838561129f565b9350610fb483858461132a565b82840190509392505050565b6000610fcb82611278565b610fd5818561128e565b9350610fe5818560208601611339565b610fee816113fe565b840191505092915050565b600061100482611283565b61100e81856112bb565b935061101e818560208601611339565b80840191505092915050565b60006110376016836112aa565b91506110428261140f565b602082019050919050565b600061105a6006836112bb565b915061106582611438565b600682019050919050565b61107981611320565b82525050565b600061108c828486610f9b565b91508190509392505050565b60006110a48284610ff9565b915081905092915050565b60006110ba8261104d565b9150819050919050565b60006020820190506110d96000830184610f7d565b92915050565b60006060820190506110f46000830186610f7d565b6111016020830185610f7d565b61110e6040830184611070565b949350505050565b600060208201905061112b6000830184610f8c565b92915050565b6000602082019050818103600083015261114b8184610fc0565b905092915050565b6000602082019050818103600083015261116c8161102a565b9050919050565b60006020820190506111886000830184611070565b92915050565b600080833560016020038436030381126111ab576111aa6113e0565b5b80840192508235915067ffffffffffffffff8211156111cd576111cc6113d6565b5b6020830192506001820236038313156111e9576111e86113ea565b5b509250929050565b60006111fb61120c565b9050611207828261136c565b919050565b6000604051905090565b600067ffffffffffffffff8211156112315761123061139d565b5b61123a826113fe565b9050602081019050919050565b600067ffffffffffffffff8211156112625761126161139d565b5b61126b826113fe565b9050602081019050919050565b600081519050919050565b600081519050919050565b600082825260208201905092915050565b600081905092915050565b600082825260208201905092915050565b600081905092915050565b60006112d182611300565b9050919050565b60008115159050919050565b6000819050919050565b60006112f9826112c6565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b82818337600083830152505050565b60005b8381101561135757808201518184015260208101905061133c565b83811115611366576000848401525b50505050565b611375826113fe565b810181811067ffffffffffffffff821117156113945761139361139d565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f756e61757468656e746963617465642073656e64657200000000000000000000600082015250565b7f7265766572740000000000000000000000000000000000000000000000000000600082015250565b61146a816112c6565b811461147557600080fd5b50565b611481816112d8565b811461148c57600080fd5b50565b611498816112e4565b81146114a357600080fd5b50565b6114af816112ee565b81146114ba57600080fd5b50565b6114c681611320565b81146114d157600080fd5b5056fea2646970667358221220e090c5cd81361f8bba5d13d4fb801ab148714dad774e12a4acf812e341dc93c564736f6c63430008070033", + ABI: "[{\"inputs\":[],\"name\":\"NO_MESSAGE_CALL\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"amountWithMessage\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"calledWithMessage\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contractIERC20\",\"name\":\"erc20\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"erc20Call\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"gasCall\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"getAmountWithMessage\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"getCalledWithMessage\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"getNoMessageIndex\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"origin\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainID\",\"type\":\"uint256\"}],\"internalType\":\"structTestDAppV2.zContext\",\"name\":\"_context\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"_zrc20\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"name\":\"onCall\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"internalType\":\"structTestDAppV2.MessageContext\",\"name\":\"messageContext\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"name\":\"onCall\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"revertMessage\",\"type\":\"bytes\"}],\"internalType\":\"structTestDAppV2.RevertContext\",\"name\":\"revertContext\",\"type\":\"tuple\"}],\"name\":\"onRevert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"senderWithMessage\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"simpleCall\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", + Bin: "0x6080604052348015600f57600080fd5b506115288061001f6000396000f3fe6080604052600436106100c65760003560e01c8063ad23b28b1161007f578063c9028a3611610059578063c9028a361461027b578063e2842ed7146102a4578063f592cbfb146102e1578063f936ae851461031e576100cd565b8063ad23b28b146101ea578063c7a339a914610227578063c85f843414610250576100cd565b806336e980a0146100d25780634297a263146100fb5780635bcfd61614610138578063676cc054146101615780639291fe2614610191578063a799911f146101ce576100cd565b366100cd57005b600080fd5b3480156100de57600080fd5b506100f960048036038101906100f49190610b86565b61035b565b005b34801561010757600080fd5b50610122600480360381019061011d9190610c05565b610385565b60405161012f9190610c4b565b60405180910390f35b34801561014457600080fd5b5061015f600480360381019061015a9190610d74565b61039d565b005b61017b60048036038101906101769190610e37565b610483565b6040516101889190610f16565b60405180910390f35b34801561019d57600080fd5b506101b860048036038101906101b39190610b86565b610595565b6040516101c59190610c4b565b60405180910390f35b6101e860048036038101906101e39190610b86565b6105d8565b005b3480156101f657600080fd5b50610211600480360381019061020c9190610f38565b610601565b60405161021e9190610fba565b60405180910390f35b34801561023357600080fd5b5061024e6004803603810190610249919061101a565b610661565b005b34801561025c57600080fd5b50610265610715565b6040516102729190610fba565b60405180910390f35b34801561028757600080fd5b506102a2600480360381019061029d91906110a8565b61074e565b005b3480156102b057600080fd5b506102cb60048036038101906102c69190610c05565b610888565b6040516102d8919061110c565b60405180910390f35b3480156102ed57600080fd5b5061030860048036038101906103039190610b86565b6108a8565b604051610315919061110c565b60405180910390f35b34801561032a57600080fd5b50610345600480360381019061034091906111c8565b6108f7565b6040516103529190611220565b60405180910390f35b61036481610940565b1561036e57600080fd5b61037781610996565b6103828160006109ea565b50565b60026020528060005260406000206000915090505481565b6103ea82828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610940565b156103f457600080fd5b600080838390501461044a5782828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610466565b6104658660200160208101906104609190610f38565b610601565b5b905061047181610996565b61047b81856109ea565b505050505050565b606060008084849050146104db5783838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506104f7565b6104f68560000160208101906104f19190610f38565b610601565b5b905061050281610996565b61050c81346109ea565b84600001602081019061051f9190610f38565b60018260405161052f9190611277565b908152602001604051809103902060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550604051806020016040528060008152509150509392505050565b600060026000836040516020016105ac91906112ca565b604051602081830303815290604052805190602001208152602001908152602001600020549050919050565b6105e181610940565b156105eb57600080fd5b6105f481610996565b6105fe81346109ea565b50565b60606040518060400160405280601681526020017f63616c6c65642077697468206e6f206d657373616765000000000000000000008152508260405160200161064b929190611329565b6040516020818303038152906040529050919050565b61066a81610940565b1561067457600080fd5b8273ffffffffffffffffffffffffffffffffffffffff166323b872dd3330856040518463ffffffff1660e01b81526004016106b193929190611351565b6020604051808303816000875af11580156106d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106f491906113b4565b6106fd57600080fd5b61070681610996565b61071081836109ea565b505050565b6040518060400160405280601681526020017f63616c6c65642077697468206e6f206d6573736167650000000000000000000081525081565b6107a981806060019061076191906113f0565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610996565b6108068180606001906107bc91906113f0565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505060006109ea565b8060000160208101906108199190610f38565b600182806060019061082b91906113f0565b604051610839929190611478565b908152602001604051809103902060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60006020528060005260406000206000915054906101000a900460ff1681565b6000806000836040516020016108be91906112ca565b60405160208183030381529060405280519060200120815260200190815260200160002060009054906101000a900460ff169050919050565b6001818051602081018201805184825260208301602085012081835280955050505050506000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000604051602001610951906114dd565b604051602081830303815290604052805190602001208260405160200161097891906112ca565b60405160208183030381529060405280519060200120149050919050565b6001600080836040516020016109ac91906112ca565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a81548160ff02191690831515021790555050565b806002600084604051602001610a0091906112ca565b604051602081830303815290604052805190602001208152602001908152602001600020819055505050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610a9382610a4a565b810181811067ffffffffffffffff82111715610ab257610ab1610a5b565b5b80604052505050565b6000610ac5610a2c565b9050610ad18282610a8a565b919050565b600067ffffffffffffffff821115610af157610af0610a5b565b5b610afa82610a4a565b9050602081019050919050565b82818337600083830152505050565b6000610b29610b2484610ad6565b610abb565b905082815260208101848484011115610b4557610b44610a45565b5b610b50848285610b07565b509392505050565b600082601f830112610b6d57610b6c610a40565b5b8135610b7d848260208601610b16565b91505092915050565b600060208284031215610b9c57610b9b610a36565b5b600082013567ffffffffffffffff811115610bba57610bb9610a3b565b5b610bc684828501610b58565b91505092915050565b6000819050919050565b610be281610bcf565b8114610bed57600080fd5b50565b600081359050610bff81610bd9565b92915050565b600060208284031215610c1b57610c1a610a36565b5b6000610c2984828501610bf0565b91505092915050565b6000819050919050565b610c4581610c32565b82525050565b6000602082019050610c606000830184610c3c565b92915050565b600080fd5b600060608284031215610c8157610c80610c66565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610cb582610c8a565b9050919050565b610cc581610caa565b8114610cd057600080fd5b50565b600081359050610ce281610cbc565b92915050565b610cf181610c32565b8114610cfc57600080fd5b50565b600081359050610d0e81610ce8565b92915050565b600080fd5b600080fd5b60008083601f840112610d3457610d33610a40565b5b8235905067ffffffffffffffff811115610d5157610d50610d14565b5b602083019150836001820283011115610d6d57610d6c610d19565b5b9250929050565b600080600080600060808688031215610d9057610d8f610a36565b5b600086013567ffffffffffffffff811115610dae57610dad610a3b565b5b610dba88828901610c6b565b9550506020610dcb88828901610cd3565b9450506040610ddc88828901610cff565b935050606086013567ffffffffffffffff811115610dfd57610dfc610a3b565b5b610e0988828901610d1e565b92509250509295509295909350565b600060208284031215610e2e57610e2d610c66565b5b81905092915050565b600080600060408486031215610e5057610e4f610a36565b5b6000610e5e86828701610e18565b935050602084013567ffffffffffffffff811115610e7f57610e7e610a3b565b5b610e8b86828701610d1e565b92509250509250925092565b600081519050919050565b600082825260208201905092915050565b60005b83811015610ed1578082015181840152602081019050610eb6565b60008484015250505050565b6000610ee882610e97565b610ef28185610ea2565b9350610f02818560208601610eb3565b610f0b81610a4a565b840191505092915050565b60006020820190508181036000830152610f308184610edd565b905092915050565b600060208284031215610f4e57610f4d610a36565b5b6000610f5c84828501610cd3565b91505092915050565b600081519050919050565b600082825260208201905092915050565b6000610f8c82610f65565b610f968185610f70565b9350610fa6818560208601610eb3565b610faf81610a4a565b840191505092915050565b60006020820190508181036000830152610fd48184610f81565b905092915050565b6000610fe782610caa565b9050919050565b610ff781610fdc565b811461100257600080fd5b50565b60008135905061101481610fee565b92915050565b60008060006060848603121561103357611032610a36565b5b600061104186828701611005565b935050602061105286828701610cff565b925050604084013567ffffffffffffffff81111561107357611072610a3b565b5b61107f86828701610b58565b9150509250925092565b60006080828403121561109f5761109e610c66565b5b81905092915050565b6000602082840312156110be576110bd610a36565b5b600082013567ffffffffffffffff8111156110dc576110db610a3b565b5b6110e884828501611089565b91505092915050565b60008115159050919050565b611106816110f1565b82525050565b600060208201905061112160008301846110fd565b92915050565b600067ffffffffffffffff82111561114257611141610a5b565b5b61114b82610a4a565b9050602081019050919050565b600061116b61116684611127565b610abb565b90508281526020810184848401111561118757611186610a45565b5b611192848285610b07565b509392505050565b600082601f8301126111af576111ae610a40565b5b81356111bf848260208601611158565b91505092915050565b6000602082840312156111de576111dd610a36565b5b600082013567ffffffffffffffff8111156111fc576111fb610a3b565b5b6112088482850161119a565b91505092915050565b61121a81610caa565b82525050565b60006020820190506112356000830184611211565b92915050565b600081905092915050565b600061125182610e97565b61125b818561123b565b935061126b818560208601610eb3565b80840191505092915050565b60006112838284611246565b915081905092915050565b600081905092915050565b60006112a482610f65565b6112ae818561128e565b93506112be818560208601610eb3565b80840191505092915050565b60006112d68284611299565b915081905092915050565b60008160601b9050919050565b60006112f9826112e1565b9050919050565b600061130b826112ee565b9050919050565b61132361131e82610caa565b611300565b82525050565b60006113358285611299565b91506113418284611312565b6014820191508190509392505050565b60006060820190506113666000830186611211565b6113736020830185611211565b6113806040830184610c3c565b949350505050565b611391816110f1565b811461139c57600080fd5b50565b6000815190506113ae81611388565b92915050565b6000602082840312156113ca576113c9610a36565b5b60006113d88482850161139f565b91505092915050565b600080fd5b600080fd5b600080fd5b6000808335600160200384360303811261140d5761140c6113e1565b5b80840192508235915067ffffffffffffffff82111561142f5761142e6113e6565b5b60208301925060018202360383131561144b5761144a6113eb565b5b509250929050565b600061145f838561123b565b935061146c838584610b07565b82840190509392505050565b6000611485828486611453565b91508190509392505050565b7f7265766572740000000000000000000000000000000000000000000000000000600082015250565b60006114c760068361128e565b91506114d282611491565b600682019050919050565b60006114e8826114ba565b915081905091905056fea26469706673582212205747e512af435680fec20f1d2e088f93d6c12052fee7e353f45e5e49a671377b64736f6c634300081a0033", } // TestDAppV2ABI is the input ABI used to generate the binding from. @@ -222,6 +222,37 @@ func (_TestDAppV2 *TestDAppV2TransactorRaw) Transact(opts *bind.TransactOpts, me return _TestDAppV2.Contract.contract.Transact(opts, method, params...) } +// NOMESSAGECALL is a free data retrieval call binding the contract method 0xc85f8434. +// +// Solidity: function NO_MESSAGE_CALL() view returns(string) +func (_TestDAppV2 *TestDAppV2Caller) NOMESSAGECALL(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _TestDAppV2.contract.Call(opts, &out, "NO_MESSAGE_CALL") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// NOMESSAGECALL is a free data retrieval call binding the contract method 0xc85f8434. +// +// Solidity: function NO_MESSAGE_CALL() view returns(string) +func (_TestDAppV2 *TestDAppV2Session) NOMESSAGECALL() (string, error) { + return _TestDAppV2.Contract.NOMESSAGECALL(&_TestDAppV2.CallOpts) +} + +// NOMESSAGECALL is a free data retrieval call binding the contract method 0xc85f8434. +// +// Solidity: function NO_MESSAGE_CALL() view returns(string) +func (_TestDAppV2 *TestDAppV2CallerSession) NOMESSAGECALL() (string, error) { + return _TestDAppV2.Contract.NOMESSAGECALL(&_TestDAppV2.CallOpts) +} + // AmountWithMessage is a free data retrieval call binding the contract method 0x4297a263. // // Solidity: function amountWithMessage(bytes32 ) view returns(uint256) @@ -284,37 +315,6 @@ func (_TestDAppV2 *TestDAppV2CallerSession) CalledWithMessage(arg0 [32]byte) (bo return _TestDAppV2.Contract.CalledWithMessage(&_TestDAppV2.CallOpts, arg0) } -// ExpectedOnCallSender is a free data retrieval call binding the contract method 0x59f4a777. -// -// Solidity: function expectedOnCallSender() view returns(address) -func (_TestDAppV2 *TestDAppV2Caller) ExpectedOnCallSender(opts *bind.CallOpts) (common.Address, error) { - var out []interface{} - err := _TestDAppV2.contract.Call(opts, &out, "expectedOnCallSender") - - if err != nil { - return *new(common.Address), err - } - - out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) - - return out0, err - -} - -// ExpectedOnCallSender is a free data retrieval call binding the contract method 0x59f4a777. -// -// Solidity: function expectedOnCallSender() view returns(address) -func (_TestDAppV2 *TestDAppV2Session) ExpectedOnCallSender() (common.Address, error) { - return _TestDAppV2.Contract.ExpectedOnCallSender(&_TestDAppV2.CallOpts) -} - -// ExpectedOnCallSender is a free data retrieval call binding the contract method 0x59f4a777. -// -// Solidity: function expectedOnCallSender() view returns(address) -func (_TestDAppV2 *TestDAppV2CallerSession) ExpectedOnCallSender() (common.Address, error) { - return _TestDAppV2.Contract.ExpectedOnCallSender(&_TestDAppV2.CallOpts) -} - // GetAmountWithMessage is a free data retrieval call binding the contract method 0x9291fe26. // // Solidity: function getAmountWithMessage(string message) view returns(uint256) @@ -377,6 +377,37 @@ func (_TestDAppV2 *TestDAppV2CallerSession) GetCalledWithMessage(message string) return _TestDAppV2.Contract.GetCalledWithMessage(&_TestDAppV2.CallOpts, message) } +// GetNoMessageIndex is a free data retrieval call binding the contract method 0xad23b28b. +// +// Solidity: function getNoMessageIndex(address sender) pure returns(string) +func (_TestDAppV2 *TestDAppV2Caller) GetNoMessageIndex(opts *bind.CallOpts, sender common.Address) (string, error) { + var out []interface{} + err := _TestDAppV2.contract.Call(opts, &out, "getNoMessageIndex", sender) + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// GetNoMessageIndex is a free data retrieval call binding the contract method 0xad23b28b. +// +// Solidity: function getNoMessageIndex(address sender) pure returns(string) +func (_TestDAppV2 *TestDAppV2Session) GetNoMessageIndex(sender common.Address) (string, error) { + return _TestDAppV2.Contract.GetNoMessageIndex(&_TestDAppV2.CallOpts, sender) +} + +// GetNoMessageIndex is a free data retrieval call binding the contract method 0xad23b28b. +// +// Solidity: function getNoMessageIndex(address sender) pure returns(string) +func (_TestDAppV2 *TestDAppV2CallerSession) GetNoMessageIndex(sender common.Address) (string, error) { + return _TestDAppV2.Contract.GetNoMessageIndex(&_TestDAppV2.CallOpts, sender) +} + // SenderWithMessage is a free data retrieval call binding the contract method 0xf936ae85. // // Solidity: function senderWithMessage(bytes ) view returns(address) @@ -450,90 +481,69 @@ func (_TestDAppV2 *TestDAppV2TransactorSession) GasCall(message string) (*types. return _TestDAppV2.Contract.GasCall(&_TestDAppV2.TransactOpts, message) } -// OnCall is a paid mutator transaction binding the contract method 0x676cc054. +// OnCall is a paid mutator transaction binding the contract method 0x5bcfd616. // -// Solidity: function onCall((address) messageContext, bytes message) payable returns(bytes) -func (_TestDAppV2 *TestDAppV2Transactor) OnCall(opts *bind.TransactOpts, messageContext TestDAppV2MessageContext, message []byte) (*types.Transaction, error) { - return _TestDAppV2.contract.Transact(opts, "onCall", messageContext, message) +// Solidity: function onCall((bytes,address,uint256) _context, address _zrc20, uint256 amount, bytes message) returns() +func (_TestDAppV2 *TestDAppV2Transactor) OnCall(opts *bind.TransactOpts, _context TestDAppV2zContext, _zrc20 common.Address, amount *big.Int, message []byte) (*types.Transaction, error) { + return _TestDAppV2.contract.Transact(opts, "onCall", _context, _zrc20, amount, message) } -// OnCall is a paid mutator transaction binding the contract method 0x676cc054. +// OnCall is a paid mutator transaction binding the contract method 0x5bcfd616. // -// Solidity: function onCall((address) messageContext, bytes message) payable returns(bytes) -func (_TestDAppV2 *TestDAppV2Session) OnCall(messageContext TestDAppV2MessageContext, message []byte) (*types.Transaction, error) { - return _TestDAppV2.Contract.OnCall(&_TestDAppV2.TransactOpts, messageContext, message) +// Solidity: function onCall((bytes,address,uint256) _context, address _zrc20, uint256 amount, bytes message) returns() +func (_TestDAppV2 *TestDAppV2Session) OnCall(_context TestDAppV2zContext, _zrc20 common.Address, amount *big.Int, message []byte) (*types.Transaction, error) { + return _TestDAppV2.Contract.OnCall(&_TestDAppV2.TransactOpts, _context, _zrc20, amount, message) } -// OnCall is a paid mutator transaction binding the contract method 0x676cc054. +// OnCall is a paid mutator transaction binding the contract method 0x5bcfd616. // -// Solidity: function onCall((address) messageContext, bytes message) payable returns(bytes) -func (_TestDAppV2 *TestDAppV2TransactorSession) OnCall(messageContext TestDAppV2MessageContext, message []byte) (*types.Transaction, error) { - return _TestDAppV2.Contract.OnCall(&_TestDAppV2.TransactOpts, messageContext, message) +// Solidity: function onCall((bytes,address,uint256) _context, address _zrc20, uint256 amount, bytes message) returns() +func (_TestDAppV2 *TestDAppV2TransactorSession) OnCall(_context TestDAppV2zContext, _zrc20 common.Address, amount *big.Int, message []byte) (*types.Transaction, error) { + return _TestDAppV2.Contract.OnCall(&_TestDAppV2.TransactOpts, _context, _zrc20, amount, message) } -// OnCrossChainCall is a paid mutator transaction binding the contract method 0xde43156e. +// OnCall0 is a paid mutator transaction binding the contract method 0x676cc054. // -// Solidity: function onCrossChainCall((bytes,address,uint256) _context, address _zrc20, uint256 amount, bytes message) returns() -func (_TestDAppV2 *TestDAppV2Transactor) OnCrossChainCall(opts *bind.TransactOpts, _context TestDAppV2zContext, _zrc20 common.Address, amount *big.Int, message []byte) (*types.Transaction, error) { - return _TestDAppV2.contract.Transact(opts, "onCrossChainCall", _context, _zrc20, amount, message) +// Solidity: function onCall((address) messageContext, bytes message) payable returns(bytes) +func (_TestDAppV2 *TestDAppV2Transactor) OnCall0(opts *bind.TransactOpts, messageContext TestDAppV2MessageContext, message []byte) (*types.Transaction, error) { + return _TestDAppV2.contract.Transact(opts, "onCall0", messageContext, message) } -// OnCrossChainCall is a paid mutator transaction binding the contract method 0xde43156e. +// OnCall0 is a paid mutator transaction binding the contract method 0x676cc054. // -// Solidity: function onCrossChainCall((bytes,address,uint256) _context, address _zrc20, uint256 amount, bytes message) returns() -func (_TestDAppV2 *TestDAppV2Session) OnCrossChainCall(_context TestDAppV2zContext, _zrc20 common.Address, amount *big.Int, message []byte) (*types.Transaction, error) { - return _TestDAppV2.Contract.OnCrossChainCall(&_TestDAppV2.TransactOpts, _context, _zrc20, amount, message) +// Solidity: function onCall((address) messageContext, bytes message) payable returns(bytes) +func (_TestDAppV2 *TestDAppV2Session) OnCall0(messageContext TestDAppV2MessageContext, message []byte) (*types.Transaction, error) { + return _TestDAppV2.Contract.OnCall0(&_TestDAppV2.TransactOpts, messageContext, message) } -// OnCrossChainCall is a paid mutator transaction binding the contract method 0xde43156e. +// OnCall0 is a paid mutator transaction binding the contract method 0x676cc054. // -// Solidity: function onCrossChainCall((bytes,address,uint256) _context, address _zrc20, uint256 amount, bytes message) returns() -func (_TestDAppV2 *TestDAppV2TransactorSession) OnCrossChainCall(_context TestDAppV2zContext, _zrc20 common.Address, amount *big.Int, message []byte) (*types.Transaction, error) { - return _TestDAppV2.Contract.OnCrossChainCall(&_TestDAppV2.TransactOpts, _context, _zrc20, amount, message) +// Solidity: function onCall((address) messageContext, bytes message) payable returns(bytes) +func (_TestDAppV2 *TestDAppV2TransactorSession) OnCall0(messageContext TestDAppV2MessageContext, message []byte) (*types.Transaction, error) { + return _TestDAppV2.Contract.OnCall0(&_TestDAppV2.TransactOpts, messageContext, message) } -// OnRevert is a paid mutator transaction binding the contract method 0x5ac1e070. +// OnRevert is a paid mutator transaction binding the contract method 0xc9028a36. // -// Solidity: function onRevert((address,address,uint64,bytes) revertContext) returns() +// Solidity: function onRevert((address,address,uint256,bytes) revertContext) returns() func (_TestDAppV2 *TestDAppV2Transactor) OnRevert(opts *bind.TransactOpts, revertContext TestDAppV2RevertContext) (*types.Transaction, error) { return _TestDAppV2.contract.Transact(opts, "onRevert", revertContext) } -// OnRevert is a paid mutator transaction binding the contract method 0x5ac1e070. +// OnRevert is a paid mutator transaction binding the contract method 0xc9028a36. // -// Solidity: function onRevert((address,address,uint64,bytes) revertContext) returns() +// Solidity: function onRevert((address,address,uint256,bytes) revertContext) returns() func (_TestDAppV2 *TestDAppV2Session) OnRevert(revertContext TestDAppV2RevertContext) (*types.Transaction, error) { return _TestDAppV2.Contract.OnRevert(&_TestDAppV2.TransactOpts, revertContext) } -// OnRevert is a paid mutator transaction binding the contract method 0x5ac1e070. +// OnRevert is a paid mutator transaction binding the contract method 0xc9028a36. // -// Solidity: function onRevert((address,address,uint64,bytes) revertContext) returns() +// Solidity: function onRevert((address,address,uint256,bytes) revertContext) returns() func (_TestDAppV2 *TestDAppV2TransactorSession) OnRevert(revertContext TestDAppV2RevertContext) (*types.Transaction, error) { return _TestDAppV2.Contract.OnRevert(&_TestDAppV2.TransactOpts, revertContext) } -// SetExpectedOnCallSender is a paid mutator transaction binding the contract method 0xc234fecf. -// -// Solidity: function setExpectedOnCallSender(address _expectedOnCallSender) returns() -func (_TestDAppV2 *TestDAppV2Transactor) SetExpectedOnCallSender(opts *bind.TransactOpts, _expectedOnCallSender common.Address) (*types.Transaction, error) { - return _TestDAppV2.contract.Transact(opts, "setExpectedOnCallSender", _expectedOnCallSender) -} - -// SetExpectedOnCallSender is a paid mutator transaction binding the contract method 0xc234fecf. -// -// Solidity: function setExpectedOnCallSender(address _expectedOnCallSender) returns() -func (_TestDAppV2 *TestDAppV2Session) SetExpectedOnCallSender(_expectedOnCallSender common.Address) (*types.Transaction, error) { - return _TestDAppV2.Contract.SetExpectedOnCallSender(&_TestDAppV2.TransactOpts, _expectedOnCallSender) -} - -// SetExpectedOnCallSender is a paid mutator transaction binding the contract method 0xc234fecf. -// -// Solidity: function setExpectedOnCallSender(address _expectedOnCallSender) returns() -func (_TestDAppV2 *TestDAppV2TransactorSession) SetExpectedOnCallSender(_expectedOnCallSender common.Address) (*types.Transaction, error) { - return _TestDAppV2.Contract.SetExpectedOnCallSender(&_TestDAppV2.TransactOpts, _expectedOnCallSender) -} - // SimpleCall is a paid mutator transaction binding the contract method 0x36e980a0. // // Solidity: function simpleCall(string message) returns() diff --git a/pkg/contracts/testdappv2/TestDAppV2.json b/pkg/contracts/testdappv2/TestDAppV2.json index 3117879927..189d8b853f 100644 --- a/pkg/contracts/testdappv2/TestDAppV2.json +++ b/pkg/contracts/testdappv2/TestDAppV2.json @@ -1,5 +1,18 @@ { "abi": [ + { + "inputs": [], + "name": "NO_MESSAGE_CALL", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -61,19 +74,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [], - "name": "expectedOnCallSender", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [ { @@ -128,32 +128,20 @@ { "inputs": [ { - "components": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "internalType": "struct TestDAppV2.MessageContext", - "name": "messageContext", - "type": "tuple" - }, - { - "internalType": "bytes", - "name": "message", - "type": "bytes" + "internalType": "address", + "name": "sender", + "type": "address" } ], - "name": "onCall", + "name": "getNoMessageIndex", "outputs": [ { - "internalType": "bytes", + "internalType": "string", "name": "", - "type": "bytes" + "type": "string" } ], - "stateMutability": "payable", + "stateMutability": "pure", "type": "function" }, { @@ -196,11 +184,42 @@ "type": "bytes" } ], - "name": "onCrossChainCall", + "name": "onCall", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "internalType": "struct TestDAppV2.MessageContext", + "name": "messageContext", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "message", + "type": "bytes" + } + ], + "name": "onCall", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "function" + }, { "inputs": [ { @@ -216,9 +235,9 @@ "type": "address" }, { - "internalType": "uint64", + "internalType": "uint256", "name": "amount", - "type": "uint64" + "type": "uint256" }, { "internalType": "bytes", @@ -255,19 +274,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [ - { - "internalType": "address", - "name": "_expectedOnCallSender", - "type": "address" - } - ], - "name": "setExpectedOnCallSender", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -286,5 +292,5 @@ "type": "receive" } ], - "bin": "608060405234801561001057600080fd5b5061150a806100206000396000f3fe6080604052600436106100c65760003560e01c8063a799911f1161007f578063de43156e11610059578063de43156e14610267578063e2842ed714610290578063f592cbfb146102cd578063f936ae851461030a576100cd565b8063a799911f146101f9578063c234fecf14610215578063c7a339a91461023e576100cd565b806336e980a0146100d25780634297a263146100fb57806359f4a777146101385780635ac1e07014610163578063676cc0541461018c5780639291fe26146101bc576100cd565b366100cd57005b600080fd5b3480156100de57600080fd5b506100f960048036038101906100f49190610de7565b610347565b005b34801561010757600080fd5b50610122600480360381019061011d9190610d02565b610371565b60405161012f9190611173565b60405180910390f35b34801561014457600080fd5b5061014d610389565b60405161015a91906110c4565b60405180910390f35b34801561016f57600080fd5b5061018a60048036038101906101859190610e90565b6103ad565b005b6101a660048036038101906101a19190610e30565b6104e7565b6040516101b39190611131565b60405180910390f35b3480156101c857600080fd5b506101e360048036038101906101de9190610de7565b61069c565b6040516101f09190611173565b60405180910390f35b610213600480360381019061020e9190610de7565b6106df565b005b34801561022157600080fd5b5061023c60048036038101906102379190610ca8565b610708565b005b34801561024a57600080fd5b5061026560048036038101906102609190610d78565b61074b565b005b34801561027357600080fd5b5061028e60048036038101906102899190610ed9565b61080e565b005b34801561029c57600080fd5b506102b760048036038101906102b29190610d02565b610907565b6040516102c49190611116565b60405180910390f35b3480156102d957600080fd5b506102f460048036038101906102ef9190610de7565b610927565b6040516103019190611116565b60405180910390f35b34801561031657600080fd5b50610331600480360381019061032c9190610d2f565b610977565b60405161033e91906110c4565b60405180910390f35b610350816109c0565b1561035a57600080fd5b61036381610a16565b61036e816000610a6a565b50565b60036020528060005260406000206000915090505481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6104088180606001906103c0919061118e565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610a16565b61046581806060019061041b919061118e565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506000610a6a565b8060000160208101906104789190610ca8565b600282806060019061048a919061118e565b60405161049892919061107f565b908152602001604051809103902060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b606060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168460000160208101906105339190610ca8565b73ffffffffffffffffffffffffffffffffffffffff1614610589576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161058090611153565b60405180910390fd5b6105d683838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610a16565b61062483838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505034610a6a565b8360000160208101906106379190610ca8565b6002848460405161064992919061107f565b908152602001604051809103902060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509392505050565b600060036000836040516020016106b39190611098565b604051602081830303815290604052805190602001208152602001908152602001600020549050919050565b6106e8816109c0565b156106f257600080fd5b6106fb81610a16565b6107058134610a6a565b50565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b610754816109c0565b1561075e57600080fd5b8273ffffffffffffffffffffffffffffffffffffffff166323b872dd3330856040518463ffffffff1660e01b815260040161079b939291906110df565b602060405180830381600087803b1580156107b557600080fd5b505af11580156107c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107ed9190610cd5565b6107f657600080fd5b6107ff81610a16565b6108098183610a6a565b505050565b61085b82828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506109c0565b1561086557600080fd5b6108b282828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610a16565b61090082828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505084610a6a565b5050505050565b60016020528060005260406000206000915054906101000a900460ff1681565b6000600160008360405160200161093e9190611098565b60405160208183030381529060405280519060200120815260200190815260200160002060009054906101000a900460ff169050919050565b6002818051602081018201805184825260208301602085012081835280955050505050506000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006040516020016109d1906110af565b60405160208183030381529060405280519060200120826040516020016109f89190611098565b60405160208183030381529060405280519060200120149050919050565b600180600083604051602001610a2c9190611098565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a81548160ff02191690831515021790555050565b806003600084604051602001610a809190611098565b604051602081830303815290604052805190602001208152602001908152602001600020819055505050565b6000610abf610aba84611216565b6111f1565b905082815260208101848484011115610adb57610ada6113ef565b5b610ae684828561132a565b509392505050565b6000610b01610afc84611247565b6111f1565b905082815260208101848484011115610b1d57610b1c6113ef565b5b610b2884828561132a565b509392505050565b600081359050610b3f81611461565b92915050565b600081519050610b5481611478565b92915050565b600081359050610b698161148f565b92915050565b60008083601f840112610b8557610b846113d1565b5b8235905067ffffffffffffffff811115610ba257610ba16113cc565b5b602083019150836001820283011115610bbe57610bbd6113e5565b5b9250929050565b600082601f830112610bda57610bd96113d1565b5b8135610bea848260208601610aac565b91505092915050565b600081359050610c02816114a6565b92915050565b600082601f830112610c1d57610c1c6113d1565b5b8135610c2d848260208601610aee565b91505092915050565b600060208284031215610c4c57610c4b6113db565b5b81905092915050565b600060808284031215610c6b57610c6a6113db565b5b81905092915050565b600060608284031215610c8a57610c896113db565b5b81905092915050565b600081359050610ca2816114bd565b92915050565b600060208284031215610cbe57610cbd6113f9565b5b6000610ccc84828501610b30565b91505092915050565b600060208284031215610ceb57610cea6113f9565b5b6000610cf984828501610b45565b91505092915050565b600060208284031215610d1857610d176113f9565b5b6000610d2684828501610b5a565b91505092915050565b600060208284031215610d4557610d446113f9565b5b600082013567ffffffffffffffff811115610d6357610d626113f4565b5b610d6f84828501610bc5565b91505092915050565b600080600060608486031215610d9157610d906113f9565b5b6000610d9f86828701610bf3565b9350506020610db086828701610c93565b925050604084013567ffffffffffffffff811115610dd157610dd06113f4565b5b610ddd86828701610c08565b9150509250925092565b600060208284031215610dfd57610dfc6113f9565b5b600082013567ffffffffffffffff811115610e1b57610e1a6113f4565b5b610e2784828501610c08565b91505092915050565b600080600060408486031215610e4957610e486113f9565b5b6000610e5786828701610c36565b935050602084013567ffffffffffffffff811115610e7857610e776113f4565b5b610e8486828701610b6f565b92509250509250925092565b600060208284031215610ea657610ea56113f9565b5b600082013567ffffffffffffffff811115610ec457610ec36113f4565b5b610ed084828501610c55565b91505092915050565b600080600080600060808688031215610ef557610ef46113f9565b5b600086013567ffffffffffffffff811115610f1357610f126113f4565b5b610f1f88828901610c74565b9550506020610f3088828901610b30565b9450506040610f4188828901610c93565b935050606086013567ffffffffffffffff811115610f6257610f616113f4565b5b610f6e88828901610b6f565b92509250509295509295909350565b610f86816112c6565b82525050565b610f95816112d8565b82525050565b6000610fa7838561129f565b9350610fb483858461132a565b82840190509392505050565b6000610fcb82611278565b610fd5818561128e565b9350610fe5818560208601611339565b610fee816113fe565b840191505092915050565b600061100482611283565b61100e81856112bb565b935061101e818560208601611339565b80840191505092915050565b60006110376016836112aa565b91506110428261140f565b602082019050919050565b600061105a6006836112bb565b915061106582611438565b600682019050919050565b61107981611320565b82525050565b600061108c828486610f9b565b91508190509392505050565b60006110a48284610ff9565b915081905092915050565b60006110ba8261104d565b9150819050919050565b60006020820190506110d96000830184610f7d565b92915050565b60006060820190506110f46000830186610f7d565b6111016020830185610f7d565b61110e6040830184611070565b949350505050565b600060208201905061112b6000830184610f8c565b92915050565b6000602082019050818103600083015261114b8184610fc0565b905092915050565b6000602082019050818103600083015261116c8161102a565b9050919050565b60006020820190506111886000830184611070565b92915050565b600080833560016020038436030381126111ab576111aa6113e0565b5b80840192508235915067ffffffffffffffff8211156111cd576111cc6113d6565b5b6020830192506001820236038313156111e9576111e86113ea565b5b509250929050565b60006111fb61120c565b9050611207828261136c565b919050565b6000604051905090565b600067ffffffffffffffff8211156112315761123061139d565b5b61123a826113fe565b9050602081019050919050565b600067ffffffffffffffff8211156112625761126161139d565b5b61126b826113fe565b9050602081019050919050565b600081519050919050565b600081519050919050565b600082825260208201905092915050565b600081905092915050565b600082825260208201905092915050565b600081905092915050565b60006112d182611300565b9050919050565b60008115159050919050565b6000819050919050565b60006112f9826112c6565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b82818337600083830152505050565b60005b8381101561135757808201518184015260208101905061133c565b83811115611366576000848401525b50505050565b611375826113fe565b810181811067ffffffffffffffff821117156113945761139361139d565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f756e61757468656e746963617465642073656e64657200000000000000000000600082015250565b7f7265766572740000000000000000000000000000000000000000000000000000600082015250565b61146a816112c6565b811461147557600080fd5b50565b611481816112d8565b811461148c57600080fd5b50565b611498816112e4565b81146114a357600080fd5b50565b6114af816112ee565b81146114ba57600080fd5b50565b6114c681611320565b81146114d157600080fd5b5056fea2646970667358221220e090c5cd81361f8bba5d13d4fb801ab148714dad774e12a4acf812e341dc93c564736f6c63430008070033" + "bin": "6080604052348015600f57600080fd5b506115288061001f6000396000f3fe6080604052600436106100c65760003560e01c8063ad23b28b1161007f578063c9028a3611610059578063c9028a361461027b578063e2842ed7146102a4578063f592cbfb146102e1578063f936ae851461031e576100cd565b8063ad23b28b146101ea578063c7a339a914610227578063c85f843414610250576100cd565b806336e980a0146100d25780634297a263146100fb5780635bcfd61614610138578063676cc054146101615780639291fe2614610191578063a799911f146101ce576100cd565b366100cd57005b600080fd5b3480156100de57600080fd5b506100f960048036038101906100f49190610b86565b61035b565b005b34801561010757600080fd5b50610122600480360381019061011d9190610c05565b610385565b60405161012f9190610c4b565b60405180910390f35b34801561014457600080fd5b5061015f600480360381019061015a9190610d74565b61039d565b005b61017b60048036038101906101769190610e37565b610483565b6040516101889190610f16565b60405180910390f35b34801561019d57600080fd5b506101b860048036038101906101b39190610b86565b610595565b6040516101c59190610c4b565b60405180910390f35b6101e860048036038101906101e39190610b86565b6105d8565b005b3480156101f657600080fd5b50610211600480360381019061020c9190610f38565b610601565b60405161021e9190610fba565b60405180910390f35b34801561023357600080fd5b5061024e6004803603810190610249919061101a565b610661565b005b34801561025c57600080fd5b50610265610715565b6040516102729190610fba565b60405180910390f35b34801561028757600080fd5b506102a2600480360381019061029d91906110a8565b61074e565b005b3480156102b057600080fd5b506102cb60048036038101906102c69190610c05565b610888565b6040516102d8919061110c565b60405180910390f35b3480156102ed57600080fd5b5061030860048036038101906103039190610b86565b6108a8565b604051610315919061110c565b60405180910390f35b34801561032a57600080fd5b50610345600480360381019061034091906111c8565b6108f7565b6040516103529190611220565b60405180910390f35b61036481610940565b1561036e57600080fd5b61037781610996565b6103828160006109ea565b50565b60026020528060005260406000206000915090505481565b6103ea82828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610940565b156103f457600080fd5b600080838390501461044a5782828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610466565b6104658660200160208101906104609190610f38565b610601565b5b905061047181610996565b61047b81856109ea565b505050505050565b606060008084849050146104db5783838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506104f7565b6104f68560000160208101906104f19190610f38565b610601565b5b905061050281610996565b61050c81346109ea565b84600001602081019061051f9190610f38565b60018260405161052f9190611277565b908152602001604051809103902060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550604051806020016040528060008152509150509392505050565b600060026000836040516020016105ac91906112ca565b604051602081830303815290604052805190602001208152602001908152602001600020549050919050565b6105e181610940565b156105eb57600080fd5b6105f481610996565b6105fe81346109ea565b50565b60606040518060400160405280601681526020017f63616c6c65642077697468206e6f206d657373616765000000000000000000008152508260405160200161064b929190611329565b6040516020818303038152906040529050919050565b61066a81610940565b1561067457600080fd5b8273ffffffffffffffffffffffffffffffffffffffff166323b872dd3330856040518463ffffffff1660e01b81526004016106b193929190611351565b6020604051808303816000875af11580156106d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106f491906113b4565b6106fd57600080fd5b61070681610996565b61071081836109ea565b505050565b6040518060400160405280601681526020017f63616c6c65642077697468206e6f206d6573736167650000000000000000000081525081565b6107a981806060019061076191906113f0565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610996565b6108068180606001906107bc91906113f0565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505060006109ea565b8060000160208101906108199190610f38565b600182806060019061082b91906113f0565b604051610839929190611478565b908152602001604051809103902060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60006020528060005260406000206000915054906101000a900460ff1681565b6000806000836040516020016108be91906112ca565b60405160208183030381529060405280519060200120815260200190815260200160002060009054906101000a900460ff169050919050565b6001818051602081018201805184825260208301602085012081835280955050505050506000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000604051602001610951906114dd565b604051602081830303815290604052805190602001208260405160200161097891906112ca565b60405160208183030381529060405280519060200120149050919050565b6001600080836040516020016109ac91906112ca565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a81548160ff02191690831515021790555050565b806002600084604051602001610a0091906112ca565b604051602081830303815290604052805190602001208152602001908152602001600020819055505050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610a9382610a4a565b810181811067ffffffffffffffff82111715610ab257610ab1610a5b565b5b80604052505050565b6000610ac5610a2c565b9050610ad18282610a8a565b919050565b600067ffffffffffffffff821115610af157610af0610a5b565b5b610afa82610a4a565b9050602081019050919050565b82818337600083830152505050565b6000610b29610b2484610ad6565b610abb565b905082815260208101848484011115610b4557610b44610a45565b5b610b50848285610b07565b509392505050565b600082601f830112610b6d57610b6c610a40565b5b8135610b7d848260208601610b16565b91505092915050565b600060208284031215610b9c57610b9b610a36565b5b600082013567ffffffffffffffff811115610bba57610bb9610a3b565b5b610bc684828501610b58565b91505092915050565b6000819050919050565b610be281610bcf565b8114610bed57600080fd5b50565b600081359050610bff81610bd9565b92915050565b600060208284031215610c1b57610c1a610a36565b5b6000610c2984828501610bf0565b91505092915050565b6000819050919050565b610c4581610c32565b82525050565b6000602082019050610c606000830184610c3c565b92915050565b600080fd5b600060608284031215610c8157610c80610c66565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610cb582610c8a565b9050919050565b610cc581610caa565b8114610cd057600080fd5b50565b600081359050610ce281610cbc565b92915050565b610cf181610c32565b8114610cfc57600080fd5b50565b600081359050610d0e81610ce8565b92915050565b600080fd5b600080fd5b60008083601f840112610d3457610d33610a40565b5b8235905067ffffffffffffffff811115610d5157610d50610d14565b5b602083019150836001820283011115610d6d57610d6c610d19565b5b9250929050565b600080600080600060808688031215610d9057610d8f610a36565b5b600086013567ffffffffffffffff811115610dae57610dad610a3b565b5b610dba88828901610c6b565b9550506020610dcb88828901610cd3565b9450506040610ddc88828901610cff565b935050606086013567ffffffffffffffff811115610dfd57610dfc610a3b565b5b610e0988828901610d1e565b92509250509295509295909350565b600060208284031215610e2e57610e2d610c66565b5b81905092915050565b600080600060408486031215610e5057610e4f610a36565b5b6000610e5e86828701610e18565b935050602084013567ffffffffffffffff811115610e7f57610e7e610a3b565b5b610e8b86828701610d1e565b92509250509250925092565b600081519050919050565b600082825260208201905092915050565b60005b83811015610ed1578082015181840152602081019050610eb6565b60008484015250505050565b6000610ee882610e97565b610ef28185610ea2565b9350610f02818560208601610eb3565b610f0b81610a4a565b840191505092915050565b60006020820190508181036000830152610f308184610edd565b905092915050565b600060208284031215610f4e57610f4d610a36565b5b6000610f5c84828501610cd3565b91505092915050565b600081519050919050565b600082825260208201905092915050565b6000610f8c82610f65565b610f968185610f70565b9350610fa6818560208601610eb3565b610faf81610a4a565b840191505092915050565b60006020820190508181036000830152610fd48184610f81565b905092915050565b6000610fe782610caa565b9050919050565b610ff781610fdc565b811461100257600080fd5b50565b60008135905061101481610fee565b92915050565b60008060006060848603121561103357611032610a36565b5b600061104186828701611005565b935050602061105286828701610cff565b925050604084013567ffffffffffffffff81111561107357611072610a3b565b5b61107f86828701610b58565b9150509250925092565b60006080828403121561109f5761109e610c66565b5b81905092915050565b6000602082840312156110be576110bd610a36565b5b600082013567ffffffffffffffff8111156110dc576110db610a3b565b5b6110e884828501611089565b91505092915050565b60008115159050919050565b611106816110f1565b82525050565b600060208201905061112160008301846110fd565b92915050565b600067ffffffffffffffff82111561114257611141610a5b565b5b61114b82610a4a565b9050602081019050919050565b600061116b61116684611127565b610abb565b90508281526020810184848401111561118757611186610a45565b5b611192848285610b07565b509392505050565b600082601f8301126111af576111ae610a40565b5b81356111bf848260208601611158565b91505092915050565b6000602082840312156111de576111dd610a36565b5b600082013567ffffffffffffffff8111156111fc576111fb610a3b565b5b6112088482850161119a565b91505092915050565b61121a81610caa565b82525050565b60006020820190506112356000830184611211565b92915050565b600081905092915050565b600061125182610e97565b61125b818561123b565b935061126b818560208601610eb3565b80840191505092915050565b60006112838284611246565b915081905092915050565b600081905092915050565b60006112a482610f65565b6112ae818561128e565b93506112be818560208601610eb3565b80840191505092915050565b60006112d68284611299565b915081905092915050565b60008160601b9050919050565b60006112f9826112e1565b9050919050565b600061130b826112ee565b9050919050565b61132361131e82610caa565b611300565b82525050565b60006113358285611299565b91506113418284611312565b6014820191508190509392505050565b60006060820190506113666000830186611211565b6113736020830185611211565b6113806040830184610c3c565b949350505050565b611391816110f1565b811461139c57600080fd5b50565b6000815190506113ae81611388565b92915050565b6000602082840312156113ca576113c9610a36565b5b60006113d88482850161139f565b91505092915050565b600080fd5b600080fd5b600080fd5b6000808335600160200384360303811261140d5761140c6113e1565b5b80840192508235915067ffffffffffffffff82111561142f5761142e6113e6565b5b60208301925060018202360383131561144b5761144a6113eb565b5b509250929050565b600061145f838561123b565b935061146c838584610b07565b82840190509392505050565b6000611485828486611453565b91508190509392505050565b7f7265766572740000000000000000000000000000000000000000000000000000600082015250565b60006114c760068361128e565b91506114d282611491565b600682019050919050565b60006114e8826114ba565b915081905091905056fea26469706673582212205747e512af435680fec20f1d2e088f93d6c12052fee7e353f45e5e49a671377b64736f6c634300081a0033" } diff --git a/pkg/contracts/testdappv2/TestDAppV2.sol b/pkg/contracts/testdappv2/TestDAppV2.sol index 5f301a08cf..3282cbe383 100644 --- a/pkg/contracts/testdappv2/TestDAppV2.sol +++ b/pkg/contracts/testdappv2/TestDAppV2.sol @@ -1,11 +1,13 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.26; interface IERC20 { function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); } contract TestDAppV2 { + string public constant NO_MESSAGE_CALL = "called with no message"; + struct zContext { bytes origin; address sender; @@ -20,7 +22,7 @@ contract TestDAppV2 { struct RevertContext { address sender; address asset; - uint64 amount; + uint256 amount; bytes revertMessage; } @@ -30,16 +32,22 @@ contract TestDAppV2 { address sender; } - address public expectedOnCallSender; - // these structures allow to assess contract calls mapping(bytes32 => bool) public calledWithMessage; mapping(bytes => address) public senderWithMessage; mapping(bytes32 => uint256) public amountWithMessage; + // return the index used for the "WithMessage" mapping when the message for calls is empty + // this allows testing the message with empty message + // this function includes the sender of the message to avoid collisions when running parallel tests with different senders + function getNoMessageIndex(address sender) pure public returns (string memory) { + return string(abi.encodePacked(NO_MESSAGE_CALL, sender)); + } + function setCalledWithMessage(string memory message) internal { calledWithMessage[keccak256(abi.encodePacked(message))] = true; } + function setAmountWithMessage(string memory message, uint256 amount) internal { amountWithMessage[keccak256(abi.encodePacked(message))] = amount; } @@ -52,8 +60,8 @@ contract TestDAppV2 { return amountWithMessage[keccak256(abi.encodePacked(message))]; } - // Universal contract interface - function onCrossChainCall( + // Universal contract interface on ZEVM + function onCall( zContext calldata _context, address _zrc20, uint256 amount, @@ -63,8 +71,10 @@ contract TestDAppV2 { { require(!isRevertMessage(string(message))); - setCalledWithMessage(string(message)); - setAmountWithMessage(string(message), amount); + string memory messageStr = message.length == 0 ? getNoMessageIndex(_context.sender) : string(message); + + setCalledWithMessage(messageStr); + setAmountWithMessage(messageStr, amount); } // called with gas token @@ -105,15 +115,15 @@ contract TestDAppV2 { senderWithMessage[revertContext.revertMessage] = revertContext.sender; } - function setExpectedOnCallSender(address _expectedOnCallSender) external { - expectedOnCallSender = _expectedOnCallSender; - } - + // Callable interface on connected EVM chains function onCall(MessageContext calldata messageContext, bytes calldata message) external payable returns (bytes memory) { - require(messageContext.sender == expectedOnCallSender, "unauthenticated sender"); - setCalledWithMessage(string(message)); - setAmountWithMessage(string(message), msg.value); - senderWithMessage[message] = messageContext.sender; + string memory messageStr = message.length == 0 ? getNoMessageIndex(messageContext.sender) : string(message); + + setCalledWithMessage(messageStr); + setAmountWithMessage(messageStr, msg.value); + senderWithMessage[bytes(messageStr)] = messageContext.sender; + + return ""; } receive() external payable {} diff --git a/pkg/contracts/testdappv2/bindings.go b/pkg/contracts/testdappv2/bindings.go index 73843ba2d2..f2dbd2bfa8 100644 --- a/pkg/contracts/testdappv2/bindings.go +++ b/pkg/contracts/testdappv2/bindings.go @@ -1,4 +1,4 @@ -//go:generate sh -c "solc TestDAppV2.sol --combined-json abi,bin | jq '.contracts.\"TestDAppV2.sol:TestDAppV2\"' > TestDAppV2.json" +//go:generate sh -c "solc TestDAppV2.sol --evm-version london --combined-json abi,bin | jq '.contracts.\"TestDAppV2.sol:TestDAppV2\"' > TestDAppV2.json" //go:generate sh -c "cat TestDAppV2.json | jq .abi > TestDAppV2.abi" //go:generate sh -c "cat TestDAppV2.json | jq .bin | tr -d '\"' > TestDAppV2.bin" //go:generate sh -c "abigen --abi TestDAppV2.abi --bin TestDAppV2.bin --pkg testdappv2 --type TestDAppV2 --out TestDAppV2.go" diff --git a/pkg/contracts/ton/gateway.go b/pkg/contracts/ton/gateway.go new file mode 100644 index 0000000000..33ca2c7977 --- /dev/null +++ b/pkg/contracts/ton/gateway.go @@ -0,0 +1,258 @@ +// Package ton provider bindings for TON blockchain including Gateway contract wrapper. +package ton + +import ( + "cosmossdk.io/math" + "github.com/pkg/errors" + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/tlb" + "github.com/tonkeeper/tongo/ton" +) + +// Gateway represents bindings for Zeta Gateway contract on TON +// +// Gateway.ParseTransaction parses Gateway transaction. +// The parser reads tx body cell and decodes it based on Operation code (op) +// - inbound transactions: deposit, donate, depositAndCall +// - outbound transactions: not implemented yet +// - errors for all other transactions +// +// `Send*` methods work the same way by constructing (& signing) tx body cell that is expected by the contract +// +// @see https://github.com/zeta-chain/protocol-contracts-ton/blob/main/contracts/gateway.fc +type Gateway struct { + accountID ton.AccountID +} + +const ( + sizeOpCode = 32 + sizeQueryID = 64 +) + +var ( + ErrParse = errors.New("unable to parse tx") + ErrUnknownOp = errors.New("unknown op") + ErrCast = errors.New("unable to cast tx content") +) + +// NewGateway Gateway constructor +func NewGateway(accountID ton.AccountID) *Gateway { + return &Gateway{accountID} +} + +// AccountID returns gateway address +func (gw *Gateway) AccountID() ton.AccountID { + return gw.accountID +} + +// ParseTransaction parses transaction to Transaction +func (gw *Gateway) ParseTransaction(tx ton.Transaction) (*Transaction, error) { + if !tx.IsSuccess() { + exitCode := tx.Description.TransOrd.ComputePh.TrPhaseComputeVm.Vm.ExitCode + return nil, errors.Wrapf(ErrParse, "tx %s is not successful (exit code %d)", tx.Hash().Hex(), exitCode) + } + + if tx.Msgs.InMsg.Exists { + inbound, err := gw.parseInbound(tx) + if err != nil { + return nil, errors.Wrapf(err, "unable to parse inbound tx %s", tx.Hash().Hex()) + } + + return inbound, nil + } + + outbound, err := gw.parseOutbound(tx) + if err != nil { + return nil, errors.Wrapf(err, "unable to parse outbound tx %s", tx.Hash().Hex()) + } + + return outbound, nil +} + +// ParseAndFilter parses transaction and applies filter to it. Returns (tx, skip?, error) +// If parse fails due to known error, skip is set to true +func (gw *Gateway) ParseAndFilter(tx ton.Transaction, filter func(*Transaction) bool) (*Transaction, bool, error) { + parsedTX, err := gw.ParseTransaction(tx) + switch { + case errors.Is(err, ErrParse): + return nil, true, nil + case errors.Is(err, ErrUnknownOp): + return nil, true, nil + case err != nil: + return nil, false, err + } + + if !filter(parsedTX) { + return nil, true, nil + } + + return parsedTX, false, nil +} + +// FilterInbounds filters transactions with deposit operations +func FilterInbounds(tx *Transaction) bool { return tx.IsInbound() } + +func (gw *Gateway) parseInbound(tx ton.Transaction) (*Transaction, error) { + body, err := parseInternalMessageBody(tx) + if err != nil { + return nil, errors.Wrap(err, "unable to parse body") + } + + intMsgInfo := tx.Msgs.InMsg.Value.Value.Info.IntMsgInfo + if intMsgInfo == nil { + return nil, errors.Wrap(ErrParse, "no internal message info") + } + + sourceID, err := ton.AccountIDFromTlb(intMsgInfo.Src) + if err != nil { + return nil, errors.Wrap(err, "unable to parse source account") + } + + destinationID, err := ton.AccountIDFromTlb(intMsgInfo.Dest) + if err != nil { + return nil, errors.Wrap(err, "unable to parse destination account") + } + + if gw.accountID != *destinationID { + return nil, errors.Wrap(ErrParse, "destination account is not gateway") + } + + op, err := body.ReadUint(sizeOpCode) + if err != nil { + return nil, errors.Wrap(err, "unable to read op code") + } + + var ( + sender = *sourceID + opCode = Op(op) + + content any + errContent error + ) + + switch opCode { + case OpDonate: + amount := intMsgInfo.Value.Grams - tx.TotalFees.Grams + content = Donation{Sender: sender, Amount: GramsToUint(amount)} + case OpDeposit: + content, errContent = parseDeposit(tx, sender, body) + case OpDepositAndCall: + content, errContent = parseDepositAndCall(tx, sender, body) + default: + // #nosec G115 always in range + return nil, errors.Wrapf(ErrUnknownOp, "op code %d", int64(op)) + } + + if errContent != nil { + // #nosec G115 always in range + return nil, errors.Wrapf(ErrParse, "unable to parse content for op code %d: %s", int64(op), errContent.Error()) + } + + return &Transaction{ + Transaction: tx, + Operation: opCode, + + content: content, + inbound: true, + }, nil +} + +func parseDeposit(tx ton.Transaction, sender ton.AccountID, body *boc.Cell) (Deposit, error) { + // skip query id + if err := body.Skip(sizeQueryID); err != nil { + return Deposit{}, err + } + + recipient, err := UnmarshalEVMAddress(body) + if err != nil { + return Deposit{}, errors.Wrap(err, "unable to read recipient") + } + + dl, err := parseDepositLog(tx) + if err != nil { + return Deposit{}, errors.Wrap(err, "unable to parse deposit log") + } + + return Deposit{ + Sender: sender, + Amount: dl.Amount, + Recipient: recipient, + }, nil +} + +type depositLog struct { + Amount math.Uint +} + +func parseDepositLog(tx ton.Transaction) (depositLog, error) { + messages := tx.Msgs.OutMsgs.Values() + if len(messages) == 0 { + return depositLog{}, errors.Wrap(ErrParse, "no out messages") + } + + // stored as ref + // cell log = begin_cell() + // .store_uint(op::internal::deposit, size::op_code_size) + // .store_uint(0, size::query_id_size) + // .store_slice(sender) + // .store_coins(deposit_amount) + // .store_uint(evm_recipient, size::evm_address) + // .end_cell(); + + var ( + bodyValue = boc.Cell(messages[0].Value.Body.Value) + body = &bodyValue + ) + + if err := body.Skip(sizeOpCode + sizeQueryID); err != nil { + return depositLog{}, errors.Wrap(err, "unable to skip bits") + } + + // skip msg address (ton sender) + if err := UnmarshalTLB(&tlb.MsgAddress{}, body); err != nil { + return depositLog{}, errors.Wrap(err, "unable to read sender address") + } + + var deposited tlb.Grams + if err := UnmarshalTLB(&deposited, body); err != nil { + return depositLog{}, errors.Wrap(err, "unable to read deposited amount") + } + + return depositLog{Amount: GramsToUint(deposited)}, nil +} + +func parseDepositAndCall(tx ton.Transaction, sender ton.AccountID, body *boc.Cell) (DepositAndCall, error) { + deposit, err := parseDeposit(tx, sender, body) + if err != nil { + return DepositAndCall{}, err + } + + callDataCell, err := body.NextRef() + if err != nil { + return DepositAndCall{}, errors.Wrap(err, "unable to read call data cell") + } + + callData, err := UnmarshalSnakeCell(callDataCell) + if err != nil { + return DepositAndCall{}, errors.Wrap(err, "unable to unmarshal call data") + } + + return DepositAndCall{Deposit: deposit, CallData: callData}, nil +} + +func (gw *Gateway) parseOutbound(_ ton.Transaction) (*Transaction, error) { + return nil, errors.New("not implemented") +} + +func parseInternalMessageBody(tx ton.Transaction) (*boc.Cell, error) { + if !tx.Msgs.InMsg.Exists { + return nil, errors.Wrap(ErrParse, "tx should have an internal message") + } + + var ( + inMsg = tx.Msgs.InMsg.Value.Value + body = boc.Cell(inMsg.Body.Value) + ) + + return &body, nil +} diff --git a/pkg/contracts/ton/gateway_op.go b/pkg/contracts/ton/gateway_op.go new file mode 100644 index 0000000000..7d711ab89c --- /dev/null +++ b/pkg/contracts/ton/gateway_op.go @@ -0,0 +1,115 @@ +package ton + +import ( + "errors" + + "cosmossdk.io/math" + eth "github.com/ethereum/go-ethereum/common" + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/ton" +) + +// Op operation code +type Op uint32 + +// github.com/zeta-chain/protocol-contracts-ton/blob/main/contracts/gateway.fc +// Inbound operations +const ( + OpDonate Op = 100 + iota + OpDeposit + OpDepositAndCall +) + +// Outbound operations +const ( + OpWithdraw Op = 200 + iota + SetDepositsEnabled + UpdateTSS + UpdateCode +) + +// Donation represents a donation operation +type Donation struct { + Sender ton.AccountID + Amount math.Uint +} + +// AsBody casts struct as internal message body. +func (d Donation) AsBody() (*boc.Cell, error) { + b := boc.NewCell() + err := ErrCollect( + b.WriteUint(uint64(OpDonate), sizeOpCode), + b.WriteUint(0, sizeQueryID), + ) + + return b, err +} + +// Deposit represents a deposit operation +type Deposit struct { + Sender ton.AccountID + Amount math.Uint + Recipient eth.Address +} + +// Memo casts deposit to memo bytes +func (d Deposit) Memo() []byte { + return d.Recipient.Bytes() +} + +// AsBody casts struct as internal message body. +func (d Deposit) AsBody() (*boc.Cell, error) { + b := boc.NewCell() + + return b, writeDepositBody(b, d.Recipient) +} + +// DepositAndCall represents a deposit and call operation +type DepositAndCall struct { + Deposit + CallData []byte +} + +// Memo casts deposit to call to memo bytes +func (d DepositAndCall) Memo() []byte { + recipient := d.Recipient.Bytes() + out := make([]byte, 0, len(recipient)+len(d.CallData)) + + out = append(out, recipient...) + out = append(out, d.CallData...) + + return out +} + +// AsBody casts struct to internal message body. +func (d DepositAndCall) AsBody() (*boc.Cell, error) { + b := boc.NewCell() + + return b, writeDepositAndCallBody(b, d.Recipient, d.CallData) +} + +func writeDepositBody(b *boc.Cell, recipient eth.Address) error { + return ErrCollect( + b.WriteUint(uint64(OpDeposit), sizeOpCode), + b.WriteUint(0, sizeQueryID), + b.WriteBytes(recipient.Bytes()), + ) +} + +func writeDepositAndCallBody(b *boc.Cell, recipient eth.Address, callData []byte) error { + if len(callData) == 0 { + return errors.New("call data is empty") + } + + callDataCell, err := MarshalSnakeCell(callData) + if err != nil { + return err + } + + return ErrCollect( + b.WriteUint(uint64(OpDepositAndCall), sizeOpCode), + b.WriteUint(0, sizeQueryID), + b.WriteBytes(recipient.Bytes()), + b.AddRef(callDataCell), + ) +} diff --git a/pkg/contracts/ton/gateway_send.go b/pkg/contracts/ton/gateway_send.go new file mode 100644 index 0000000000..5dd9c21340 --- /dev/null +++ b/pkg/contracts/ton/gateway_send.go @@ -0,0 +1,72 @@ +package ton + +import ( + "context" + + "cosmossdk.io/math" + eth "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/tlb" + "github.com/tonkeeper/tongo/wallet" +) + +// Sender TON tx sender. +type Sender interface { + Send(ctx context.Context, messages ...wallet.Sendable) error +} + +// see https://docs.ton.org/develop/smart-contracts/messages#message-modes +const ( + SendFlagSeparateFees = uint8(1) + SendFlagIgnoreErrors = uint8(2) +) + +// SendDeposit sends a deposit operation to the gateway on behalf of the sender. +func (gw *Gateway) SendDeposit( + ctx context.Context, + s Sender, + amount math.Uint, + zevmRecipient eth.Address, + sendMode uint8, +) error { + body := boc.NewCell() + + if err := writeDepositBody(body, zevmRecipient); err != nil { + return errors.Wrap(err, "failed to write deposit body") + } + + return gw.send(ctx, s, amount, body, sendMode) +} + +// SendDepositAndCall sends a deposit operation to the gateway on behalf of the sender +// with a callData to the recipient. +func (gw *Gateway) SendDepositAndCall( + ctx context.Context, + s Sender, + amount math.Uint, + zevmRecipient eth.Address, + callData []byte, + sendMode uint8, +) error { + body := boc.NewCell() + + if err := writeDepositAndCallBody(body, zevmRecipient, callData); err != nil { + return errors.Wrap(err, "failed to write depositAndCall body") + } + + return gw.send(ctx, s, amount, body, sendMode) +} + +func (gw *Gateway) send(ctx context.Context, s Sender, amount math.Uint, body *boc.Cell, sendMode uint8) error { + if body == nil { + return errors.New("body is nil") + } + + return s.Send(ctx, wallet.Message{ + Amount: tlb.Coins(amount.Uint64()), + Address: gw.accountID, + Body: body, + Mode: sendMode, + }) +} diff --git a/pkg/contracts/ton/gateway_test.go b/pkg/contracts/ton/gateway_test.go new file mode 100644 index 0000000000..dc680761ff --- /dev/null +++ b/pkg/contracts/ton/gateway_test.go @@ -0,0 +1,313 @@ +package ton + +import ( + "embed" + "encoding/json" + "fmt" + "strings" + "testing" + + "github.com/samber/lo" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/tlb" + "github.com/tonkeeper/tongo/ton" +) + +func TestParsing(t *testing.T) { + swapBodyAndParse := func(gw *Gateway, tx ton.Transaction, body *boc.Cell) *Transaction { + tx.Msgs.InMsg.Value.Value.Body.Value = tlb.Any(*body) + + parsed, err := gw.ParseTransaction(tx) + require.NoError(t, err) + + return parsed + } + + t.Run("Donate", func(t *testing.T) { + // ARRANGE + // Given a tx + tx, fx := getFixtureTX(t, "00-donation") + + // Given a gateway contract + gw := NewGateway(ton.MustParseAccountID(fx.Account)) + + // ACT + parsedTX, err := gw.ParseTransaction(tx) + + // ASSERT + require.NoError(t, err) + + assert.Equal(t, int(OpDonate), int(parsedTX.Operation)) + assert.Equal(t, true, parsedTX.IsInbound()) + + const ( + expectedSender = "0:9594c719ec4c95f66683b2fb1ca0b09de4a41f6fb087ba4c8d265b96a4cce50f" + expectedDonation = 1_499_432_947 // 1.49... TON + ) + + donation, err := parsedTX.Donation() + assert.NoError(t, err) + assert.Equal(t, expectedSender, donation.Sender.ToRaw()) + assert.Equal(t, expectedDonation, int(donation.Amount.Uint64())) + + // Check that AsBody works + var ( + parsedTX2 = swapBodyAndParse(gw, tx, lo.Must(donation.AsBody())) + donation2 = lo.Must(parsedTX2.Donation()) + ) + + assert.Equal(t, donation, donation2) + }) + + t.Run("Deposit", func(t *testing.T) { + // ARRANGE + // Given a tx + tx, fx := getFixtureTX(t, "01-deposit") + + // Given a gateway contract + gw := NewGateway(ton.MustParseAccountID(fx.Account)) + + // ACT + parsedTX, err := gw.ParseTransaction(tx) + + // ASSERT + require.NoError(t, err) + + // Check tx props + assert.Equal(t, int(OpDeposit), int(parsedTX.Operation)) + + // Check deposit + deposit, err := parsedTX.Deposit() + assert.NoError(t, err) + + const ( + expectedSender = "0:9594c719ec4c95f66683b2fb1ca0b09de4a41f6fb087ba4c8d265b96a4cce50f" + vitalikDotETH = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" + expectedDeposit = 990_000_000 // 0.99 TON + ) + + assert.Equal(t, expectedSender, deposit.Sender.ToRaw()) + assert.Equal(t, expectedDeposit, int(deposit.Amount.Uint64())) + assert.Equal(t, vitalikDotETH, deposit.Recipient.Hex()) + + // Check that other casting fails + _, err = parsedTX.Donation() + assert.ErrorIs(t, err, ErrCast) + + // Check that AsBody works + var ( + parsedTX2 = swapBodyAndParse(gw, tx, lo.Must(deposit.AsBody())) + deposit2 = lo.Must(parsedTX2.Deposit()) + ) + + assert.Equal(t, deposit, deposit2) + }) + + t.Run("Deposit and call", func(t *testing.T) { + // ARRANGE + // Given a tx + tx, fx := getFixtureTX(t, "02-deposit-and-call") + + // Given a gateway contract + gw := NewGateway(ton.MustParseAccountID(fx.Account)) + + // ACT + parsedTX, err := gw.ParseTransaction(tx) + + // ASSERT + require.NoError(t, err) + + // Check tx props + assert.Equal(t, int(OpDepositAndCall), int(parsedTX.Operation)) + + // Check deposit and call + depositAndCall, err := parsedTX.DepositAndCall() + assert.NoError(t, err) + + const ( + expectedSender = "0:9594c719ec4c95f66683b2fb1ca0b09de4a41f6fb087ba4c8d265b96a4cce50f" + vitalikDotETH = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" + expectedDeposit = 490_000_000 // 0.49 TON + ) + + expectedCallData := readFixtureFile(t, "testdata/long-call-data.txt") + + assert.Equal(t, expectedSender, depositAndCall.Sender.ToRaw()) + assert.Equal(t, expectedDeposit, int(depositAndCall.Amount.Uint64())) + assert.Equal(t, vitalikDotETH, depositAndCall.Recipient.Hex()) + assert.Equal(t, expectedCallData, depositAndCall.CallData) + + // Check that AsBody works + var ( + parsedTX2 = swapBodyAndParse(gw, tx, lo.Must(depositAndCall.AsBody())) + depositAndCall2 = lo.Must(parsedTX2.DepositAndCall()) + ) + + assert.Equal(t, depositAndCall, depositAndCall2) + }) + + t.Run("Irrelevant tx", func(t *testing.T) { + t.Run("Failed tx", func(t *testing.T) { + // ARRANGE + // Given a tx + tx, fx := getFixtureTX(t, "03-failed-tx") + + // Given a gateway contract + gw := NewGateway(ton.MustParseAccountID(fx.Account)) + + // ACT + _, err := gw.ParseTransaction(tx) + + assert.ErrorIs(t, err, ErrParse) + + // 102 is 'unknown op' + // https://github.com/zeta-chain/protocol-contracts-ton/blob/main/contracts/common/errors.fc + assert.ErrorContains(t, err, "is not successful (exit code 102)") + }) + + t.Run("not a deposit nor withdrawal", func(t *testing.T) { + // actually, it's a bounce of the previous tx + + // ARRANGE + // Given a tx + tx, fx := getFixtureTX(t, "04-bounced-msg") + + // Given a gateway contract + gw := NewGateway(ton.MustParseAccountID(fx.Account)) + + // ACT + _, err := gw.ParseTransaction(tx) + assert.Error(t, err) + }) + }) +} + +func TestFiltering(t *testing.T) { + t.Run("Inbound", func(t *testing.T) { + for _, tt := range []struct { + name string + skip bool + error bool + }{ + // Should be parsed and filtered + {"00-donation", false, false}, + {"01-deposit", false, false}, + {"02-deposit-and-call", false, false}, + + // Should be skipped + {"03-failed-tx", true, false}, + {"04-bounced-msg", true, false}, + } { + t.Run(tt.name, func(t *testing.T) { + // ARRANGE + // Given a tx + tx, fx := getFixtureTX(t, tt.name) + + // Given a gateway + gw := NewGateway(ton.MustParseAccountID(fx.Account)) + + // ACT + parsedTX, skip, err := gw.ParseAndFilter(tx, FilterInbounds) + + if tt.error { + require.Error(t, err) + assert.False(t, skip) + assert.Nil(t, parsedTX) + return + } + + require.NoError(t, err) + assert.Equal(t, tt.skip, skip) + + if tt.skip { + assert.Nil(t, parsedTX) + return + } + + assert.NotNil(t, parsedTX) + }) + } + }) +} + +func TestFixtures(t *testing.T) { + // ACT + tx, _ := getFixtureTX(t, "01-deposit") + + // ASSERT + require.Equal(t, uint64(26023788000003), tx.Lt) + require.Equal(t, "cbd6e2261334d08120e2fef428ecbb4e7773606ced878d0e6da204f2b4bf42bf", tx.Hash().Hex()) +} + +func TestSnakeData(t *testing.T) { + for _, tt := range []string{ + "Hello world", + "123", + strings.Repeat(`ZetaChain `, 300), + string(readFixtureFile(t, "testdata/long-call-data.txt")), + } { + a := []byte(tt) + + cell, err := MarshalSnakeCell(a) + require.NoError(t, err) + + b, err := UnmarshalSnakeCell(cell) + require.NoError(t, err) + + t.Logf(string(b)) + + assert.Equal(t, a, b, tt) + } +} + +//go:embed testdata +var fixtures embed.FS + +type fixture struct { + Account string `json:"account"` + BOC string `json:"boc"` + Description string `json:"description"` + Hash string `json:"hash"` + LogicalTime uint64 `json:"logicalTime"` + Test bool `json:"test"` +} + +// testdata/$name.json tx +func getFixtureTX(t *testing.T, name string) (ton.Transaction, fixture) { + t.Helper() + + var ( + filename = fmt.Sprintf("testdata/%s.json", name) + b = readFixtureFile(t, filename) + ) + + // bag of cells + var fx fixture + + require.NoError(t, json.Unmarshal(b, &fx)) + + cells, err := boc.DeserializeBocHex(fx.BOC) + require.NoError(t, err) + require.Len(t, cells, 1) + + cell := cells[0] + + var tx ton.Transaction + + require.NoError(t, tx.UnmarshalTLB(cell, &tlb.Decoder{})) + + t.Logf("Loaded fixture %s\n%s", filename, fx.Description) + + return tx, fx +} + +func readFixtureFile(t *testing.T, filename string) []byte { + t.Helper() + + b, err := fixtures.ReadFile(filename) + require.NoError(t, err, filename) + + return b +} diff --git a/pkg/contracts/ton/gateway_tx.go b/pkg/contracts/ton/gateway_tx.go new file mode 100644 index 0000000000..75c12c8eff --- /dev/null +++ b/pkg/contracts/ton/gateway_tx.go @@ -0,0 +1,51 @@ +package ton + +import ( + "cosmossdk.io/errors" + "cosmossdk.io/math" + "github.com/tonkeeper/tongo/ton" +) + +// Transaction represents a Gateway transaction. +type Transaction struct { + ton.Transaction + Operation Op + + content any + inbound bool +} + +// IsInbound returns true if the transaction is inbound. +func (tx *Transaction) IsInbound() bool { + return tx.inbound +} + +// GasUsed returns the amount of gas used by the transaction. +func (tx *Transaction) GasUsed() math.Uint { + return math.NewUint(uint64(tx.TotalFees.Grams)) +} + +// Donation casts the transaction content to a Donation. +func (tx *Transaction) Donation() (Donation, error) { + return retrieveContent[Donation](tx) +} + +// Deposit casts the transaction content to a Deposit. +func (tx *Transaction) Deposit() (Deposit, error) { + return retrieveContent[Deposit](tx) +} + +// DepositAndCall casts the transaction content to a DepositAndCall. +func (tx *Transaction) DepositAndCall() (DepositAndCall, error) { + return retrieveContent[DepositAndCall](tx) +} + +func retrieveContent[T any](tx *Transaction) (T, error) { + typed, ok := tx.content.(T) + if !ok { + var tt T + return tt, errors.Wrapf(ErrCast, "not a %T (op %d)", tt, int(tx.Operation)) + } + + return typed, nil +} diff --git a/pkg/contracts/ton/testdata/00-donation.json b/pkg/contracts/ton/testdata/00-donation.json new file mode 100644 index 0000000000..867f096a90 --- /dev/null +++ b/pkg/contracts/ton/testdata/00-donation.json @@ -0,0 +1,8 @@ +{ + "account": "0:997d889c815aeac21c47f86ae0e38383efc3c3463067582f6263ad48c5a1485b", + "boc": "b5ee9c72010207010001a10003b57997d889c815aeac21c47f86ae0e38383efc3c3463067582f6263ad48c5a1485b000017d46c458143cbd6e2261334d08120e2fef428ecbb4e7773606ced878d0e6da204f2b4bf42bf000017ab22a3b30366f17efd000146114e1a80102030101a00400827213c7e41677dcade29f2b424cdad8712132fbd5465b37df2e1763369e2fb12da0a7b38b0f8722a81351a279e9d229e4f5d92a5d91aed6c197ab4b5ef756b042cc021b04c0731749165a0bc01860db5611050600c968012b298e33d8992beccd0765f63941613bc9483edf610f74991a4cb72d4999ca1f00265f62272056bab08711fe1ab838e0e0fbf0f0d18c19d60bd898eb5231685216d165a0bc000608235a00002fa8d88b0284cde2fdfa00000032000000000000000040009e408c6c3d090000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005bc00000000000000000000000012d452da449e50b8cf7dd27861f146122afe1b546bb8b70fc8216f0c614139f8e04", + "description": "Sample donation to gw contract. https://testnet.tonviewer.com/transaction/d9339c9e78a55ee9ea0cd46cab798926c139db2a7c17a002041c3db90a80d5ea", + "hash": "d9339c9e78a55ee9ea0cd46cab798926c139db2a7c17a002041c3db90a80d5ea", + "logicalTime": 26201117000003, + "test": true +} \ No newline at end of file diff --git a/pkg/contracts/ton/testdata/01-deposit.json b/pkg/contracts/ton/testdata/01-deposit.json new file mode 100644 index 0000000000..d22170ddf0 --- /dev/null +++ b/pkg/contracts/ton/testdata/01-deposit.json @@ -0,0 +1,8 @@ +{ + "account": "0:997d889c815aeac21c47f86ae0e38383efc3c3463067582f6263ad48c5a1485b", + "boc": "b5ee9c7201020a0100023d0003b57997d889c815aeac21c47f86ae0e38383efc3c3463067582f6263ad48c5a1485b000017ab22a3b3031b48df020aa3647a59163a25772d81991916a2bf523b771d89deec9e5be15d58000017ab20f8740366ead1e30003465b1d0080102030201e004050082723e346a6461f48fab691e87d9fd5954595eb15351fa1c536486a863d9708b79c313c7e41677dcade29f2b424cdad8712132fbd5465b37df2e1763369e2fb12da0021904222490ee6b28018646dca110080900f168012b298e33d8992beccd0765f63941613bc9483edf610f74991a4cb72d4999ca1f00265f62272056bab08711fe1ab838e0e0fbf0f0d18c19d60bd898eb5231685216d0ee6b28000608235a00002f5645476604cdd5a3c60000003280000000000000006c6d35f934b257cebf76cf01f29a0ae9bd54b022c00101df06015de004cbec44e40ad75610e23fc357071c1c1f7e1e1a31833ac17b131d6a462d0a42d800002f5645476608cdd5a3c6c007008b0000006500000000000000008012b298e33d8992beccd0765f63941613bc9483edf610f74991a4cb72d4999ca1e876046701b1b4d7e4d2c95f3afddb3c07ca682ba6f552c08b009e42d5ac3d090000000000000000007e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006fc98510184c2880c0000000000002000000000003da7f47a5d1898330bd18801617ae23a388cbaf527c312921718ef36ce9cf8c4e40901d04", + "description": "Sample deposit to gw contract. https://testnet.tonviewer.com/transaction/cbd6e2261334d08120e2fef428ecbb4e7773606ced878d0e6da204f2b4bf42bf", + "hash": "cbd6e2261334d08120e2fef428ecbb4e7773606ced878d0e6da204f2b4bf42bf", + "logicalTime": 26023788000003, + "test": true +} diff --git a/pkg/contracts/ton/testdata/02-deposit-and-call.json b/pkg/contracts/ton/testdata/02-deposit-and-call.json new file mode 100644 index 0000000000..60f824a828 --- /dev/null +++ b/pkg/contracts/ton/testdata/02-deposit-and-call.json @@ -0,0 +1,8 @@ +{ + "account": "0:997d889c815aeac21c47f86ae0e38383efc3c3463067582f6263ad48c5a1485b", + "boc": "b5ee9c7201021a01000a040003b57997d889c815aeac21c47f86ae0e38383efc3c3463067582f6263ad48c5a1485b000017d4acf14a83d9339c9e78a55ee9ea0cd46cab798926c139db2a7c17a002041c3db90a80d5ea000017d46c45814366f1896b000347487d2080102030201e00405008272a7b38b0f8722a81351a279e9d229e4f5d92a5d91aed6c197ab4b5ef756b042ccdb7075fb2e3f1dc397aa3c93d8dd352cbe54af9fb033df63082875f03a70947102190480b409077359401866309211181901f168012b298e33d8992beccd0765f63941613bc9483edf610f74991a4cb72d4999ca1f00265f62272056bab08711fe1ab838e0e0fbf0f0d18c19d60bd898eb5231685216d077359400069397a000002fa959e29504cde312d60000003300000000000000006c6d35f934b257cebf76cf01f29a0ae9bd54b022c0080101df06015de004cbec44e40ad75610e23fc357071c1c1f7e1e1a31833ac17b131d6a462d0a42d800002fa959e29508cde312d6c007018b0000006600000000000000008012b298e33d8992beccd0765f63941613bc9483edf610f74991a4cb72d4999ca1e83a699d01b1b4d7e4d2c95f3afddb3c07ca682ba6f552c08b0801fe57686174206973204c6f72656d20497073756d3f2028617070726f782032204b696c6f6279746573290a0a4c6f72656d20497073756d2069732073696d706c792064756d6d792074657874206f6620746865207072696e74696e6720616e64207479706573657474696e6720696e6475737472792e0a4c6f72656d204970730901fe756d20686173206265656e2074686520696e6475737472792773207374616e646172642064756d6d79207465787420657665722073696e6365207468652031353030732c207768656e20616e20756e6b6e6f776e207072696e74657220746f6f6b0a612067616c6c6579206f66207479706520616e6420736372616d626c650a01fe6420697420746f206d616b65206120747970652073706563696d656e20626f6f6b2e20497420686173207375727669766564206e6f74206f6e6c7920666976652063656e7475726965732c0a62757420616c736f20746865206c65617020696e746f20656c656374726f6e6963207479706573657474696e672c2072656d610b01fe696e696e6720657373656e7469616c6c7920756e6368616e6765642e0a0a49742077617320706f70756c61726973656420696e207468652031393630732077697468207468652072656c65617365206f66204c657472617365742073686565747320636f6e7461696e696e67204c6f72656d20497073756d207061737361670c01fe65732c0a616e64206d6f726520726563656e746c792077697468206465736b746f70207075626c697368696e6720736f667477617265206c696b6520416c64757320506167654d616b657220696e636c7564696e672076657273696f6e73206f66204c6f72656d20497073756d2e0a0a57687920646f2077652075736520690d01fe743f0a0a49742069732061206c6f6e672d65737461626c6973686564206661637420746861742061207265616465722077696c6c206265206469737472616374656420627920746865207265616461626c6520636f6e74656e74206f6620612070616765207768656e0a6c6f6f6b696e6720617420697473206c61796f75740e01fe2e2054686520706f696e74206f66207573696e67204c6f72656d20497073756d2069732074686174206974206861732061206d6f72652d6f722d6c657373206e6f726d616c20646973747269627574696f6e206f66206c6574746572732c0a6173206f70706f73656420746f207573696e672027436f6e74656e74206865720f01fe652c20636f6e74656e742068657265272c206d616b696e67206974206c6f6f6b206c696b65207265616461626c6520456e676c6973682e204d616e79206465736b746f70207075626c697368696e670a7061636b6167657320616e6420776562207061676520656469746f7273206e6f7720757365204c6f72656d204970731001fe756d2061732074686569722064656661756c74206d6f64656c20746578742c20616e6420612073656172636820666f7220276c6f72656d20697073756d270a77696c6c20756e636f766572206d616e7920776562207369746573207374696c6c20696e20746865697220696e66616e63792e20566172696f757320766572731101fe696f6e7320686176652065766f6c766564206f766572207468652079656172732c20736f6d6574696d65730a6279206163636964656e742c20736f6d6574696d6573206f6e20707572706f73652028696e6a65637465642068756d6f757220616e6420746865206c696b65292e0a0a576865726520646f657320697420636f1201fe6d652066726f6d3f0a0a436f6e747261727920746f20706f70756c61722062656c6965662c204c6f72656d20497073756d206973206e6f742073696d706c792072616e646f6d20746578742e2049742068617320726f6f747320696e2061207069656365206f6620636c6173736963616c0a4c6174696e206c6974657261741301fe7572652066726f6d2034352042432c206d616b696e67206974206f7665722032303030207965617273206f6c642e2052696368617264204d63436c696e746f636b2c2061204c6174696e2070726f666573736f720a61742048616d7064656e2d5379646e657920436f6c6c65676520696e2056697267696e69612c206c6f6f1401fe6b6564207570206f6e65206f6620746865206d6f7265206f627363757265204c6174696e20776f7264732c20636f6e73656374657475722c0a66726f6d2061204c6f72656d20497073756d20706173736167652c20616e6420676f696e67207468726f75676820746865206369746573206f662074686520776f726420696e1501fe20636c6173736963616c206c6974657261747572652c20646973636f76657265640a74686520756e646f75627461626c6520736f757263652e204c6f72656d20497073756d20636f6d65732066726f6d2073656374696f6e7320312e31302e333220616e6420312e31302e3333206f66202264652046696e6962757320426f1601fe6e6f72756d206574204d616c6f72756d220a285468652045787472656d6573206f6620476f6f6420616e64204576696c292062792043696365726f2c207772697474656e20696e2034352042432e205468697320626f6f6b2069732061207472656174697365206f6e20746865207468656f7279206f66206574686963732c17004a0a7665727920706f70756c617220647572696e67207468652052656e61697373616e63652e009e43f62c3d090000000000000000009b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006fc9b95b984dcadcc0000000000002000000000003fa4c79ea2219e757506c681b3ab938299662dccbcef3f334edcddee1cd13bade44920284", + "description": "Sample deposit-and-call to gw contract. https://testnet.tonviewer.com/transaction/3647f17cc28e4a70404a10c62ad6262fbf67aa72579acde449d66cc0d0fd7ca8", + "hash": "3647f17cc28e4a70404a10c62ad6262fbf67aa72579acde449d66cc0d0fd7ca8", + "logicalTime": 26202202000003, + "test": true +} \ No newline at end of file diff --git a/pkg/contracts/ton/testdata/03-failed-tx.json b/pkg/contracts/ton/testdata/03-failed-tx.json new file mode 100644 index 0000000000..0fa772b7fe --- /dev/null +++ b/pkg/contracts/ton/testdata/03-failed-tx.json @@ -0,0 +1,8 @@ +{ + "account": "0:997d889c815aeac21c47f86ae0e38383efc3c3463067582f6263ad48c5a1485b", + "boc": "b5ee9c72010208010001e90003b57997d889c815aeac21c47f86ae0e38383efc3c3463067582f6263ad48c5a1485b000017d5af81eb033647f17cc28e4a70404a10c62ad6262fbf67aa72579acde449d66cc0d0fd7ca8000017d4acf14a8366f1b2b00003461489a480102030201e00405008272db7075fb2e3f1dc397aa3c93d8dd352cbe54af9fb033df63082875f03a709471f41d1551f3ff76f22096fabf1806f354a5437a60a611df452f2a78e6dbd9adab01290482c7c9017d78401061061c0e0181046998208d6a0700cb68012b298e33d8992beccd0765f63941613bc9483edf610f74991a4cb72d4999ca1f00265f62272056bab08711fe1ab838e0e0fbf0f0d18c19d60bd898eb5231685216d017d784000608235a00002fab5f03d604cde365602432b6363796102bb7b9363210c00101df0600d3580132fb113902b5d584388ff0d5c1c70707df87868c60ceb05ec4c75a918b4290b700256531c67b13257d99a0ecbec7282c27792907dbec21ee93234996e5a9333943d0179e56800608235a00002fab5f03d608cde365607fffffffa432b6363796102bb7b9363210c0009e40a7cc0f424000000000cc0000002600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "description": "failed tx with body='Hello, World!'; https://testnet.tonviewer.com/transaction/653d37cfbff76585d336fb74a0eaa7fe6d1a2b3cae56d5e5f9609a821c9f1e45", + "hash": "653d37cfbff76585d336fb74a0eaa7fe6d1a2b3cae56d5e5f9609a821c9f1e45", + "logicalTime": 26206540000003, + "test": true +} diff --git a/pkg/contracts/ton/testdata/04-bounced-msg.json b/pkg/contracts/ton/testdata/04-bounced-msg.json new file mode 100644 index 0000000000..9887db3123 --- /dev/null +++ b/pkg/contracts/ton/testdata/04-bounced-msg.json @@ -0,0 +1,8 @@ +{ + "account": "0:9594c719ec4c95f66683b2fb1ca0b09de4a41f6fb087ba4c8d265b96a4cce50f", + "boc": "b5ee9c72010207010001a30003b579594c719ec4c95f66683b2fb1ca0b09de4a41f6fb087ba4c8d265b96a4cce50f000017d5af81eb05b4269279feefdc3ea4220465ec2bce20c6e7f0a8c09b51dac55b90aef3c342c6000017d5af81eb0166f1b2b0000146097f4080102030101a0040082727682d6ce21cbeaec70573e8d6ab7dc6357b875cbffc8c50608035fda3fe3bc378db05a075336855e0ae2db0067bf7de2341a87b9d555e968d64b216fe5491aba02150c090179e568186097f411050600d3580132fb113902b5d584388ff0d5c1c70707df87868c60ceb05ec4c75a918b4290b700256531c67b13257d99a0ecbec7282c27792907dbec21ee93234996e5a9333943d0179e56800608235a00002fab5f03d608cde365607fffffffa432b6363796102bb7b9363210c0009e40614c0f1da800000000000000001700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005bc00000000000000000000000012d452da449e50b8cf7dd27861f146122afe1b546bb8b70fc8216f0c614139f8e04", + "description": "Sample bounced message. This address is not even a gw. https://testnet.tonviewer.com/transaction/b3c46f5faf8aee7348083e7adbbc9a60ab1c8e0eac09133d64e2c4eb831e607b", + "hash": "b3c46f5faf8aee7348083e7adbbc9a60ab1c8e0eac09133d64e2c4eb831e607b", + "logicalTime": 26206540000005, + "test": true +} diff --git a/pkg/contracts/ton/testdata/long-call-data.txt b/pkg/contracts/ton/testdata/long-call-data.txt new file mode 100644 index 0000000000..d66c4d103a --- /dev/null +++ b/pkg/contracts/ton/testdata/long-call-data.txt @@ -0,0 +1,28 @@ +What is Lorem Ipsum? (approx 2 Kilobytes) + +Lorem Ipsum is simply dummy text of the printing and typesetting industry. +Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took +a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, +but also the leap into electronic typesetting, remaining essentially unchanged. + +It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, +and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. + +Why do we use it? + +It is a long-established fact that a reader will be distracted by the readable content of a page when +looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, +as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing +packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' +will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes +by accident, sometimes on purpose (injected humour and the like). + +Where does it come from? + +Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical +Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor +at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, +from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered +the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" +(The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, +very popular during the Renaissance. \ No newline at end of file diff --git a/pkg/contracts/ton/testdata/readme.md b/pkg/contracts/ton/testdata/readme.md new file mode 100644 index 0000000000..8695f06982 --- /dev/null +++ b/pkg/contracts/ton/testdata/readme.md @@ -0,0 +1,29 @@ +# TON transaction scraper + +`scraper.go` represents a handy tool that allows to fetch transactions from TON blockchain +for further usage in test cases. + +`go run pkg/contracts/ton/testdata/scraper.go
[--testnet]` + +## Example usage + +```sh +go run pkg/contracts/ton/testdata/scraper.go -testnet \ + kQCZfYicgVrqwhxH-Grg44OD78PDRjBnWC9iY61IxaFIW77M \ + 26023788000003 \ + cbd6e2261334d08120e2fef428ecbb4e7773606ced878d0e6da204f2b4bf42bf | jq +``` + +Returns + +```json +{ + "account": "0:997d889c815aeac21c47f86ae0e38383efc3c3463067582f6263ad48c5a1485b", + "boc": "b5ee9c7201020a0100023d0003b57997d889c815aeac21c47f86ae0e38383efc3c3463067582f6263ad48c5a1485b000017ab22a3b3031b48df020aa3647a59163a25772d81991916a2bf523b771d89deec9e5be15d58000017ab20f8740366ead1e30003465b1d0080102030201e004050082723e346a6461f48fab691e87d9fd5954595eb15351fa1c536486a863d9708b79c313c7e41677dcade29f2b424cdad8712132fbd5465b37df2e1763369e2fb12da0021904222490ee6b28018646dca110080900f168012b298e33d8992beccd0765f63941613bc9483edf610f74991a4cb72d4999ca1f00265f62272056bab08711fe1ab838e0e0fbf0f0d18c19d60bd898eb5231685216d0ee6b28000608235a00002f5645476604cdd5a3c60000003280000000000000006c6d35f934b257cebf76cf01f29a0ae9bd54b022c00101df06015de004cbec44e40ad75610e23fc357071c1c1f7e1e1a31833ac17b131d6a462d0a42d800002f5645476608cdd5a3c6c007008b0000006500000000000000008012b298e33d8992beccd0765f63941613bc9483edf610f74991a4cb72d4999ca1e876046701b1b4d7e4d2c95f3afddb3c07ca682ba6f552c08b009e42d5ac3d090000000000000000007e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006fc98510184c2880c0000000000002000000000003da7f47a5d1898330bd18801617ae23a388cbaf527c312921718ef36ce9cf8c4e40901d04", + "description": "Lorem Ipsum", + "hash": "cbd6e2261334d08120e2fef428ecbb4e7773606ced878d0e6da204f2b4bf42bf", + "logicalTime": 26023788000003, + "test": true +} +``` + diff --git a/pkg/contracts/ton/testdata/scraper.go b/pkg/contracts/ton/testdata/scraper.go new file mode 100644 index 0000000000..efe76ca3aa --- /dev/null +++ b/pkg/contracts/ton/testdata/scraper.go @@ -0,0 +1,131 @@ +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "log" + "strconv" + + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/liteapi" + "github.com/tonkeeper/tongo/tlb" + "github.com/tonkeeper/tongo/ton" +) + +func main() { + var testnet bool + + flag.BoolVar(&testnet, "testnet", false, "Use testnet network") + flag.Parse() + + if len(flag.Args()) < 3 { + log.Fatalf("Usage: go run scraper.go [-testnet] ") + } + + // Parse account + acc, err := ton.ParseAccountID(flag.Arg(0)) + must(err, "Unable to parse account") + + // Parse LT + lt, err := strconv.ParseUint(flag.Arg(1), 10, 64) + must(err, "Unable to parse logical time") + + // Parse hash + var hash ton.Bits256 + + must(hash.FromHex(flag.Arg(2)), "Unable to parse hash") + + ctx, client := context.Background(), getClient(testnet) + + state, err := client.GetAccountState(ctx, acc) + must(err, "Unable to get account state") + + if state.Account.Status() != tlb.AccountActive { + fail("account %s is not active", acc.ToRaw()) + } + + txs, err := client.GetTransactions(ctx, 1, acc, lt, hash) + must(err, "Unable to get transactions") + + switch { + case len(txs) == 0: + fail("Not found") + case len(txs) > 1: + fail("invalid tx list length (got %d, want 1); lt %d, hash %s", len(txs), lt, hash.Hex()) + } + + // Print the transaction + tx := txs[0] + + cell, err := transactionToCell(tx) + must(err, "unable to convert tx to cell") + + bocRaw, err := cell.MarshalJSON() + must(err, "unable to marshal cell to JSON") + + printAny(map[string]any{ + "test": testnet, + "account": acc.ToRaw(), + "description": "todo", + "logicalTime": lt, + "hash": hash.Hex(), + "boc": json.RawMessage(bocRaw), + }) +} + +func getClient(testnet bool) *liteapi.Client { + if testnet { + c, err := liteapi.NewClientWithDefaultTestnet() + must(err, "unable to create testnet lite client") + + return c + } + + c, err := liteapi.NewClientWithDefaultMainnet() + must(err, "unable to create mainnet lite client") + + return c +} + +func printAny(v any) { + b, err := json.MarshalIndent(v, "", " ") + must(err, "unable marshal data") + + fmt.Println(string(b)) +} + +func transactionToCell(tx ton.Transaction) (*boc.Cell, error) { + b, err := tx.SourceBoc() + if err != nil { + return nil, err + } + + cells, err := boc.DeserializeBoc(b) + if err != nil { + return nil, err + } + + if len(cells) != 1 { + return nil, fmt.Errorf("invalid cell count: %d", len(cells)) + } + + return cells[0], nil +} + +func must(err error, msg string) { + if err == nil { + return + } + + if msg == "" { + log.Fatalf("Error: %s", err.Error()) + } + + log.Fatalf("%s; error: %s", msg, err.Error()) +} + +func fail(msg string, args ...any) { + must(fmt.Errorf(msg, args...), "FAIL") +} diff --git a/pkg/contracts/ton/tlb.go b/pkg/contracts/ton/tlb.go new file mode 100644 index 0000000000..0ee3aea98a --- /dev/null +++ b/pkg/contracts/ton/tlb.go @@ -0,0 +1,79 @@ +package ton + +import ( + "bytes" + + "cosmossdk.io/math" + eth "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/tlb" +) + +// MarshalTLB encodes entity to BOC +func MarshalTLB(v tlb.MarshalerTLB) (*boc.Cell, error) { + cell := boc.NewCell() + + if err := v.MarshalTLB(cell, &tlb.Encoder{}); err != nil { + return nil, err + } + + return cell, nil +} + +// UnmarshalTLB decodes entity from BOC +func UnmarshalTLB(t tlb.UnmarshalerTLB, cell *boc.Cell) error { + return t.UnmarshalTLB(cell, &tlb.Decoder{}) +} + +// UnmarshalSnakeCell decodes TLB cell to []byte using snake-cell encoding +func UnmarshalSnakeCell(cell *boc.Cell) ([]byte, error) { + var sd tlb.SnakeData + + if err := UnmarshalTLB(&sd, cell); err != nil { + return nil, err + } + + cd := boc.BitString(sd) + + // TLB operates with bits, so we (might) need to trim some "leftovers" (null chars) + return bytes.Trim(cd.Buffer(), "\x00"), nil +} + +// MarshalSnakeCell encodes []byte to TLB using snake-cell encoding +func MarshalSnakeCell(data []byte) (*boc.Cell, error) { + b := boc.NewCell() + + wrapped := tlb.Bytes(data) + if err := wrapped.MarshalTLB(b, &tlb.Encoder{}); err != nil { + return nil, err + } + + return b, nil +} + +// UnmarshalEVMAddress decodes eth.Address from BOC +func UnmarshalEVMAddress(cell *boc.Cell) (eth.Address, error) { + const evmAddrBits = 20 * 8 + + s, err := cell.ReadBits(evmAddrBits) + if err != nil { + return eth.Address{}, err + } + + return eth.BytesToAddress(s.Buffer()), nil +} + +func GramsToUint(g tlb.Grams) math.Uint { + return math.NewUint(uint64(g)) +} + +func ErrCollect(errs ...error) error { + for i, err := range errs { + if err != nil { + return errors.Wrapf(err, "error at index %d", i) + } + } + + return nil +} diff --git a/pkg/math/bits/bits.go b/pkg/math/bits/bits.go new file mode 100644 index 0000000000..a7a7865793 --- /dev/null +++ b/pkg/math/bits/bits.go @@ -0,0 +1,56 @@ +package math + +import ( + "math/bits" +) + +// SetBit sets the bit at the given position (0-7) in the byte to 1 +func SetBit(b *byte, position uint8) { + if position > 7 { + return + } + + // Example: given b = 0b00000000 and position = 3 + // step-1: shift value 1 to left by 3 times: 1 << 3 = 0b00001000 + // step-2: make an OR operation with original byte to set the bit: 0b00000000 | 0b00001000 = 0b00001000 + *b |= 1 << position +} + +// IsBitSet returns true if the bit at the given position (0-7) is set in the byte, false otherwise +func IsBitSet(b byte, position uint8) bool { + if position > 7 { + return false + } + bitMask := byte(1 << position) + return b&bitMask != 0 +} + +// GetBits extracts the value of bits for a given mask +// +// Example: given b = 0b11011001 and mask = 0b11100000, the function returns 0b110 +func GetBits(b byte, mask byte) byte { + extracted := b & mask + + // get the number of trailing zero bits + trailingZeros := bits.TrailingZeros8(mask) + + // remove trailing zeros + return extracted >> trailingZeros +} + +// SetBits sets the value to the bits specified in the mask +// +// Example: given b = 0b00100001 and mask = 0b11100000, and value = 0b110, the function returns 0b11000001 +func SetBits(b byte, mask byte, value byte) byte { + // get the number of trailing zero bits in the mask + trailingZeros := bits.TrailingZeros8(mask) + + // shift the value left by the number of trailing zeros + valueShifted := value << trailingZeros + + // clear the bits in 'b' that correspond to the mask + bCleared := b &^ mask + + // Set the bits by ORing the cleared 'b' with the shifted value + return bCleared | valueShifted +} diff --git a/pkg/math/bits/bits_test.go b/pkg/math/bits/bits_test.go new file mode 100644 index 0000000000..65632d0a24 --- /dev/null +++ b/pkg/math/bits/bits_test.go @@ -0,0 +1,171 @@ +package math_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + zetabits "github.com/zeta-chain/node/pkg/math/bits" +) + +func TestSetBit(t *testing.T) { + tests := []struct { + name string + initial byte + position uint8 + expected byte + }{ + { + name: "set bit at position 0", + initial: 0b00001000, + position: 0, + expected: 0b00001001, + }, + { + name: "set bit at position 7", + initial: 0b00001000, + position: 7, + expected: 0b10001000, + }, + { + name: "out of range bit position (no effect)", + initial: 0b00000000, + position: 8, // Out of range + expected: 0b00000000, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b := tt.initial + zetabits.SetBit(&b, tt.position) + require.Equal(t, tt.expected, b) + }) + } +} + +func TestIsBitSet(t *testing.T) { + tests := []struct { + name string + b byte + position uint8 + expected bool + }{ + { + name: "bit 0 set", + b: 0b00000001, + position: 0, + expected: true, + }, + { + name: "bit 7 set", + b: 0b10000000, + position: 7, + expected: true, + }, + { + name: "bit 2 not set", + b: 0b00000001, + position: 2, + expected: false, + }, + { + name: "bit out of range", + b: 0b00000001, + position: 8, // Position out of range + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := zetabits.IsBitSet(tt.b, tt.position) + require.Equal(t, tt.expected, result) + }) + } +} + +func TestGetBits(t *testing.T) { + tests := []struct { + name string + b byte + mask byte + expected byte + }{ + { + name: "extract upper 3 bits", + b: 0b11011001, + mask: 0b11100000, + expected: 0b110, + }, + { + name: "extract middle 3 bits", + b: 0b11011001, + mask: 0b00011100, + expected: 0b110, + }, + { + name: "extract lower 3 bits", + b: 0b11011001, + mask: 0b00000111, + expected: 0b001, + }, + { + name: "extract no bits", + b: 0b11011001, + mask: 0b00000000, + expected: 0b000, + }, + { + name: "extract all bits", + b: 0b11111111, + mask: 0b11111111, + expected: 0b11111111, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := zetabits.GetBits(tt.b, tt.mask) + require.Equal(t, tt.expected, result) + }) + } +} + +func TestSetBits(t *testing.T) { + tests := []struct { + name string + b byte + mask byte + value byte + expected byte + }{ + { + name: "set upper 3 bits", + b: 0b00100001, + mask: 0b11100000, + value: 0b110, + expected: 0b11000001, + }, + { + name: "set middle 3 bits", + b: 0b00100001, + mask: 0b00011100, + value: 0b101, + expected: 0b00110101, + }, + { + name: "set lower 3 bits", + b: 0b11111100, + mask: 0b00000111, + value: 0b101, + expected: 0b11111101, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := zetabits.SetBits(tt.b, tt.mask, tt.value) + require.Equal(t, tt.expected, result) + }) + } +} diff --git a/pkg/memo/arg.go b/pkg/memo/arg.go new file mode 100644 index 0000000000..e29615f562 --- /dev/null +++ b/pkg/memo/arg.go @@ -0,0 +1,52 @@ +package memo + +// ArgType is the enum for types supported by the codec +type ArgType string + +// Define all the types supported by the codec +const ( + ArgTypeBytes ArgType = "bytes" + ArgTypeString ArgType = "string" + ArgTypeAddress ArgType = "address" +) + +// CodecArg represents a codec argument +type CodecArg struct { + Name string + Type ArgType + Arg interface{} +} + +// NewArg create a new codec argument +func NewArg(name string, argType ArgType, arg interface{}) CodecArg { + return CodecArg{ + Name: name, + Type: argType, + Arg: arg, + } +} + +// ArgReceiver wraps the receiver address in a CodecArg +func ArgReceiver(arg interface{}) CodecArg { + return NewArg("receiver", ArgTypeAddress, arg) +} + +// ArgPayload wraps the payload in a CodecArg +func ArgPayload(arg interface{}) CodecArg { + return NewArg("payload", ArgTypeBytes, arg) +} + +// ArgRevertAddress wraps the revert address in a CodecArg +func ArgRevertAddress(arg interface{}) CodecArg { + return NewArg("revertAddress", ArgTypeString, arg) +} + +// ArgAbortAddress wraps the abort address in a CodecArg +func ArgAbortAddress(arg interface{}) CodecArg { + return NewArg("abortAddress", ArgTypeAddress, arg) +} + +// ArgRevertMessage wraps the revert message in a CodecArg +func ArgRevertMessage(arg interface{}) CodecArg { + return NewArg("revertMessage", ArgTypeBytes, arg) +} diff --git a/pkg/memo/arg_test.go b/pkg/memo/arg_test.go new file mode 100644 index 0000000000..ee9373e688 --- /dev/null +++ b/pkg/memo/arg_test.go @@ -0,0 +1,70 @@ +package memo_test + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/node/pkg/memo" +) + +func Test_NewArg(t *testing.T) { + argAddress := common.HexToAddress("0x0B85C56e5453e0f4273d1D1BF3091d43B08B38CE") + argString := "some other string argument" + argBytes := []byte("here is a bytes argument") + + tests := []struct { + name string + argType string + arg interface{} + }{ + { + name: "receiver", + argType: "address", + arg: &argAddress, + }, + { + name: "payload", + argType: "bytes", + arg: &argBytes, + }, + { + name: "revertAddress", + argType: "string", + arg: &argString, + }, + { + name: "abortAddress", + argType: "address", + arg: &argAddress, + }, + { + name: "revertMessage", + argType: "bytes", + arg: &argBytes, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + arg := memo.NewArg(tt.name, memo.ArgType(tt.argType), tt.arg) + + require.Equal(t, tt.name, arg.Name) + require.Equal(t, memo.ArgType(tt.argType), arg.Type) + require.Equal(t, tt.arg, arg.Arg) + + switch tt.name { + case "receiver": + require.Equal(t, arg, memo.ArgReceiver(&argAddress)) + case "payload": + require.Equal(t, arg, memo.ArgPayload(&argBytes)) + case "revertAddress": + require.Equal(t, arg, memo.ArgRevertAddress(&argString)) + case "abortAddress": + require.Equal(t, arg, memo.ArgAbortAddress(&argAddress)) + case "revertMessage": + require.Equal(t, arg, memo.ArgRevertMessage(&argBytes)) + } + }) + } +} diff --git a/pkg/memo/codec.go b/pkg/memo/codec.go new file mode 100644 index 0000000000..ea60fa24c1 --- /dev/null +++ b/pkg/memo/codec.go @@ -0,0 +1,64 @@ +package memo + +import ( + "fmt" +) + +type EncodingFormat uint8 + +// Enum for non-EVM chain memo encoding format (2 bits) +const ( + // EncodingFmtABI represents ABI encoding format + EncodingFmtABI EncodingFormat = 0b0000 + + // EncodingFmtCompactShort represents 'compact short' encoding format + EncodingFmtCompactShort EncodingFormat = 0b0001 + + // EncodingFmtCompactLong represents 'compact long' encoding format + EncodingFmtCompactLong EncodingFormat = 0b0010 + + // EncodingFmtInvalid represents invalid encoding format + EncodingFmtInvalid EncodingFormat = 0b0011 +) + +// Enum for length of bytes used to encode compact data +const ( + LenBytesShort = 1 + LenBytesLong = 2 +) + +// Codec is the interface for a codec +type Codec interface { + // AddArguments adds a list of arguments to the codec + AddArguments(args ...CodecArg) + + // PackArguments packs the arguments into the encoded data + PackArguments() ([]byte, error) + + // UnpackArguments unpacks the encoded data into the arguments + UnpackArguments(data []byte) error +} + +// GetLenBytes returns the number of bytes used to encode the length of the data +func GetLenBytes(encodingFmt EncodingFormat) (int, error) { + switch encodingFmt { + case EncodingFmtCompactShort: + return LenBytesShort, nil + case EncodingFmtCompactLong: + return LenBytesLong, nil + default: + return 0, fmt.Errorf("invalid compact encoding format %d", encodingFmt) + } +} + +// GetCodec returns the codec based on the encoding format +func GetCodec(encodingFmt EncodingFormat) (Codec, error) { + switch encodingFmt { + case EncodingFmtABI: + return NewCodecABI(), nil + case EncodingFmtCompactShort, EncodingFmtCompactLong: + return NewCodecCompact(encodingFmt) + default: + return nil, fmt.Errorf("invalid encoding format %d", encodingFmt) + } +} diff --git a/pkg/memo/codec_abi.go b/pkg/memo/codec_abi.go new file mode 100644 index 0000000000..708e1a6398 --- /dev/null +++ b/pkg/memo/codec_abi.go @@ -0,0 +1,94 @@ +package memo + +import ( + "fmt" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/pkg/errors" +) + +const ( + // selectorLength is the length of the selector in bytes + selectorLength = 4 + + // codecMethod is the name of the codec method + codecMethod = "codec" + + // codecMethodABIString is the ABI string template for codec method + codecMethodABIString = `[{"name":"codec", "inputs":[%s], "outputs":[%s], "type":"function"}]` +) + +var _ Codec = (*CodecABI)(nil) + +// CodecABI is a coder/decoder for ABI encoded memo fields +type CodecABI struct { + // abiTypes contains the ABI types of the arguments + abiTypes []string + + // abiArgs contains the ABI arguments to be packed or unpacked into + abiArgs []interface{} +} + +// NewCodecABI creates a new ABI codec +func NewCodecABI() *CodecABI { + return &CodecABI{ + abiTypes: make([]string, 0), + abiArgs: make([]interface{}, 0), + } +} + +// AddArguments adds a list of arguments to the codec +func (c *CodecABI) AddArguments(args ...CodecArg) { + for _, arg := range args { + typeJSON := fmt.Sprintf(`{"type":"%s"}`, arg.Type) + c.abiTypes = append(c.abiTypes, typeJSON) + c.abiArgs = append(c.abiArgs, arg.Arg) + } +} + +// PackArguments packs the arguments into the ABI encoded data +func (c *CodecABI) PackArguments() ([]byte, error) { + // get parsed ABI based on the inputs + parsedABI, err := c.parsedABI() + if err != nil { + return nil, errors.Wrap(err, "failed to parse ABI string") + } + + // pack the arguments + data, err := parsedABI.Pack(codecMethod, c.abiArgs...) + if err != nil { + return nil, errors.Wrap(err, "failed to pack ABI arguments") + } + + // this never happens + if len(data) < selectorLength { + return nil, errors.New("packed data less than selector length") + } + + return data[selectorLength:], nil +} + +// UnpackArguments unpacks the ABI encoded data into the output arguments +func (c *CodecABI) UnpackArguments(data []byte) error { + // get parsed ABI based on the inputs + parsedABI, err := c.parsedABI() + if err != nil { + return errors.Wrap(err, "failed to parse ABI string") + } + + // unpack data into outputs + err = parsedABI.UnpackIntoInterface(&c.abiArgs, codecMethod, data) + if err != nil { + return errors.Wrap(err, "failed to unpack ABI encoded data") + } + + return nil +} + +// parsedABI builds a parsed ABI based on the inputs +func (c *CodecABI) parsedABI() (abi.ABI, error) { + typeList := strings.Join(c.abiTypes, ",") + abiString := fmt.Sprintf(codecMethodABIString, typeList, typeList) + return abi.JSON(strings.NewReader(abiString)) +} diff --git a/pkg/memo/codec_abi_test.go b/pkg/memo/codec_abi_test.go new file mode 100644 index 0000000000..c4a2e1decd --- /dev/null +++ b/pkg/memo/codec_abi_test.go @@ -0,0 +1,345 @@ +package memo_test + +import ( + "bytes" + "encoding/binary" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/node/pkg/memo" +) + +const ( + // abiAlignment is the number of bytes used to align the ABI encoded data + abiAlignment = 32 +) + +// ABIPack is a helper function that simulates the abi.Pack function. +// Note: all arguments are assumed to be <= 32 bytes for simplicity. +func ABIPack(t *testing.T, args ...memo.CodecArg) []byte { + packedData := make([]byte, 0) + + // data offset for 1st dynamic-length field + offset := abiAlignment * len(args) + + // 1. pack 32-byte offset for each dynamic-length field (bytes, string) + // 2. pack actual data for each fixed-length field (address) + for _, arg := range args { + switch arg.Type { + case memo.ArgTypeBytes: + // left-pad length as uint16 + buff := make([]byte, 2) + binary.BigEndian.PutUint16(buff, uint16(offset)) + offsetData := abiPad32(t, buff, true) + packedData = append(packedData, offsetData...) + + argLen := len(arg.Arg.([]byte)) + if argLen > 0 { + offset += abiAlignment * 2 // [length + data] + } else { + offset += abiAlignment // only [length] + } + + case memo.ArgTypeString: + // left-pad length as uint16 + buff := make([]byte, 2) + binary.BigEndian.PutUint16(buff, uint16(offset)) + offsetData := abiPad32(t, buff, true) + packedData = append(packedData, offsetData...) + + argLen := len([]byte(arg.Arg.(string))) + if argLen > 0 { + offset += abiAlignment * 2 // [length + data] + } else { + offset += abiAlignment // only [length] + } + + case memo.ArgTypeAddress: // left-pad for address + data := abiPad32(t, arg.Arg.(common.Address).Bytes(), true) + packedData = append(packedData, data...) + } + } + + // pack dynamic-length fields + dynamicData := abiPackDynamicData(t, args...) + packedData = append(packedData, dynamicData...) + + return packedData +} + +// abiPad32 is a helper function to pad a byte slice to 32 bytes +func abiPad32(t *testing.T, data []byte, left bool) []byte { + // nothing needs to be encoded, return empty bytes + if len(data) == 0 { + return []byte{} + } + + require.LessOrEqual(t, len(data), abiAlignment) + padded := make([]byte, 32) + + if left { + // left-pad the data for fixed-size types + copy(padded[32-len(data):], data) + } else { + // right-pad the data for dynamic types + copy(padded, data) + } + return padded +} + +// abiPackDynamicData is a helper function to pack dynamic-length data +func abiPackDynamicData(t *testing.T, args ...memo.CodecArg) []byte { + packedData := make([]byte, 0) + + // pack with ABI format: length + data + for _, arg := range args { + // get length + var length int + switch arg.Type { + case memo.ArgTypeBytes: + length = len(arg.Arg.([]byte)) + case memo.ArgTypeString: + length = len([]byte(arg.Arg.(string))) + default: + continue + } + + // append length in bytes + lengthData := abiPad32(t, []byte{byte(length)}, true) + packedData = append(packedData, lengthData...) + + // append actual data in bytes + switch arg.Type { + case memo.ArgTypeBytes: // right-pad for bytes + data := abiPad32(t, arg.Arg.([]byte), false) + packedData = append(packedData, data...) + case memo.ArgTypeString: // right-pad for string + data := abiPad32(t, []byte(arg.Arg.(string)), false) + packedData = append(packedData, data...) + } + } + + return packedData +} + +// newArgInstance creates a new instance of the given argument type +func newArgInstance(v interface{}) interface{} { + switch v.(type) { + case common.Address: + return new(common.Address) + case []byte: + return &[]byte{} + case string: + return new(string) + } + return nil +} + +// ensureArgEquality ensures the expected argument and actual value are equal +func ensureArgEquality(t *testing.T, expected, actual interface{}) { + switch v := expected.(type) { + case common.Address: + require.Equal(t, v.Hex(), actual.(*common.Address).Hex()) + case []byte: + require.True(t, bytes.Equal(v, *actual.(*[]byte))) + case string: + require.Equal(t, v, *actual.(*string)) + default: + require.FailNow(t, "unexpected argument type", "Type: %T", v) + } +} + +func Test_NewCodecABI(t *testing.T) { + c := memo.NewCodecABI() + require.NotNil(t, c) +} + +func Test_CodecABI_AddArguments(t *testing.T) { + codec := memo.NewCodecABI() + require.NotNil(t, codec) + + address := common.HexToAddress("0xEf221eC80f004E6A2ee4E5F5d800699c1C68cD6F") + codec.AddArguments(memo.ArgReceiver(&address)) + + // attempt to pack the arguments, result should not be nil + packedData, err := codec.PackArguments() + require.NoError(t, err) + require.True(t, len(packedData) > 0) +} + +func Test_CodecABI_PackArgument(t *testing.T) { + // create sample arguments + argAddress := common.HexToAddress("0xEf221eC80f004E6A2ee4E5F5d800699c1C68cD6F") + argBytes := []byte("some test bytes argument") + argString := "some test string argument" + + // test cases + tests := []struct { + name string + args []memo.CodecArg + errMsg string + }{ + { + name: "pack in the order of [address, bytes, string]", + args: []memo.CodecArg{ + memo.ArgReceiver(argAddress), + memo.ArgPayload(argBytes), + memo.ArgRevertAddress(argString), + }, + }, + { + name: "pack in the order of [string, address, bytes]", + args: []memo.CodecArg{ + memo.ArgRevertAddress(argString), + memo.ArgReceiver(argAddress), + memo.ArgPayload(argBytes), + }, + }, + { + name: "pack empty bytes array and string", + args: []memo.CodecArg{ + memo.ArgPayload([]byte{}), + memo.ArgRevertAddress(""), + }, + }, + { + name: "unable to parse unsupported ABI type", + args: []memo.CodecArg{ + memo.ArgReceiver(&argAddress), + memo.NewArg("payload", memo.ArgType("unknown"), nil), + }, + errMsg: "failed to parse ABI string", + }, + { + name: "packing should fail on argument type mismatch", + args: []memo.CodecArg{ + memo.ArgReceiver(argBytes), // expect address type, but passed bytes + }, + errMsg: "failed to pack ABI arguments", + }, + } + + // loop through each test case + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // create a new ABI codec and add arguments + codec := memo.NewCodecABI() + codec.AddArguments(tc.args...) + + // pack arguments into ABI-encoded packedData + packedData, err := codec.PackArguments() + if tc.errMsg != "" { + require.ErrorContains(t, err, tc.errMsg) + require.Nil(t, packedData) + return + } + require.NoError(t, err) + + // calc expected data for comparison + expectedData := ABIPack(t, tc.args...) + + // validate the packed data + require.True(t, bytes.Equal(expectedData, packedData), "ABI encoded data mismatch") + }) + } +} + +func Test_CodecABI_UnpackArguments(t *testing.T) { + // create sample arguments + argAddress := common.HexToAddress("0xEf221eC80f004E6A2ee4E5F5d800699c1C68cD6F") + argBytes := []byte("some test bytes argument") + argString := "some test string argument" + + // test cases + tests := []struct { + name string + data []byte + expected []memo.CodecArg + errMsg string + }{ + { + name: "unpack in the order of [address, bytes, string]", + data: ABIPack(t, + memo.ArgReceiver(argAddress), + memo.ArgPayload(argBytes), + memo.ArgRevertAddress(argString)), + expected: []memo.CodecArg{ + memo.ArgReceiver(argAddress), + memo.ArgPayload(argBytes), + memo.ArgRevertAddress(argString), + }, + }, + { + name: "unpack in the order of [string, address, bytes]", + data: ABIPack(t, + memo.ArgRevertAddress(argString), + memo.ArgReceiver(argAddress), + memo.ArgPayload(argBytes)), + expected: []memo.CodecArg{ + memo.ArgRevertAddress(argString), + memo.ArgReceiver(argAddress), + memo.ArgPayload(argBytes), + }, + }, + { + name: "unpack empty bytes array and string", + data: ABIPack(t, + memo.ArgPayload([]byte{}), + memo.ArgRevertAddress("")), + expected: []memo.CodecArg{ + memo.ArgPayload([]byte{}), + memo.ArgRevertAddress(""), + }, + }, + { + name: "unable to parse unsupported ABI type", + data: []byte{}, + expected: []memo.CodecArg{ + memo.ArgReceiver(argAddress), + memo.NewArg("payload", memo.ArgType("unknown"), nil), + }, + errMsg: "failed to parse ABI string", + }, + { + name: "unpacking should fail on argument type mismatch", + data: ABIPack(t, + memo.ArgReceiver(argAddress), + ), + expected: []memo.CodecArg{ + memo.ArgReceiver(argBytes), // expect address type, but passed bytes + }, + errMsg: "failed to unpack ABI encoded data", + }, + } + + // loop through each test case + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // create a new ABI codec + codec := memo.NewCodecABI() + + // add output arguments + output := make([]memo.CodecArg, len(tc.expected)) + for i, arg := range tc.expected { + output[i] = memo.NewArg(arg.Name, arg.Type, newArgInstance(arg.Arg)) + } + codec.AddArguments(output...) + + // unpack arguments from ABI-encoded data + err := codec.UnpackArguments(tc.data) + + // validate the error message + if tc.errMsg != "" { + require.ErrorContains(t, err, tc.errMsg) + return + } + + // validate the unpacked arguments values + require.NoError(t, err) + for i, arg := range tc.expected { + ensureArgEquality(t, arg.Arg, output[i].Arg) + } + }) + } +} diff --git a/pkg/memo/codec_compact.go b/pkg/memo/codec_compact.go new file mode 100644 index 0000000000..bcb49d3f92 --- /dev/null +++ b/pkg/memo/codec_compact.go @@ -0,0 +1,265 @@ +package memo + +import ( + "encoding/binary" + "fmt" + "math" + + "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" +) + +var _ Codec = (*CodecCompact)(nil) + +// CodecCompact is a coder/decoder for compact encoded memo fields +// +// This encoding format concatenates the memo fields into a single byte array +// with zero padding to minimize the total size of the memo. +type CodecCompact struct { + // lenBytes is the number of bytes used to encode the length of the data + lenBytes int + + // args contains the list of arguments + args []CodecArg +} + +// NewCodecCompact creates a new compact codec +func NewCodecCompact(encodingFmt EncodingFormat) (*CodecCompact, error) { + lenBytes, err := GetLenBytes(encodingFmt) + if err != nil { + return nil, err + } + + return &CodecCompact{ + lenBytes: lenBytes, + args: make([]CodecArg, 0), + }, nil +} + +// AddArguments adds a list of arguments to the codec +func (c *CodecCompact) AddArguments(args ...CodecArg) { + c.args = append(c.args, args...) +} + +// PackArguments packs the arguments into the compact encoded data +func (c *CodecCompact) PackArguments() ([]byte, error) { + data := make([]byte, 0) + + // pack according to argument type + for _, arg := range c.args { + switch arg.Type { + case ArgTypeBytes: + dataBytes, err := c.packBytes(arg.Arg) + if err != nil { + return nil, errors.Wrapf(err, "failed to pack bytes argument: %s", arg.Name) + } + data = append(data, dataBytes...) + case ArgTypeAddress: + dataAddress, err := c.packAddress(arg.Arg) + if err != nil { + return nil, errors.Wrapf(err, "failed to pack address argument: %s", arg.Name) + } + data = append(data, dataAddress...) + case ArgTypeString: + dataString, err := c.packString(arg.Arg) + if err != nil { + return nil, errors.Wrapf(err, "failed to pack string argument: %s", arg.Name) + } + data = append(data, dataString...) + default: + return nil, fmt.Errorf("unsupported argument (%s) type: %s", arg.Name, arg.Type) + } + } + + return data, nil +} + +// UnpackArguments unpacks the compact encoded data into the output arguments +func (c *CodecCompact) UnpackArguments(data []byte) error { + // unpack according to argument type + offset := 0 + for _, arg := range c.args { + switch arg.Type { + case ArgTypeBytes: + bytesRead, err := c.unpackBytes(data[offset:], arg.Arg) + if err != nil { + return errors.Wrapf(err, "failed to unpack bytes argument: %s", arg.Name) + } + offset += bytesRead + case ArgTypeAddress: + bytesRead, err := c.unpackAddress(data[offset:], arg.Arg) + if err != nil { + return errors.Wrapf(err, "failed to unpack address argument: %s", arg.Name) + } + offset += bytesRead + case ArgTypeString: + bytesRead, err := c.unpackString(data[offset:], arg.Arg) + if err != nil { + return errors.Wrapf(err, "failed to unpack string argument: %s", arg.Name) + } + offset += bytesRead + default: + return fmt.Errorf("unsupported argument (%s) type: %s", arg.Name, arg.Type) + } + } + + // ensure all data is consumed + if offset != len(data) { + return fmt.Errorf("consumed bytes (%d) != total bytes (%d)", offset, len(data)) + } + + return nil +} + +// packLength packs the length of the data into the compact format +func (c *CodecCompact) packLength(length int) ([]byte, error) { + data := make([]byte, c.lenBytes) + + switch c.lenBytes { + case LenBytesShort: + if length > math.MaxUint8 { + return nil, fmt.Errorf("data length %d exceeds %d bytes", length, math.MaxUint8) + } + data[0] = uint8(length) + case LenBytesLong: + if length > math.MaxUint16 { + return nil, fmt.Errorf("data length %d exceeds %d bytes", length, math.MaxUint16) + } + // #nosec G115 range checked + binary.LittleEndian.PutUint16(data, uint16(length)) + } + return data, nil +} + +// packAddress packs argument of type 'address'. +func (c *CodecCompact) packAddress(arg interface{}) ([]byte, error) { + // type assertion + address, ok := arg.(common.Address) + if !ok { + return nil, fmt.Errorf("argument is not of type common.Address") + } + + return address.Bytes(), nil +} + +// packBytes packs argument of type 'bytes'. +func (c *CodecCompact) packBytes(arg interface{}) ([]byte, error) { + // type assertion + bytes, ok := arg.([]byte) + if !ok { + return nil, fmt.Errorf("argument is not of type []byte") + } + + // pack length of the data + data, err := c.packLength(len(bytes)) + if err != nil { + return nil, errors.Wrap(err, "failed to pack length of bytes") + } + + // append the data + data = append(data, bytes...) + return data, nil +} + +// packString packs argument of type 'string'. +func (c *CodecCompact) packString(arg interface{}) ([]byte, error) { + // type assertion + str, ok := arg.(string) + if !ok { + return nil, fmt.Errorf("argument is not of type string") + } + + // pack length of the data + data, err := c.packLength(len([]byte(str))) + if err != nil { + return nil, errors.Wrap(err, "failed to pack length of string") + } + + // append the string + data = append(data, []byte(str)...) + return data, nil +} + +// unpackLength returns the length of the data encoded in the compact format +func (c *CodecCompact) unpackLength(data []byte) (int, error) { + if len(data) < c.lenBytes { + return 0, fmt.Errorf("expected %d bytes to decode length, got %d", c.lenBytes, len(data)) + } + + // decode length of the data + length := 0 + switch c.lenBytes { + case LenBytesShort: + length = int(data[0]) + case LenBytesLong: + // convert little-endian bytes to integer + length = int(binary.LittleEndian.Uint16(data[:2])) + } + + // ensure remaining data is long enough + if len(data) < c.lenBytes+length { + return 0, fmt.Errorf("expected %d bytes, got %d", length, len(data)-c.lenBytes) + } + + return length, nil +} + +// unpackAddress unpacks argument of type 'address'. +func (c *CodecCompact) unpackAddress(data []byte, output interface{}) (int, error) { + // type assertion + pAddress, ok := output.(*common.Address) + if !ok { + return 0, fmt.Errorf("argument is not of type *common.Address") + } + + // ensure remaining data >= 20 bytes + if len(data) < common.AddressLength { + return 0, fmt.Errorf("expected address, got %d bytes", len(data)) + } + *pAddress = common.BytesToAddress((data[:20])) + + return common.AddressLength, nil +} + +// unpackBytes unpacks argument of type 'bytes' and returns the number of bytes read. +func (c *CodecCompact) unpackBytes(data []byte, output interface{}) (int, error) { + // type assertion + pSlice, ok := output.(*[]byte) + if !ok { + return 0, fmt.Errorf("argument is not of type *[]byte") + } + + // unpack length + dataLen, err := c.unpackLength(data) + if err != nil { + return 0, errors.Wrap(err, "failed to unpack length of bytes") + } + + // make a copy of the data + if dataLen > 0 { + *pSlice = make([]byte, dataLen) + copy(*pSlice, data[c.lenBytes:c.lenBytes+dataLen]) + } + + return c.lenBytes + dataLen, nil +} + +// unpackString unpacks argument of type 'string' and returns the number of bytes read. +func (c *CodecCompact) unpackString(data []byte, output interface{}) (int, error) { + // type assertion + pString, ok := output.(*string) + if !ok { + return 0, fmt.Errorf("argument is not of type *string") + } + + // unpack length + strLen, err := c.unpackLength(data) + if err != nil { + return 0, errors.Wrap(err, "failed to unpack length of string") + } + + // make a copy of the string + *pString = string(data[c.lenBytes : c.lenBytes+strLen]) + + return c.lenBytes + strLen, nil +} diff --git a/pkg/memo/codec_compact_test.go b/pkg/memo/codec_compact_test.go new file mode 100644 index 0000000000..328beeb7e4 --- /dev/null +++ b/pkg/memo/codec_compact_test.go @@ -0,0 +1,413 @@ +package memo_test + +import ( + "bytes" + "encoding/binary" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/node/pkg/memo" +) + +// CompactPack is a helper function to pack arguments into compact encoded data +// Note: all arguments are assumed to be <= 65535 bytes for simplicity. +func CompactPack(encodingFmt memo.EncodingFormat, args ...memo.CodecArg) []byte { + var ( + length int + packedData []byte + ) + + for _, arg := range args { + // get length of argument + switch arg.Type { + case memo.ArgTypeBytes: + length = len(arg.Arg.([]byte)) + case memo.ArgTypeString: + length = len([]byte(arg.Arg.(string))) + default: + // skip length for other types + length = -1 + } + + // append length in bytes + if length != -1 { + switch encodingFmt { + case memo.EncodingFmtCompactShort: + packedData = append(packedData, byte(length)) + case memo.EncodingFmtCompactLong: + buff := make([]byte, 2) + binary.LittleEndian.PutUint16(buff, uint16(length)) + packedData = append(packedData, buff...) + } + } + + // append actual data in bytes + switch arg.Type { + case memo.ArgTypeBytes: + packedData = append(packedData, arg.Arg.([]byte)...) + case memo.ArgTypeAddress: + packedData = append(packedData, arg.Arg.(common.Address).Bytes()...) + case memo.ArgTypeString: + packedData = append(packedData, []byte(arg.Arg.(string))...) + } + } + + return packedData +} + +func Test_NewCodecCompact(t *testing.T) { + tests := []struct { + name string + encodeFmt memo.EncodingFormat + fail bool + }{ + { + name: "create codec compact successfully", + encodeFmt: memo.EncodingFmtCompactShort, + }, + { + name: "create codec compact failed on invalid encoding format", + encodeFmt: 0b11, + fail: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + codec, err := memo.NewCodecCompact(tc.encodeFmt) + if tc.fail { + require.Error(t, err) + require.Nil(t, codec) + } else { + require.NoError(t, err) + require.NotNil(t, codec) + } + }) + } +} + +func Test_CodecCompact_AddArguments(t *testing.T) { + codec, err := memo.NewCodecCompact(memo.EncodingFmtCompactLong) + require.NoError(t, err) + require.NotNil(t, codec) + + address := common.HexToAddress("0x855EfD3C54F9Ed106C6c3FB343539c89Df042e0B") + codec.AddArguments(memo.ArgReceiver(address)) + + // attempt to pack the arguments, result should not be nil + packedData, err := codec.PackArguments() + require.NoError(t, err) + require.True(t, len(packedData) == common.AddressLength) +} + +func Test_CodecCompact_PackArguments(t *testing.T) { + // create sample arguments + argAddress := common.HexToAddress("0x855EfD3C54F9Ed106C6c3FB343539c89Df042e0B") + argBytes := []byte("here is a bytes argument") + argString := "some other string argument" + + // test cases + tests := []struct { + name string + encodeFmt memo.EncodingFormat + args []memo.CodecArg + expectedLen int + errMsg string + }{ + { + name: "pack arguments of [address, bytes, string] in compact-short format", + encodeFmt: memo.EncodingFmtCompactShort, + args: []memo.CodecArg{ + memo.ArgReceiver(argAddress), + memo.ArgPayload(argBytes), + memo.ArgRevertAddress(argString), + }, + expectedLen: 20 + 1 + len(argBytes) + 1 + len([]byte(argString)), + }, + { + name: "pack arguments of [string, address, bytes] in compact-long format", + encodeFmt: memo.EncodingFmtCompactLong, + args: []memo.CodecArg{ + memo.ArgRevertAddress(argString), + memo.ArgReceiver(argAddress), + memo.ArgPayload(argBytes), + }, + expectedLen: 2 + len([]byte(argString)) + 20 + 2 + len(argBytes), + }, + { + name: "pack long string (> 255 bytes) with compact-long format", + encodeFmt: memo.EncodingFmtCompactLong, + args: []memo.CodecArg{ + memo.ArgPayload([]byte(strings.Repeat("a", 256))), + }, + expectedLen: 2 + 256, + }, + { + name: "pack long string (> 255 bytes) with compact-short format should fail", + encodeFmt: memo.EncodingFmtCompactShort, + args: []memo.CodecArg{ + memo.ArgPayload([]byte(strings.Repeat("b", 256))), + }, + errMsg: "exceeds 255 bytes", + }, + { + name: "pack long string (> 65535 bytes) with compact-long format should fail", + encodeFmt: memo.EncodingFmtCompactLong, + args: []memo.CodecArg{ + memo.ArgPayload([]byte(strings.Repeat("c", 65536))), + }, + errMsg: "exceeds 65535 bytes", + }, + { + name: "pack empty byte array and string arguments", + encodeFmt: memo.EncodingFmtCompactShort, + args: []memo.CodecArg{ + memo.ArgPayload([]byte{}), + memo.ArgRevertAddress(""), + }, + expectedLen: 2, + }, + { + name: "failed to pack bytes argument if string is passed", + encodeFmt: memo.EncodingFmtCompactShort, + args: []memo.CodecArg{ + memo.ArgPayload(argString), // expect bytes type, but passed string + }, + errMsg: "argument is not of type []byte", + }, + { + name: "failed to pack address argument if bytes is passed", + encodeFmt: memo.EncodingFmtCompactShort, + args: []memo.CodecArg{ + memo.ArgReceiver(argBytes), // expect address type, but passed bytes + }, + errMsg: "argument is not of type common.Address", + }, + { + name: "failed to pack string argument if bytes is passed", + encodeFmt: memo.EncodingFmtCompactShort, + args: []memo.CodecArg{ + memo.ArgRevertAddress(argBytes), // expect string type, but passed bytes + }, + errMsg: "argument is not of type string", + }, + { + name: "failed to pack unsupported argument type", + encodeFmt: memo.EncodingFmtCompactShort, + args: []memo.CodecArg{ + memo.NewArg("receiver", memo.ArgType("unknown"), nil), + }, + errMsg: "unsupported argument (receiver) type", + }, + } + + // loop through each test case + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // create a new compact codec and add arguments + codec, err := memo.NewCodecCompact(tc.encodeFmt) + require.NoError(t, err) + codec.AddArguments(tc.args...) + + // pack arguments + packedData, err := codec.PackArguments() + + if tc.errMsg != "" { + require.ErrorContains(t, err, tc.errMsg) + require.Nil(t, packedData) + return + } + require.NoError(t, err) + require.Equal(t, tc.expectedLen, len(packedData)) + + // calc expected data for comparison + expectedData := CompactPack(tc.encodeFmt, tc.args...) + + // validate the packed data + require.True(t, bytes.Equal(expectedData, packedData), "compact encoded data mismatch") + }) + } +} + +func Test_CodecCompact_UnpackArguments(t *testing.T) { + // create sample arguments + argAddress := common.HexToAddress("0x855EfD3C54F9Ed106C6c3FB343539c89Df042e0B") + argBytes := []byte("some test bytes argument") + argString := "some other string argument" + + // test cases + tests := []struct { + name string + encodeFmt memo.EncodingFormat + data []byte + expected []memo.CodecArg + errMsg string + }{ + { + name: "unpack arguments of [address, bytes, string] in compact-short format", + encodeFmt: memo.EncodingFmtCompactShort, + data: CompactPack( + memo.EncodingFmtCompactShort, + memo.ArgReceiver(argAddress), + memo.ArgPayload(argBytes), + memo.ArgRevertAddress(argString), + ), + expected: []memo.CodecArg{ + memo.ArgReceiver(argAddress), + memo.ArgPayload(argBytes), + memo.ArgRevertAddress(argString), + }, + }, + { + name: "unpack arguments of [string, address, bytes] in compact-long format", + encodeFmt: memo.EncodingFmtCompactLong, + data: CompactPack( + memo.EncodingFmtCompactLong, + memo.ArgRevertAddress(argString), + memo.ArgReceiver(argAddress), + memo.ArgPayload(argBytes), + ), + expected: []memo.CodecArg{ + memo.ArgRevertAddress(argString), + memo.ArgReceiver(argAddress), + memo.ArgPayload(argBytes), + }, + }, + { + name: "unpack empty byte array and string argument", + encodeFmt: memo.EncodingFmtCompactShort, + data: CompactPack( + memo.EncodingFmtCompactShort, + memo.ArgPayload([]byte{}), + memo.ArgRevertAddress(""), + ), + expected: []memo.CodecArg{ + memo.ArgPayload([]byte{}), + memo.ArgRevertAddress(""), + }, + }, + { + name: "failed to unpack address if data length < 20 bytes", + encodeFmt: memo.EncodingFmtCompactShort, + data: []byte{0x01, 0x02, 0x03, 0x04, 0x05}, + expected: []memo.CodecArg{ + memo.ArgReceiver(argAddress), + }, + errMsg: "expected address, got 5 bytes", + }, + { + name: "failed to unpack string if data length < 1 byte", + encodeFmt: memo.EncodingFmtCompactShort, + data: []byte{}, + expected: []memo.CodecArg{ + memo.ArgRevertAddress(argString), + }, + errMsg: "expected 1 bytes to decode length", + }, + { + name: "failed to unpack string if actual data is less than decoded length", + encodeFmt: memo.EncodingFmtCompactShort, + data: []byte{0x05, 0x0a, 0x0b, 0x0c, 0x0d}, // length = 5, but only 4 bytes provided + expected: []memo.CodecArg{ + memo.ArgPayload(argBytes), + }, + errMsg: "expected 5 bytes, got 4", + }, + { + name: "failed to unpack bytes argument if string is passed", + encodeFmt: memo.EncodingFmtCompactShort, + data: CompactPack( + memo.EncodingFmtCompactShort, + memo.ArgPayload(argBytes), + ), + expected: []memo.CodecArg{ + memo.ArgPayload(argString), // expect bytes type, but passed string + }, + errMsg: "argument is not of type *[]byte", + }, + { + name: "failed to unpack address argument if bytes is passed", + encodeFmt: memo.EncodingFmtCompactShort, + data: CompactPack( + memo.EncodingFmtCompactShort, + memo.ArgReceiver(argAddress), + ), + expected: []memo.CodecArg{ + memo.ArgReceiver(argBytes), // expect address type, but passed bytes + }, + errMsg: "argument is not of type *common.Address", + }, + { + name: "failed to unpack string argument if address is passed", + encodeFmt: memo.EncodingFmtCompactShort, + data: CompactPack( + memo.EncodingFmtCompactShort, + memo.ArgRevertAddress(argString), + ), + expected: []memo.CodecArg{ + memo.ArgRevertAddress(argAddress), // expect string type, but passed address + }, + errMsg: "argument is not of type *string", + }, + { + name: "failed to unpack unsupported argument type", + encodeFmt: memo.EncodingFmtCompactShort, + data: []byte{}, + expected: []memo.CodecArg{ + memo.NewArg("payload", memo.ArgType("unknown"), nil), + }, + errMsg: "unsupported argument (payload) type", + }, + { + name: "unpacking should fail if not all data is consumed", + encodeFmt: memo.EncodingFmtCompactShort, + data: func() []byte { + data := CompactPack( + memo.EncodingFmtCompactShort, + memo.ArgReceiver(argAddress), + memo.ArgPayload(argBytes), + ) + // append 1 extra byte + return append(data, 0x00) + }(), + expected: []memo.CodecArg{ + memo.ArgReceiver(argAddress), + memo.ArgPayload(argBytes), + }, + errMsg: "consumed bytes (45) != total bytes (46)", + }, + } + + // loop through each test case + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // create a new compact codec and add arguments + codec, err := memo.NewCodecCompact(tc.encodeFmt) + require.NoError(t, err) + + // add output arguments + output := make([]memo.CodecArg, len(tc.expected)) + for i, arg := range tc.expected { + output[i] = memo.NewArg(arg.Name, arg.Type, newArgInstance(arg.Arg)) + } + codec.AddArguments(output...) + + // unpack arguments from compact-encoded data + err = codec.UnpackArguments(tc.data) + + // validate error message + if tc.errMsg != "" { + require.ErrorContains(t, err, tc.errMsg) + return + } + + // validate the unpacked arguments values + require.NoError(t, err) + for i, arg := range tc.expected { + ensureArgEquality(t, arg.Arg, output[i].Arg) + } + }) + } +} diff --git a/pkg/memo/codec_test.go b/pkg/memo/codec_test.go new file mode 100644 index 0000000000..aa27667967 --- /dev/null +++ b/pkg/memo/codec_test.go @@ -0,0 +1,92 @@ +package memo_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/node/pkg/memo" +) + +func Test_GetLenBytes(t *testing.T) { + // Define table-driven test cases + tests := []struct { + name string + encodeFmt memo.EncodingFormat + expectedLen int + expectErr bool + }{ + { + name: "compact short", + encodeFmt: memo.EncodingFmtCompactShort, + expectedLen: 1, + }, + { + name: "compact long", + encodeFmt: memo.EncodingFmtCompactLong, + expectedLen: 2, + }, + { + name: "non-compact encoding format", + encodeFmt: memo.EncodingFmtABI, + expectedLen: 0, + expectErr: true, + }, + } + + // Loop through each test case + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + length, err := memo.GetLenBytes(tc.encodeFmt) + + // Check if error is expected + if tc.expectErr { + require.Error(t, err) + require.Equal(t, 0, length) + } else { + require.NoError(t, err) + require.Equal(t, tc.expectedLen, length) + } + }) + } +} + +func Test_GetCodec(t *testing.T) { + // Define table-driven test cases + tests := []struct { + name string + encodeFmt memo.EncodingFormat + errMsg string + }{ + { + name: "should get ABI codec", + encodeFmt: memo.EncodingFmtABI, + }, + { + name: "should get compact codec", + encodeFmt: memo.EncodingFmtCompactShort, + }, + { + name: "should get compact codec", + encodeFmt: memo.EncodingFmtCompactLong, + }, + { + name: "should fail to get codec", + encodeFmt: 0b0011, + errMsg: "invalid encoding format", + }, + } + + // Loop through each test case + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + codec, err := memo.GetCodec(tc.encodeFmt) + if tc.errMsg != "" { + require.Error(t, err) + require.Nil(t, codec) + } else { + require.NoError(t, err) + require.NotNil(t, codec) + } + }) + } +} diff --git a/pkg/memo/fields.go b/pkg/memo/fields.go new file mode 100644 index 0000000000..fff853f955 --- /dev/null +++ b/pkg/memo/fields.go @@ -0,0 +1,16 @@ +package memo + +// Fields is the interface for memo fields +type Fields interface { + // Pack encodes the memo fields + Pack(opCode OpCode, encodingFmt EncodingFormat, dataFlags uint8) ([]byte, error) + + // Unpack decodes the memo fields + Unpack(opCode OpCode, encodingFmt EncodingFormat, dataFlags uint8, data []byte) error + + // Validate checks if the fields are valid + Validate(opCode OpCode, dataFlags uint8) error + + // DataFlags build the data flags for the fields + DataFlags() uint8 +} diff --git a/pkg/memo/fields_v0.go b/pkg/memo/fields_v0.go new file mode 100644 index 0000000000..a8f79d99ba --- /dev/null +++ b/pkg/memo/fields_v0.go @@ -0,0 +1,208 @@ +package memo + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" + + "github.com/zeta-chain/node/pkg/crypto" + zetabits "github.com/zeta-chain/node/pkg/math/bits" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" +) + +// Enum of the bit position of each memo fields +const ( + bitPosReceiver uint8 = 0 // receiver + bitPosPayload uint8 = 1 // payload + bitPosRevertAddress uint8 = 2 // revertAddress + bitPosAbortAddress uint8 = 3 // abortAddress + bitPosRevertMessage uint8 = 4 // revertMessage +) + +const ( + // MaskFlagsReserved is the mask for reserved data flags + MaskFlagsReserved = 0b11100000 +) + +var _ Fields = (*FieldsV0)(nil) + +// FieldsV0 contains the data fields of the inbound memo V0 +type FieldsV0 struct { + // Receiver is the ZEVM receiver address + Receiver common.Address + + // Payload is the calldata passed to ZEVM contract call + Payload []byte + + // RevertOptions is the options for cctx revert handling + RevertOptions crosschaintypes.RevertOptions +} + +// Pack encodes the memo fields +func (f *FieldsV0) Pack(opCode OpCode, encodingFmt EncodingFormat, dataFlags uint8) ([]byte, error) { + // validate fields + err := f.Validate(opCode, dataFlags) + if err != nil { + return nil, err + } + + codec, err := GetCodec(encodingFmt) + if err != nil { + return nil, errors.Wrap(err, "unable to get codec") + } + + return f.packFields(codec, dataFlags) +} + +// Unpack decodes the memo fields +func (f *FieldsV0) Unpack(opCode OpCode, encodingFmt EncodingFormat, dataFlags uint8, data []byte) error { + codec, err := GetCodec(encodingFmt) + if err != nil { + return errors.Wrap(err, "unable to get codec") + } + + err = f.unpackFields(codec, dataFlags, data) + if err != nil { + return err + } + + return f.Validate(opCode, dataFlags) +} + +// Validate checks if the fields are valid +func (f *FieldsV0) Validate(opCode OpCode, dataFlags uint8) error { + // receiver address must be a valid address + if zetabits.IsBitSet(dataFlags, bitPosReceiver) && crypto.IsEmptyAddress(f.Receiver) { + return errors.New("receiver address is empty") + } + + // payload is not allowed for deposit operation + if opCode == OpCodeDeposit && len(f.Payload) > 0 { + return errors.New("payload is not allowed for deposit operation") + } + + // abort address must be a valid address + if zetabits.IsBitSet(dataFlags, bitPosAbortAddress) && !common.IsHexAddress(f.RevertOptions.AbortAddress) { + return errors.New("invalid abort address") + } + + // revert message is not allowed when CallOnRevert is false + // 1. it's a good-to-have check to make the fields semantically correct. + // 2. unpacking won't hit this error as the codec will catch it earlier. + if !f.RevertOptions.CallOnRevert && len(f.RevertOptions.RevertMessage) > 0 { + return errors.New("revert message is not allowed when CallOnRevert is false") + } + + return nil +} + +// DataFlags build the data flags from actual fields +func (f *FieldsV0) DataFlags() uint8 { + var dataFlags uint8 + + // set 'receiver' flag if provided + if !crypto.IsEmptyAddress(f.Receiver) { + zetabits.SetBit(&dataFlags, bitPosReceiver) + } + + // set 'payload' flag if provided + if len(f.Payload) > 0 { + zetabits.SetBit(&dataFlags, bitPosPayload) + } + + // set 'revertAddress' flag if provided + if f.RevertOptions.RevertAddress != "" { + zetabits.SetBit(&dataFlags, bitPosRevertAddress) + } + + // set 'abortAddress' flag if provided + if f.RevertOptions.AbortAddress != "" { + zetabits.SetBit(&dataFlags, bitPosAbortAddress) + } + + // set 'revertMessage' flag if provided + if f.RevertOptions.CallOnRevert { + zetabits.SetBit(&dataFlags, bitPosRevertMessage) + } + + return dataFlags +} + +// packFieldsV0 packs the memo fields for version 0 +func (f *FieldsV0) packFields(codec Codec, dataFlags uint8) ([]byte, error) { + // add 'receiver' argument optionally + if zetabits.IsBitSet(dataFlags, bitPosReceiver) { + codec.AddArguments(ArgReceiver(f.Receiver)) + } + + // add 'payload' argument optionally + if zetabits.IsBitSet(dataFlags, bitPosPayload) { + codec.AddArguments(ArgPayload(f.Payload)) + } + + // add 'revertAddress' argument optionally + if zetabits.IsBitSet(dataFlags, bitPosRevertAddress) { + codec.AddArguments(ArgRevertAddress(f.RevertOptions.RevertAddress)) + } + + // add 'abortAddress' argument optionally + abortAddress := common.HexToAddress(f.RevertOptions.AbortAddress) + if zetabits.IsBitSet(dataFlags, bitPosAbortAddress) { + codec.AddArguments(ArgAbortAddress(abortAddress)) + } + + // add 'revertMessage' argument optionally + if f.RevertOptions.CallOnRevert { + codec.AddArguments(ArgRevertMessage(f.RevertOptions.RevertMessage)) + } + + // pack the codec arguments into data + data, err := codec.PackArguments() + if err != nil { // never happens + return nil, errors.Wrap(err, "failed to pack arguments") + } + + return data, nil +} + +// unpackFields unpacks the memo fields for version 0 +func (f *FieldsV0) unpackFields(codec Codec, dataFlags byte, data []byte) error { + // add 'receiver' argument optionally + if zetabits.IsBitSet(dataFlags, bitPosReceiver) { + codec.AddArguments(ArgReceiver(&f.Receiver)) + } + + // add 'payload' argument optionally + if zetabits.IsBitSet(dataFlags, bitPosPayload) { + codec.AddArguments(ArgPayload(&f.Payload)) + } + + // add 'revertAddress' argument optionally + if zetabits.IsBitSet(dataFlags, bitPosRevertAddress) { + codec.AddArguments(ArgRevertAddress(&f.RevertOptions.RevertAddress)) + } + + // add 'abortAddress' argument optionally + var abortAddress common.Address + if zetabits.IsBitSet(dataFlags, bitPosAbortAddress) { + codec.AddArguments(ArgAbortAddress(&abortAddress)) + } + + // add 'revertMessage' argument optionally + f.RevertOptions.CallOnRevert = zetabits.IsBitSet(dataFlags, bitPosRevertMessage) + if f.RevertOptions.CallOnRevert { + codec.AddArguments(ArgRevertMessage(&f.RevertOptions.RevertMessage)) + } + + // unpack the data (after flags) into codec arguments + err := codec.UnpackArguments(data) + if err != nil { + return errors.Wrap(err, "failed to unpack arguments") + } + + // convert abort address to string + if !crypto.IsEmptyAddress(abortAddress) { + f.RevertOptions.AbortAddress = abortAddress.Hex() + } + + return nil +} diff --git a/pkg/memo/fields_v0_test.go b/pkg/memo/fields_v0_test.go new file mode 100644 index 0000000000..13742422c2 --- /dev/null +++ b/pkg/memo/fields_v0_test.go @@ -0,0 +1,388 @@ +package memo_test + +import ( + "bytes" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/node/pkg/memo" + "github.com/zeta-chain/node/testutil/sample" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" +) + +const ( + // flagsAllFieldsSet sets all fields: [receiver, payload, revert address, abort address, CallOnRevert] + flagsAllFieldsSet = 0b00011111 +) + +func Test_V0_Pack(t *testing.T) { + // create sample fields + fAddress := sample.EthAddress() + fBytes := []byte("here_s_some_bytes_field") + fString := "this_is_a_string_field" + + tests := []struct { + name string + opCode memo.OpCode + encodeFmt memo.EncodingFormat + dataFlags uint8 + fields memo.FieldsV0 + expectedFlags byte + expectedData []byte + errMsg string + }{ + { + name: "pack all fields with ABI encoding", + opCode: memo.OpCodeDepositAndCall, + encodeFmt: memo.EncodingFmtABI, + dataFlags: flagsAllFieldsSet, // all fields are set + fields: memo.FieldsV0{ + Receiver: fAddress, + Payload: fBytes, + RevertOptions: crosschaintypes.RevertOptions{ + RevertAddress: fString, + CallOnRevert: true, + AbortAddress: fAddress.String(), // it's a ZEVM address + RevertMessage: fBytes, + }, + }, + expectedData: ABIPack(t, + memo.ArgReceiver(fAddress), + memo.ArgPayload(fBytes), + memo.ArgRevertAddress(fString), + memo.ArgAbortAddress(fAddress), + memo.ArgRevertMessage(fBytes)), + }, + { + name: "pack all fields with compact encoding", + opCode: memo.OpCodeDepositAndCall, + encodeFmt: memo.EncodingFmtCompactShort, + dataFlags: flagsAllFieldsSet, // all fields are set + fields: memo.FieldsV0{ + Receiver: fAddress, + Payload: fBytes, + RevertOptions: crosschaintypes.RevertOptions{ + RevertAddress: fString, + CallOnRevert: true, + AbortAddress: fAddress.String(), // it's a ZEVM address + RevertMessage: fBytes, + }, + }, + expectedData: CompactPack( + memo.EncodingFmtCompactShort, + memo.ArgReceiver(fAddress), + memo.ArgPayload(fBytes), + memo.ArgRevertAddress(fString), + memo.ArgAbortAddress(fAddress), + memo.ArgRevertMessage(fBytes)), + }, + { + name: "fields validation failed due to empty receiver address", + opCode: memo.OpCodeDepositAndCall, + encodeFmt: memo.EncodingFmtABI, + dataFlags: 0b00000001, // receiver flag is set + fields: memo.FieldsV0{ + Receiver: common.Address{}, + }, + errMsg: "receiver address is empty", + }, + { + name: "unable to get codec on invalid encoding format", + opCode: memo.OpCodeDepositAndCall, + fields: memo.FieldsV0{ + Receiver: fAddress, + }, + encodeFmt: 0x0F, + errMsg: "unable to get codec", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // pack the fields + data, err := tc.fields.Pack(tc.opCode, tc.encodeFmt, tc.dataFlags) + + // validate the error message + if tc.errMsg != "" { + require.ErrorContains(t, err, tc.errMsg) + require.Nil(t, data) + return + } + + // compare the fields + require.NoError(t, err) + require.True(t, bytes.Equal(tc.expectedData, data)) + }) + } +} + +func Test_V0_Unpack(t *testing.T) { + // create sample fields + fAddress := common.HexToAddress("0xA029D053E13223E2442E28be80b3CeDA27ecbE31") + fBytes := []byte("here_s_some_bytes_field") + fString := "this_is_a_string_field" + + tests := []struct { + name string + opCode memo.OpCode + encodeFmt memo.EncodingFormat + dataFlags byte + data []byte + expected memo.FieldsV0 + errMsg string + }{ + { + name: "unpack all fields with ABI encoding", + opCode: memo.OpCodeDepositAndCall, + encodeFmt: memo.EncodingFmtABI, + dataFlags: flagsAllFieldsSet, // all fields are set + data: ABIPack(t, + memo.ArgReceiver(fAddress), + memo.ArgPayload(fBytes), + memo.ArgRevertAddress(fString), + memo.ArgAbortAddress(fAddress), + memo.ArgRevertMessage(fBytes)), + expected: memo.FieldsV0{ + Receiver: fAddress, + Payload: fBytes, + RevertOptions: crosschaintypes.RevertOptions{ + RevertAddress: fString, + CallOnRevert: true, + AbortAddress: fAddress.String(), + RevertMessage: fBytes, + }, + }, + }, + { + name: "unpack all fields with compact encoding", + opCode: memo.OpCodeDepositAndCall, + encodeFmt: memo.EncodingFmtCompactShort, + dataFlags: flagsAllFieldsSet, // all fields are set + data: CompactPack( + memo.EncodingFmtCompactShort, + memo.ArgReceiver(fAddress), + memo.ArgPayload(fBytes), + memo.ArgRevertAddress(fString), + memo.ArgAbortAddress(fAddress), + memo.ArgRevertMessage(fBytes)), + expected: memo.FieldsV0{ + Receiver: fAddress, + Payload: fBytes, + RevertOptions: crosschaintypes.RevertOptions{ + RevertAddress: fString, + CallOnRevert: true, + AbortAddress: fAddress.String(), + RevertMessage: fBytes, + }, + }, + }, + { + name: "unpack empty ABI encoded payload if flag is set", + opCode: memo.OpCodeDepositAndCall, + encodeFmt: memo.EncodingFmtABI, + dataFlags: 0b00000010, // payload flags are set + data: ABIPack(t, + memo.ArgPayload([]byte{})), // empty payload + expected: memo.FieldsV0{}, + }, + { + name: "unpack empty compact encoded payload if flag is set", + opCode: memo.OpCodeDepositAndCall, + encodeFmt: memo.EncodingFmtCompactShort, + dataFlags: 0b00000010, // payload flag is set + data: CompactPack( + memo.EncodingFmtCompactShort, + memo.ArgPayload([]byte{})), // empty payload + expected: memo.FieldsV0{}, + }, + { + name: "unable to get codec on invalid encoding format", + opCode: memo.OpCodeDepositAndCall, + encodeFmt: 0x0F, + dataFlags: 0b00000001, + data: []byte{}, + errMsg: "unable to get codec", + }, + { + name: "failed to unpack ABI encoded data with compact encoding format", + opCode: memo.OpCodeDepositAndCall, + encodeFmt: memo.EncodingFmtCompactShort, + dataFlags: 0b00000011, // receiver and payload flags are set + data: ABIPack(t, + memo.ArgReceiver(fAddress), + memo.ArgPayload(fBytes)), + errMsg: "failed to unpack arguments", + }, + { + name: "fields validation failed due to empty receiver address", + opCode: memo.OpCodeDepositAndCall, + encodeFmt: memo.EncodingFmtABI, + dataFlags: 0b00000011, // receiver and payload flags are set + data: ABIPack(t, + memo.ArgReceiver(common.Address{}), + memo.ArgPayload(fBytes)), + errMsg: "receiver address is empty", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // unpack the fields + fields := memo.FieldsV0{} + err := fields.Unpack(tc.opCode, tc.encodeFmt, tc.dataFlags, tc.data) + + // validate the error message + if tc.errMsg != "" { + require.ErrorContains(t, err, tc.errMsg) + return + } + + // compare the fields + require.NoError(t, err) + require.Equal(t, tc.expected, fields) + }) + } +} + +func Test_V0_Validate(t *testing.T) { + // create sample fields + fAddress := common.HexToAddress("0xA029D053E13223E2442E28be80b3CeDA27ecbE31") + fBytes := []byte("here_s_some_bytes_field") + fString := "this_is_a_string_field" + + tests := []struct { + name string + opCode memo.OpCode + dataFlags uint8 + fields memo.FieldsV0 + errMsg string + }{ + { + name: "valid fields", + opCode: memo.OpCodeDepositAndCall, + dataFlags: flagsAllFieldsSet, // all fields are set + fields: memo.FieldsV0{ + Receiver: fAddress, + Payload: fBytes, + RevertOptions: crosschaintypes.RevertOptions{ + RevertAddress: fString, + CallOnRevert: true, + AbortAddress: fAddress.String(), + RevertMessage: fBytes, + }, + }, + }, + { + name: "invalid receiver address", + opCode: memo.OpCodeCall, + dataFlags: 0b00000001, // receiver flag is set + fields: memo.FieldsV0{ + Receiver: common.Address{}, // provide empty receiver address + }, + errMsg: "receiver address is empty", + }, + { + name: "payload is not allowed when opCode is deposit", + opCode: memo.OpCodeDeposit, + fields: memo.FieldsV0{ + Receiver: fAddress, + Payload: fBytes, // payload is mistakenly set + }, + errMsg: "payload is not allowed for deposit operation", + }, + { + name: "abort address is invalid", + opCode: memo.OpCodeDeposit, + dataFlags: 0b00001000, // abort address flag is set + fields: memo.FieldsV0{ + RevertOptions: crosschaintypes.RevertOptions{ + AbortAddress: "invalid abort address", + }, + }, + errMsg: "invalid abort address", + }, + { + name: "revert message is not allowed when CallOnRevert is false", + opCode: memo.OpCodeDeposit, + fields: memo.FieldsV0{ + Receiver: fAddress, + RevertOptions: crosschaintypes.RevertOptions{ + RevertAddress: fString, + CallOnRevert: false, // CallOnRevert is false + RevertMessage: []byte("revert message"), // revert message is mistakenly set + }, + }, + errMsg: "revert message is not allowed when CallOnRevert is false", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // validate the fields + err := tc.fields.Validate(tc.opCode, tc.dataFlags) + + // validate the error message + if tc.errMsg != "" { + require.ErrorContains(t, err, tc.errMsg) + return + } + require.NoError(t, err) + }) + } +} + +func Test_V0_DataFlags(t *testing.T) { + // create sample fields + fAddress := common.HexToAddress("0xA029D053E13223E2442E28be80b3CeDA27ecbE31") + fBytes := []byte("here_s_some_bytes_field") + fString := "this_is_a_string_field" + + tests := []struct { + name string + fields memo.FieldsV0 + expectedFlags uint8 + }{ + { + name: "all fields set", + fields: memo.FieldsV0{ + Receiver: fAddress, + Payload: fBytes, + RevertOptions: crosschaintypes.RevertOptions{ + RevertAddress: fString, + CallOnRevert: true, + AbortAddress: fAddress.String(), + RevertMessage: fBytes, + }, + }, + expectedFlags: flagsAllFieldsSet, + }, + { + name: "no fields set", + fields: memo.FieldsV0{}, + expectedFlags: 0b00000000, + }, + { + name: "a few fields set", + fields: memo.FieldsV0{ + Receiver: fAddress, + RevertOptions: crosschaintypes.RevertOptions{ + RevertAddress: fString, + CallOnRevert: true, + RevertMessage: fBytes, + }, + }, + expectedFlags: 0b00010101, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // get the data flags + flags := tc.fields.DataFlags() + + // compare the flags + require.Equal(t, tc.expectedFlags, flags) + }) + } +} diff --git a/pkg/memo/header.go b/pkg/memo/header.go new file mode 100644 index 0000000000..5f9bcd1e14 --- /dev/null +++ b/pkg/memo/header.go @@ -0,0 +1,139 @@ +package memo + +import ( + "fmt" + + "github.com/pkg/errors" + + zetabits "github.com/zeta-chain/node/pkg/math/bits" +) + +type OpCode uint8 + +const ( + // Identifier is the ASCII code of 'Z' (0x5A) + Identifier byte = 0x5A + + // HeaderSize is the size of the memo header: [identifier + ctrlByte1+ ctrlByte2 + dataFlags] + HeaderSize = 4 + + // maskVersion is the mask for the version bits(upper 4 bits) + maskVersion byte = 0b11110000 + + // maskEncodingFormat is the mask for the encoding format bits(lower 4 bits) + maskEncodingFormat byte = 0b00001111 + + // maskOpCode is the mask for the operation code bits(upper 4 bits) + maskOpCode byte = 0b11110000 + + // maskCtrlReserved is the mask for reserved control bits (lower 4 bits) + maskCtrlReserved byte = 0b00001111 +) + +// Enum for non-EVM chain inbound operation code (4 bits) +const ( + OpCodeDeposit OpCode = 0b0000 // operation 'deposit' + OpCodeDepositAndCall OpCode = 0b0001 // operation 'deposit_and_call' + OpCodeCall OpCode = 0b0010 // operation 'call' + OpCodeInvalid OpCode = 0b0011 // invalid operation code +) + +// Header represent the memo header +type Header struct { + // Version is the memo Version + Version uint8 + + // EncodingFmt is the memo encoding format + EncodingFmt EncodingFormat + + // OpCode is the inbound operation code + OpCode OpCode + + // Reserved is the reserved control bits + Reserved uint8 + + // DataFlags is the data flags + DataFlags uint8 +} + +// EncodeToBytes encodes the memo header to raw bytes +func (h *Header) EncodeToBytes() ([]byte, error) { + // validate header + if err := h.Validate(); err != nil { + return nil, err + } + + // create buffer for the header + data := make([]byte, HeaderSize) + + // set byte-0 as memo identifier + data[0] = Identifier + + // set version #, encoding format + var ctrlByte1 byte + ctrlByte1 = zetabits.SetBits(ctrlByte1, maskVersion, h.Version) + ctrlByte1 = zetabits.SetBits(ctrlByte1, maskEncodingFormat, byte(h.EncodingFmt)) + data[1] = ctrlByte1 + + // set operation code, reserved bits + var ctrlByte2 byte + ctrlByte2 = zetabits.SetBits(ctrlByte2, maskOpCode, byte(h.OpCode)) + ctrlByte2 = zetabits.SetBits(ctrlByte2, maskCtrlReserved, h.Reserved) + data[2] = ctrlByte2 + + // set data flags + data[3] = h.DataFlags + + return data, nil +} + +// DecodeFromBytes decodes the memo header from the given data +func (h *Header) DecodeFromBytes(data []byte) error { + // memo data must be longer than the header size + if len(data) < HeaderSize { + return errors.New("memo is too short") + } + + // byte-0 is the memo identifier + if data[0] != Identifier { + return fmt.Errorf("invalid memo identifier: %d", data[0]) + } + + // extract version #, encoding format + ctrlByte1 := data[1] + h.Version = zetabits.GetBits(ctrlByte1, maskVersion) + h.EncodingFmt = EncodingFormat(zetabits.GetBits(ctrlByte1, maskEncodingFormat)) + + // extract operation code, reserved bits + ctrlByte2 := data[2] + h.OpCode = OpCode(zetabits.GetBits(ctrlByte2, maskOpCode)) + h.Reserved = zetabits.GetBits(ctrlByte2, maskCtrlReserved) + + // extract data flags + h.DataFlags = data[3] + + // validate header + return h.Validate() +} + +// Validate checks if the memo header is valid +func (h *Header) Validate() error { + if h.Version != 0 { + return fmt.Errorf("invalid memo version: %d", h.Version) + } + + if h.EncodingFmt >= EncodingFmtInvalid { + return fmt.Errorf("invalid encoding format: %d", h.EncodingFmt) + } + + if h.OpCode >= OpCodeInvalid { + return fmt.Errorf("invalid operation code: %d", h.OpCode) + } + + // reserved control bits must be zero + if h.Reserved != 0 { + return fmt.Errorf("reserved control bits are not zero: %d", h.Reserved) + } + + return nil +} diff --git a/pkg/memo/header_test.go b/pkg/memo/header_test.go new file mode 100644 index 0000000000..9d89e1f1d6 --- /dev/null +++ b/pkg/memo/header_test.go @@ -0,0 +1,167 @@ +package memo_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/node/pkg/memo" +) + +// MakeHead is a helper function to create a memo head +// Note: all arguments are assumed to be <= 0b1111 for simplicity. +func MakeHead(version, encodingFmt, opCode, reserved, flags uint8) []byte { + head := make([]byte, memo.HeaderSize) + head[0] = memo.Identifier + head[1] = version<<4 | encodingFmt + head[2] = opCode<<4 | reserved + head[3] = flags + return head +} + +func Test_Header_EncodeToBytes(t *testing.T) { + tests := []struct { + name string + header memo.Header + expected []byte + errMsg string + }{ + { + name: "it works", + header: memo.Header{ + Version: 0, + EncodingFmt: memo.EncodingFmtABI, + OpCode: memo.OpCodeCall, + DataFlags: 0b00011111, + }, + expected: []byte{memo.Identifier, 0b00000000, 0b00100000, 0b00011111}, + }, + { + name: "header validation failed", + header: memo.Header{ + Version: 1, // invalid version + }, + errMsg: "invalid memo version", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + header, err := tt.header.EncodeToBytes() + if tt.errMsg != "" { + require.ErrorContains(t, err, tt.errMsg) + require.Nil(t, header) + return + } + require.NoError(t, err) + require.Equal(t, tt.expected, header) + }) + } +} + +func Test_Header_DecodeFromBytes(t *testing.T) { + tests := []struct { + name string + data []byte + expected memo.Header + errMsg string + }{ + { + name: "it works", + data: append( + MakeHead(0, uint8(memo.EncodingFmtABI), uint8(memo.OpCodeCall), 0, 0), + []byte{0x01, 0x02}...), + expected: memo.Header{ + Version: 0, + EncodingFmt: memo.EncodingFmtABI, + OpCode: memo.OpCodeCall, + Reserved: 0, + }, + }, + { + name: "memo is too short", + data: []byte{0x01, 0x02, 0x03}, + errMsg: "memo is too short", + }, + { + name: "invalid memo identifier", + data: []byte{'M', 0x02, 0x03, 0x04, 0x05}, + errMsg: "invalid memo identifier", + }, + { + name: "header validation failed", + data: append( + MakeHead(0, uint8(memo.EncodingFmtInvalid), uint8(memo.OpCodeCall), 0, 0), + []byte{0x01, 0x02}...), // invalid encoding format + errMsg: "invalid encoding format", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + memo := &memo.Header{} + err := memo.DecodeFromBytes(tt.data) + if tt.errMsg != "" { + require.ErrorContains(t, err, tt.errMsg) + return + } + require.NoError(t, err) + require.Equal(t, tt.expected, *memo) + }) + } +} + +func Test_Header_Validate(t *testing.T) { + tests := []struct { + name string + header memo.Header + errMsg string + }{ + { + name: "valid header", + header: memo.Header{ + Version: 0, + EncodingFmt: memo.EncodingFmtCompactShort, + OpCode: memo.OpCodeDepositAndCall, + }, + }, + { + name: "invalid version", + header: memo.Header{ + Version: 1, + }, + errMsg: "invalid memo version", + }, + { + name: "invalid encoding format", + header: memo.Header{ + EncodingFmt: memo.EncodingFmtInvalid, + }, + errMsg: "invalid encoding format", + }, + { + name: "invalid operation code", + header: memo.Header{ + OpCode: memo.OpCodeInvalid, + }, + errMsg: "invalid operation code", + }, + { + name: "reserved field is not zero", + header: memo.Header{ + Reserved: 1, + }, + errMsg: "reserved control bits are not zero", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.header.Validate() + if tt.errMsg != "" { + require.ErrorContains(t, err, tt.errMsg) + return + } + require.NoError(t, err) + }) + } +} diff --git a/pkg/memo/memo.go b/pkg/memo/memo.go new file mode 100644 index 0000000000..30670952b0 --- /dev/null +++ b/pkg/memo/memo.go @@ -0,0 +1,98 @@ +package memo + +import ( + "encoding/hex" + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" +) + +// InboundMemo represents the inbound memo structure for non-EVM chains +type InboundMemo struct { + // Header contains the memo header + Header + + // FieldsV0 contains the memo fields V0 + // Note: add a FieldsV1 if breaking change is needed in the future + FieldsV0 +} + +// EncodeToBytes encodes a InboundMemo struct to raw bytes +// +// Note: +// - Any provided 'DataFlags' is ignored as they are calculated based on the fields set in the memo. +// - The 'RevertGasLimit' is not used for now for non-EVM chains. +func (m *InboundMemo) EncodeToBytes() ([]byte, error) { + // build fields flags + dataFlags := m.FieldsV0.DataFlags() + m.Header.DataFlags = dataFlags + + // encode head + head, err := m.Header.EncodeToBytes() + if err != nil { + return nil, errors.Wrap(err, "failed to encode memo header") + } + + // encode fields based on version + var data []byte + switch m.Version { + case 0: + data, err = m.FieldsV0.Pack(m.OpCode, m.EncodingFmt, dataFlags) + default: + return nil, fmt.Errorf("invalid memo version: %d", m.Version) + } + if err != nil { + return nil, errors.Wrapf(err, "failed to pack memo fields version: %d", m.Version) + } + + return append(head, data...), nil +} + +// DecodeFromBytes decodes a InboundMemo struct from raw bytes +// +// Returns an error if given data is not a valid memo +func DecodeFromBytes(data []byte) (*InboundMemo, error) { + memo := &InboundMemo{} + + // decode header + err := memo.Header.DecodeFromBytes(data) + if err != nil { + return nil, errors.Wrap(err, "failed to decode memo header") + } + + // decode fields based on version + switch memo.Version { + case 0: + err = memo.FieldsV0.Unpack(memo.OpCode, memo.EncodingFmt, memo.Header.DataFlags, data[HeaderSize:]) + default: + return nil, fmt.Errorf("invalid memo version: %d", memo.Version) + } + if err != nil { + return nil, errors.Wrapf(err, "failed to unpack memo fields version: %d", memo.Version) + } + + return memo, nil +} + +// DecodeLegacyMemoHex decodes hex encoded memo message into address and calldata +// +// The layout of legacy memo is: [20-byte address, variable calldata] +func DecodeLegacyMemoHex(message string) (common.Address, []byte, error) { + if len(message) == 0 { + return common.Address{}, nil, nil + } + + data, err := hex.DecodeString(message) + if err != nil { + return common.Address{}, nil, errors.Wrap(err, "message should be a hex encoded string") + } + + if len(data) < common.AddressLength { + return common.Address{}, data, nil + } + + address := common.BytesToAddress(data[:common.AddressLength]) + data = data[common.AddressLength:] + return address, data, nil +} diff --git a/pkg/memo/memo_test.go b/pkg/memo/memo_test.go new file mode 100644 index 0000000000..e6cb067793 --- /dev/null +++ b/pkg/memo/memo_test.go @@ -0,0 +1,306 @@ +package memo_test + +import ( + "encoding/hex" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/node/pkg/memo" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" +) + +func Test_Memo_EncodeToBytes(t *testing.T) { + // create sample fields + fAddress := common.HexToAddress("0xEA9808f0Ac504d1F521B5BbdfC33e6f1953757a7") + fBytes := []byte("here_s_some_bytes_field") + fString := "this_is_a_string_field" + + tests := []struct { + name string + memo *memo.InboundMemo + expectedHead []byte + expectedData []byte + errMsg string + }{ + { + name: "encode memo with ABI encoding", + memo: &memo.InboundMemo{ + Header: memo.Header{ + Version: 0, + EncodingFmt: memo.EncodingFmtABI, + OpCode: memo.OpCodeDepositAndCall, + }, + FieldsV0: memo.FieldsV0{ + Receiver: fAddress, + Payload: fBytes, + RevertOptions: crosschaintypes.RevertOptions{ + RevertAddress: fString, + CallOnRevert: true, + AbortAddress: fAddress.String(), // it's a ZEVM address + RevertMessage: fBytes, + }, + }, + }, + expectedHead: MakeHead( + 0, + uint8(memo.EncodingFmtABI), + uint8(memo.OpCodeDepositAndCall), + 0, + flagsAllFieldsSet, // all fields are set + ), + expectedData: ABIPack(t, + memo.ArgReceiver(fAddress), + memo.ArgPayload(fBytes), + memo.ArgRevertAddress(fString), + memo.ArgAbortAddress(fAddress), + memo.ArgRevertMessage(fBytes)), + }, + { + name: "encode memo with compact encoding", + memo: &memo.InboundMemo{ + Header: memo.Header{ + Version: 0, + EncodingFmt: memo.EncodingFmtCompactShort, + OpCode: memo.OpCodeDepositAndCall, + }, + FieldsV0: memo.FieldsV0{ + Receiver: fAddress, + Payload: fBytes, + RevertOptions: crosschaintypes.RevertOptions{ + RevertAddress: fString, + CallOnRevert: true, + AbortAddress: fAddress.String(), // it's a ZEVM address + RevertMessage: fBytes, + }, + }, + }, + expectedHead: MakeHead( + 0, + uint8(memo.EncodingFmtCompactShort), + uint8(memo.OpCodeDepositAndCall), + 0, + flagsAllFieldsSet, // all fields are set + ), + expectedData: CompactPack( + memo.EncodingFmtCompactShort, + memo.ArgReceiver(fAddress), + memo.ArgPayload(fBytes), + memo.ArgRevertAddress(fString), + memo.ArgAbortAddress(fAddress), + memo.ArgRevertMessage(fBytes)), + }, + { + name: "failed to encode memo header", + memo: &memo.InboundMemo{ + Header: memo.Header{ + OpCode: memo.OpCodeInvalid, // invalid operation code + }, + }, + errMsg: "failed to encode memo header", + }, + { + name: "failed to encode if version is invalid", + memo: &memo.InboundMemo{ + Header: memo.Header{ + Version: 1, + }, + }, + errMsg: "invalid memo version", + }, + { + name: "failed to pack memo fields", + memo: &memo.InboundMemo{ + Header: memo.Header{ + Version: 0, + EncodingFmt: memo.EncodingFmtABI, + OpCode: memo.OpCodeDeposit, + }, + FieldsV0: memo.FieldsV0{ + Receiver: fAddress, + Payload: fBytes, // payload is not allowed for deposit + }, + }, + errMsg: "failed to pack memo fields version: 0", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + data, err := tt.memo.EncodeToBytes() + if tt.errMsg != "" { + require.ErrorContains(t, err, tt.errMsg) + require.Nil(t, data) + return + } + require.NoError(t, err) + require.Equal(t, append(tt.expectedHead, tt.expectedData...), data) + + // decode the memo and compare with the original + decodedMemo, err := memo.DecodeFromBytes(data) + require.NoError(t, err) + require.Equal(t, tt.memo, decodedMemo) + }) + } +} + +func Test_Memo_DecodeFromBytes(t *testing.T) { + // create sample fields + fAddress := common.HexToAddress("0xEA9808f0Ac504d1F521B5BbdfC33e6f1953757a7") + fBytes := []byte("here_s_some_bytes_field") + fString := "this_is_a_string_field" + + tests := []struct { + name string + head []byte + data []byte + expectedMemo memo.InboundMemo + errMsg string + }{ + { + name: "decode memo with ABI encoding", + head: MakeHead( + 0, + uint8(memo.EncodingFmtABI), + uint8(memo.OpCodeDepositAndCall), + 0, + flagsAllFieldsSet, // all fields are set + ), + data: ABIPack(t, + memo.ArgReceiver(fAddress), + memo.ArgPayload(fBytes), + memo.ArgRevertAddress(fString), + memo.ArgAbortAddress(fAddress), + memo.ArgRevertMessage(fBytes)), + expectedMemo: memo.InboundMemo{ + Header: memo.Header{ + Version: 0, + EncodingFmt: memo.EncodingFmtABI, + OpCode: memo.OpCodeDepositAndCall, + DataFlags: 0b00011111, + }, + FieldsV0: memo.FieldsV0{ + Receiver: fAddress, + Payload: fBytes, + RevertOptions: crosschaintypes.RevertOptions{ + RevertAddress: fString, + CallOnRevert: true, + AbortAddress: fAddress.String(), // it's a ZEVM address + RevertMessage: fBytes, + }, + }, + }, + }, + { + name: "decode memo with compact encoding", + head: MakeHead( + 0, + uint8(memo.EncodingFmtCompactLong), + uint8(memo.OpCodeDepositAndCall), + 0, + flagsAllFieldsSet, // all fields are set + ), + data: CompactPack( + memo.EncodingFmtCompactLong, + memo.ArgReceiver(fAddress), + memo.ArgPayload(fBytes), + memo.ArgRevertAddress(fString), + memo.ArgAbortAddress(fAddress), + memo.ArgRevertMessage(fBytes)), + expectedMemo: memo.InboundMemo{ + Header: memo.Header{ + Version: 0, + EncodingFmt: memo.EncodingFmtCompactLong, + OpCode: memo.OpCodeDepositAndCall, + DataFlags: 0b00011111, + }, + FieldsV0: memo.FieldsV0{ + Receiver: fAddress, + Payload: fBytes, + RevertOptions: crosschaintypes.RevertOptions{ + RevertAddress: fString, + CallOnRevert: true, + AbortAddress: fAddress.String(), // it's a ZEVM address + RevertMessage: fBytes, + }, + }, + }, + }, + { + name: "failed to decode memo header", + head: MakeHead(0, uint8(memo.EncodingFmtABI), uint8(memo.OpCodeInvalid), 0, 0), + data: ABIPack(t, memo.ArgReceiver(fAddress)), + errMsg: "failed to decode memo header", + }, + { + name: "failed to decode if version is invalid", + head: MakeHead(1, uint8(memo.EncodingFmtABI), uint8(memo.OpCodeDeposit), 0, 0), + data: ABIPack(t, memo.ArgReceiver(fAddress)), + errMsg: "invalid memo version", + }, + { + name: "failed to decode compact encoded data with ABI encoding format", + head: MakeHead( + 0, + uint8(memo.EncodingFmtABI), + uint8(memo.OpCodeDepositAndCall), + 0, + 0, + ), // header says ABI encoding + data: CompactPack( + memo.EncodingFmtCompactShort, + memo.ArgReceiver(fAddress), + ), // but data is compact encoded + errMsg: "failed to unpack memo fields", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + data := append(tt.head, tt.data...) + memo, err := memo.DecodeFromBytes(data) + if tt.errMsg != "" { + require.ErrorContains(t, err, tt.errMsg) + return + } + require.NoError(t, err) + require.Equal(t, tt.expectedMemo, *memo) + }) + } +} + +func Test_DecodeLegacyMemoHex(t *testing.T) { + expectedShortMsgResult, err := hex.DecodeString("1a2b3c4d5e6f708192a3b4c5d6e7f808") + require.NoError(t, err) + tests := []struct { + name string + message string + expectAddr common.Address + expectData []byte + wantErr bool + }{ + { + "valid msg", + "95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5", + common.HexToAddress("95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5"), + []byte{}, + false, + }, + {"empty msg", "", common.Address{}, nil, false}, + {"invalid hex", "invalidHex", common.Address{}, nil, true}, + {"short msg", "1a2b3c4d5e6f708192a3b4c5d6e7f808", common.Address{}, expectedShortMsgResult, false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + addr, data, err := memo.DecodeLegacyMemoHex(tt.message) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tt.expectAddr, addr) + require.Equal(t, tt.expectData, data) + } + }) + } +} diff --git a/pkg/ticker/ticker.go b/pkg/ticker/ticker.go index 94fc9ab2fb..2a5a7edff1 100644 --- a/pkg/ticker/ticker.go +++ b/pkg/ticker/ticker.go @@ -34,16 +34,18 @@ import ( "sync" "time" - "cosmossdk.io/errors" + "github.com/rs/zerolog" ) +// Task is a function that will be called by the Ticker +type Task func(ctx context.Context, t *Ticker) error + // Ticker represents a ticker that will run a function periodically. // It also invokes BEFORE ticker starts. type Ticker struct { - interval time.Duration - ticker *time.Ticker - task Task - signalChan chan struct{} + interval time.Duration + ticker *time.Ticker + task Task // runnerMu is a mutex to prevent double run runnerMu sync.Mutex @@ -51,25 +53,47 @@ type Ticker struct { // stateMu is a mutex to prevent concurrent SetInterval calls stateMu sync.Mutex - stopped bool + stopped bool + ctxCancel context.CancelFunc + + externalStopChan <-chan struct{} + logger zerolog.Logger } -// Task is a function that will be called by the Ticker -type Task func(ctx context.Context, t *Ticker) error +// Opt is a configuration option for the Ticker. +type Opt func(*Ticker) -// New creates a new Ticker. -func New(interval time.Duration, runner Task) *Ticker { - return &Ticker{interval: interval, task: runner} +// WithLogger sets the logger for the Ticker. +func WithLogger(log zerolog.Logger, name string) Opt { + return func(t *Ticker) { + t.logger = log.With().Str("ticker_name", name).Logger() + } } -// Run creates and runs a new Ticker. -func Run(ctx context.Context, interval time.Duration, task Task) error { - return New(interval, task).Run(ctx) +// WithStopChan sets the stop channel for the Ticker. +// Please note that stopChan is NOT signalChan. +// Stop channel is a trigger for invoking ticker.Stop(); +func WithStopChan(stopChan <-chan struct{}) Opt { + return func(cfg *Ticker) { cfg.externalStopChan = stopChan } } -// SecondsFromUint64 converts uint64 to time.Duration in seconds. -func SecondsFromUint64(d uint64) time.Duration { - return time.Duration(d) * time.Second +// New creates a new Ticker. +func New(interval time.Duration, task Task, opts ...Opt) *Ticker { + t := &Ticker{ + interval: interval, + task: task, + } + + for _, opt := range opts { + opt(t) + } + + return t +} + +// Run creates and runs a new Ticker. +func Run(ctx context.Context, interval time.Duration, task Task, opts ...Opt) error { + return New(interval, task, opts...).Run(ctx) } // Run runs the ticker by blocking current goroutine. It also invokes BEFORE ticker starts. @@ -96,24 +120,33 @@ func (t *Ticker) Run(ctx context.Context) (err error) { defer t.runnerMu.Unlock() // setup + ctx, t.ctxCancel = context.WithCancel(ctx) t.ticker = time.NewTicker(t.interval) - t.signalChan = make(chan struct{}) t.stopped = false // initial run if err := t.task(ctx, t); err != nil { - return errors.Wrap(err, "ticker task failed") + t.Stop() + return fmt.Errorf("ticker task failed (initial run): %w", err) } for { select { case <-ctx.Done(): + // if task is finished (i.e. last tick completed BEFORE ticker.Stop(), + // then we need to return nil) + if t.stopped { + return nil + } return ctx.Err() case <-t.ticker.C: + // If another goroutine calls ticker.Stop() while the current tick is running, + // Then it's okay to return ctx error if err := t.task(ctx, t); err != nil { - return errors.Wrap(err, "ticker task failed") + return fmt.Errorf("ticker task failed: %w", err) } - case <-t.signalChan: + case <-t.externalStopChan: + t.Stop() return nil } } @@ -139,11 +172,18 @@ func (t *Ticker) Stop() { defer t.stateMu.Unlock() // noop - if t.stopped || t.signalChan == nil { + if t.stopped { return } - close(t.signalChan) + t.ctxCancel() t.stopped = true t.ticker.Stop() + + t.logger.Info().Msgf("Ticker stopped") +} + +// SecondsFromUint64 converts uint64 to time.Duration in seconds. +func SecondsFromUint64(d uint64) time.Duration { + return time.Duration(d) * time.Second } diff --git a/pkg/ticker/ticker_test.go b/pkg/ticker/ticker_test.go index 4d890bf051..60d6c74dc8 100644 --- a/pkg/ticker/ticker_test.go +++ b/pkg/ticker/ticker_test.go @@ -1,12 +1,15 @@ package ticker import ( + "bytes" "context" "fmt" "testing" "time" + "github.com/rs/zerolog" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestTicker(t *testing.T) { @@ -148,7 +151,7 @@ func TestTicker(t *testing.T) { // ASSERT assert.ErrorContains(t, err, "panic during ticker run: oops") // assert that we get error with the correct line number - assert.ErrorContains(t, err, "ticker_test.go:142") + assert.ErrorContains(t, err, "ticker_test.go:145") }) t.Run("Nil panic", func(t *testing.T) { @@ -173,7 +176,7 @@ func TestTicker(t *testing.T) { "panic during ticker run: runtime error: invalid memory address or nil pointer dereference", ) // assert that we get error with the correct line number - assert.ErrorContains(t, err, "ticker_test.go:162") + assert.ErrorContains(t, err, "ticker_test.go:165") }) t.Run("Run as a single call", func(t *testing.T) { @@ -197,4 +200,51 @@ func TestTicker(t *testing.T) { assert.ErrorIs(t, err, context.DeadlineExceeded) assert.Equal(t, 2, counter) }) + + t.Run("With stop channel", func(t *testing.T) { + // ARRANGE + var ( + tickerInterval = 100 * time.Millisecond + counter = 0 + + stopChan = make(chan struct{}) + sleepBeforeStop = 5*tickerInterval + (10 * time.Millisecond) + ) + + task := func(ctx context.Context, _ *Ticker) error { + t.Logf("Tick %d", counter) + counter++ + + return nil + } + + // ACT + go func() { + time.Sleep(sleepBeforeStop) + close(stopChan) + }() + + err := Run(context.Background(), tickerInterval, task, WithStopChan(stopChan)) + + // ASSERT + require.NoError(t, err) + require.Equal(t, 6, counter) // initial tick + 5 more ticks + }) + + t.Run("With logger", func(t *testing.T) { + // ARRANGE + out := &bytes.Buffer{} + logger := zerolog.New(out) + + // ACT + task := func(ctx context.Context, _ *Ticker) error { + return fmt.Errorf("hey") + } + + err := Run(context.Background(), time.Second, task, WithLogger(logger, "my-task")) + + // ARRANGE + require.ErrorContains(t, err, "hey") + require.Contains(t, out.String(), `{"level":"info","ticker_name":"my-task","message":"Ticker stopped"}`) + }) } diff --git a/precompiles/bank/bank.go b/precompiles/bank/bank.go index 436364dfa6..51a559edd3 100644 --- a/precompiles/bank/bank.go +++ b/precompiles/bank/bank.go @@ -136,8 +136,8 @@ func (c *Contract) Run(evm *vm.EVM, contract *vm.Contract, readOnly bool) ([]byt // Deposit and Withdraw methods are both not allowed in read-only mode. case DepositMethodName, WithdrawMethodName: if readOnly { - return nil, ptypes.ErrUnexpected{ - Got: "method not allowed in read-only mode: " + method.Name, + return nil, ptypes.ErrWriteMethod{ + Method: method.Name, } } diff --git a/precompiles/bank/method_deposit.go b/precompiles/bank/method_deposit.go index 2f57944723..1d5792d8a3 100644 --- a/precompiles/bank/method_deposit.go +++ b/precompiles/bank/method_deposit.go @@ -18,7 +18,7 @@ import ( // The caller cosmos address will be calculated from the EVM caller address. by executing toAddr := sdk.AccAddress(addr.Bytes()). // This function can be think of a permissionless way of minting cosmos coins. // This is how deposit works: -// - The caller has to allow the bank contract to spend a certain amount ZRC20 token coins on its behalf. This is mandatory. +// - The caller has to allow the bank precompile address to spend a certain amount ZRC20 token coins on its behalf. This is mandatory. // - Then, the caller calls deposit(ZRC20 address, amount), to deposit the amount and receive cosmos coins. // - The bank will check there's enough balance, the caller is not a blocked address, and the token is a not paused ZRC20. // - Then the cosmos coins "zrc20/0x12345" will be minted and sent to the caller's cosmos address. @@ -59,22 +59,6 @@ func (c *Contract) deposit( return nil, err } - // Safety check: token has to be a valid whitelisted ZRC20 and not be paused. - t, found := c.fungibleKeeper.GetForeignCoins(ctx, zrc20Addr.String()) - if !found { - return nil, &ptypes.ErrInvalidToken{ - Got: zrc20Addr.String(), - Reason: "token is not a whitelisted ZRC20", - } - } - - if t.Paused { - return nil, &ptypes.ErrInvalidToken{ - Got: zrc20Addr.String(), - Reason: "token is paused", - } - } - // Check for enough balance. // function balanceOf(address account) public view virtual override returns (uint256) resBalanceOf, err := c.CallContract( @@ -105,71 +89,24 @@ func (c *Contract) deposit( } } - // Check for enough bank's allowance. - // function allowance(address owner, address spender) public view virtual override returns (uint256) - resAllowance, err := c.CallContract( - ctx, - &c.fungibleKeeper, - c.zrc20ABI, - zrc20Addr, - "allowance", - []interface{}{caller, ContractAddress}, - ) - if err != nil { - return nil, &ptypes.ErrUnexpected{ - When: "allowance", - Got: err.Error(), - } - } - - allowance, ok := resAllowance[0].(*big.Int) - if !ok { - return nil, &ptypes.ErrUnexpected{ - Got: "ZRC20 allowance returned an unexpected type", - } - } - - if allowance.Cmp(amount) < 0 || allowance.Cmp(big.NewInt(0)) <= 0 { - return nil, &ptypes.ErrInvalidAmount{ - Got: allowance.String(), - } - } - // The process of creating a new cosmos coin is: // - Generate the new coin denom using ZRC20 address, // this way we map ZRC20 addresses to cosmos denoms "zevm/0x12345". - // - Mint coins. - // - Send coins to the caller. + // - Mint coins to the fungible module. + // - Send coins from fungible to the caller. coinSet, err := createCoinSet(ZRC20ToCosmosDenom(zrc20Addr), amount) if err != nil { return nil, err } // 2. Effect: subtract balance. - // function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) - resTransferFrom, err := c.CallContract( - ctx, - &c.fungibleKeeper, - c.zrc20ABI, - zrc20Addr, - "transferFrom", - []interface{}{caller, ContractAddress, amount}, - ) - if err != nil { + if err := c.fungibleKeeper.LockZRC20(ctx, c.zrc20ABI, zrc20Addr, c.Address(), caller, c.Address(), amount); err != nil { return nil, &ptypes.ErrUnexpected{ - When: "transferFrom", + When: "LockZRC20InBank", Got: err.Error(), } } - transferred, ok := resTransferFrom[0].(bool) - if !ok || !transferred { - return nil, &ptypes.ErrUnexpected{ - When: "transferFrom", - Got: "transaction not successful", - } - } - // 3. Interactions: create cosmos coin and send. if err := c.bankKeeper.MintCoins(ctx, types.ModuleName, coinSet); err != nil { return nil, &ptypes.ErrUnexpected{ @@ -205,7 +142,7 @@ func unpackDepositArgs(args []interface{}) (zrc20Addr common.Address, amount *bi } amount, ok = args[1].(*big.Int) - if !ok || amount.Sign() < 0 || amount == nil || amount == new(big.Int) { + if !ok || amount == nil || amount.Sign() <= 0 { return common.Address{}, nil, &ptypes.ErrInvalidAmount{ Got: amount.String(), } diff --git a/precompiles/bank/method_test.go b/precompiles/bank/method_test.go index 912c41b40c..e416187490 100644 --- a/precompiles/bank/method_test.go +++ b/precompiles/bank/method_test.go @@ -46,8 +46,8 @@ func Test_Methods(t *testing.T) { success, err := ts.bankContract.Run(ts.mockEVM, ts.mockVMContract, true) require.ErrorIs( t, - ptypes.ErrUnexpected{ - Got: "method not allowed in read-only mode: deposit", + ptypes.ErrWriteMethod{ + Method: "deposit", }, err) require.Empty(t, success) @@ -73,8 +73,8 @@ func Test_Methods(t *testing.T) { success, err := ts.bankContract.Run(ts.mockEVM, ts.mockVMContract, true) require.ErrorIs( t, - ptypes.ErrUnexpected{ - Got: "method not allowed in read-only mode: withdraw", + ptypes.ErrWriteMethod{ + Method: "withdraw", }, err) require.Empty(t, success) @@ -130,18 +130,12 @@ func Test_Methods(t *testing.T) { ts.mockVMContract.Input = packInputArgs( t, methodID, - []interface{}{ts.zrc20Address, big.NewInt(0)}..., + []interface{}{ts.zrc20Address, big.NewInt(1000)}..., ) success, err := ts.bankContract.Run(ts.mockEVM, ts.mockVMContract, false) require.Error(t, err) - require.ErrorAs( - t, - ptypes.ErrInvalidAmount{ - Got: "0", - }, - err, - ) + require.Contains(t, err.Error(), "invalid allowance, got 0") res, err := ts.bankABI.Methods[DepositMethodName].Outputs.Unpack(success) require.NoError(t, err) @@ -150,6 +144,33 @@ func Test_Methods(t *testing.T) { require.False(t, ok) }) + t.Run("should fail when trying to deposit 0", func(t *testing.T) { + ts := setupChain(t) + caller := fungibletypes.ModuleAddressEVM + ts.fungibleKeeper.DepositZRC20(ts.ctx, ts.zrc20Address, caller, big.NewInt(1000)) + + methodID := ts.bankABI.Methods[DepositMethodName] + + // Allow bank to spend 500 ZRC20 tokens. + allowBank(t, ts, big.NewInt(500)) + + // Set CallerAddress and evm.Origin to the caller address. + // Caller does not have any balance, and bank does not have any allowance. + ts.mockVMContract.CallerAddress = caller + ts.mockEVM.Origin = caller + + // Set the input arguments for the deposit method. + ts.mockVMContract.Input = packInputArgs( + t, + methodID, + []interface{}{ts.zrc20Address, big.NewInt(0)}..., + ) + + _, err := ts.bankContract.Run(ts.mockEVM, ts.mockVMContract, false) + require.Error(t, err) + require.Contains(t, err.Error(), "invalid token amount: 0") + }) + t.Run("should fail when trying to deposit more than allowed to bank", func(t *testing.T) { ts := setupChain(t) caller := fungibletypes.ModuleAddressEVM @@ -173,12 +194,10 @@ func Test_Methods(t *testing.T) { success, err := ts.bankContract.Run(ts.mockEVM, ts.mockVMContract, false) require.Error(t, err) - require.ErrorAs( + require.Contains( t, - ptypes.ErrInvalidAmount{ - Got: "500", - }, - err, + err.Error(), + "unexpected error in LockZRC20InBank: failed allowance check: invalid allowance, got 500, wanted 501", ) res, err := ts.bankABI.Methods[DepositMethodName].Outputs.Unpack(success) diff --git a/precompiles/bank/method_withdraw.go b/precompiles/bank/method_withdraw.go index 99cb4f763c..e071ed04cd 100644 --- a/precompiles/bank/method_withdraw.go +++ b/precompiles/bank/method_withdraw.go @@ -55,19 +55,11 @@ func (c *Contract) withdraw( return nil, err } - // Safety check: token has to be a valid whitelisted ZRC20 and not be paused. - t, found := c.fungibleKeeper.GetForeignCoins(ctx, zrc20Addr.String()) - if !found { + // Safety check: token has to be a non-paused whitelisted ZRC20. + if err := c.fungibleKeeper.IsValidZRC20(ctx, zrc20Addr); err != nil { return nil, &ptypes.ErrInvalidToken{ Got: zrc20Addr.String(), - Reason: "token is not a whitelisted ZRC20", - } - } - - if t.Paused { - return nil, &ptypes.ErrInvalidToken{ - Got: zrc20Addr.String(), - Reason: "token is paused", + Reason: err.Error(), } } @@ -92,33 +84,11 @@ func (c *Contract) withdraw( return nil, err } - // Check for bank's ZRC20 balance. - // function balanceOf(address account) public view virtual override returns (uint256) - resBalanceOf, err := c.CallContract( - ctx, - &c.fungibleKeeper, - c.zrc20ABI, - zrc20Addr, - "balanceOf", - []interface{}{ContractAddress}, - ) - if err != nil { - return nil, &ptypes.ErrUnexpected{ - When: "balanceOf", - Got: err.Error(), - } - } - - balance, ok := resBalanceOf[0].(*big.Int) - if !ok { - return nil, &ptypes.ErrUnexpected{ - Got: "ZRC20 balanceOf returned an unexpected type", - } - } - - if balance.Cmp(amount) == -1 { - return nil, &ptypes.ErrInvalidAmount{ - Got: balance.String(), + // Check if bank address has enough ZRC20 balance. + if err := c.fungibleKeeper.CheckZRC20Balance(ctx, c.zrc20ABI, zrc20Addr, c.Address(), amount); err != nil { + return nil, &ptypes.ErrInsufficientBalance{ + Requested: amount.String(), + Got: err.Error(), } } @@ -137,45 +107,21 @@ func (c *Contract) withdraw( } } - if err := c.addEventLog(ctx, evm.StateDB, WithdrawEventName, eventData{caller, zrc20Addr, fromAddr.String(), coinSet.Denoms()[0], amount}); err != nil { + // 3. Interactions: send ZRC20. + if err := c.fungibleKeeper.UnlockZRC20(ctx, c.zrc20ABI, zrc20Addr, caller, c.Address(), amount); err != nil { return nil, &ptypes.ErrUnexpected{ - When: "AddWithdrawLog", + When: "UnlockZRC20InBank", Got: err.Error(), } } - // 3. Interactions: send to module and burn. - - // function transfer(address recipient, uint256 amount) public virtual override returns (bool) - resTransfer, err := c.CallContract( - ctx, - &c.fungibleKeeper, - c.zrc20ABI, - zrc20Addr, - "transfer", - []interface{}{caller /* sender */, amount}, - ) - if err != nil { + if err := c.addEventLog(ctx, evm.StateDB, WithdrawEventName, eventData{caller, zrc20Addr, fromAddr.String(), coinSet.Denoms()[0], amount}); err != nil { return nil, &ptypes.ErrUnexpected{ - When: "transfer", + When: "AddWithdrawLog", Got: err.Error(), } } - transferred, ok := resTransfer[0].(bool) - if !ok { - return nil, &ptypes.ErrUnexpected{ - Got: "ZRC20 transfer returned an unexpected type", - } - } - - if !transferred { - return nil, &ptypes.ErrUnexpected{ - When: "transfer", - Got: "transaction not successful", - } - } - return method.Outputs.Pack(true) } @@ -188,7 +134,7 @@ func unpackWithdrawArgs(args []interface{}) (zrc20Addr common.Address, amount *b } amount, ok = args[1].(*big.Int) - if !ok || amount.Sign() < 0 || amount == nil || amount == new(big.Int) { + if !ok || amount == nil || amount.Sign() <= 0 { return common.Address{}, nil, &ptypes.ErrInvalidAmount{ Got: amount.String(), } diff --git a/precompiles/staking/staking.go b/precompiles/staking/staking.go index 7ef467b039..fea0d0f9f2 100644 --- a/precompiles/staking/staking.go +++ b/precompiles/staking/staking.go @@ -380,7 +380,7 @@ func (c *Contract) MoveStake( // Run is the entrypoint of the precompiled contract, it switches over the input method, // and execute them accordingly. -func (c *Contract) Run(evm *vm.EVM, contract *vm.Contract, _ bool) ([]byte, error) { +func (c *Contract) Run(evm *vm.EVM, contract *vm.Contract, readOnly bool) ([]byte, error) { method, err := ABI.MethodById(contract.Input[:4]) if err != nil { return nil, err @@ -415,6 +415,18 @@ func (c *Contract) Run(evm *vm.EVM, contract *vm.Contract, _ bool) ([]byte, erro } return res, nil case StakeMethodName: + // Disabled until further notice, check https://github.com/zeta-chain/node/issues/3005. + return nil, ptypes.ErrDisabledMethod{ + Method: method.Name, + } + + //nolint:govet + if readOnly { + return nil, ptypes.ErrWriteMethod{ + Method: method.Name, + } + } + var res []byte execErr := stateDB.ExecuteNativeAction(contract.Address(), nil, func(ctx sdk.Context) error { res, err = c.Stake(ctx, evm, contract, method, args) @@ -425,6 +437,18 @@ func (c *Contract) Run(evm *vm.EVM, contract *vm.Contract, _ bool) ([]byte, erro } return res, nil case UnstakeMethodName: + // Disabled until further notice, check https://github.com/zeta-chain/node/issues/3005. + return nil, ptypes.ErrDisabledMethod{ + Method: method.Name, + } + + //nolint:govet + if readOnly { + return nil, ptypes.ErrWriteMethod{ + Method: method.Name, + } + } + var res []byte execErr := stateDB.ExecuteNativeAction(contract.Address(), nil, func(ctx sdk.Context) error { res, err = c.Unstake(ctx, evm, contract, method, args) @@ -435,6 +459,18 @@ func (c *Contract) Run(evm *vm.EVM, contract *vm.Contract, _ bool) ([]byte, erro } return res, nil case MoveStakeMethodName: + // Disabled until further notice, check https://github.com/zeta-chain/node/issues/3005. + return nil, ptypes.ErrDisabledMethod{ + Method: method.Name, + } + + //nolint:govet + if readOnly { + return nil, ptypes.ErrWriteMethod{ + Method: method.Name, + } + } + var res []byte execErr := stateDB.ExecuteNativeAction(contract.Address(), nil, func(ctx sdk.Context) error { res, err = c.MoveStake(ctx, evm, contract, method, args) diff --git a/precompiles/staking/staking_test.go b/precompiles/staking/staking_test.go index 94c1a432c1..aaea87f94d 100644 --- a/precompiles/staking/staking_test.go +++ b/precompiles/staking/staking_test.go @@ -23,6 +23,7 @@ import ( "github.com/zeta-chain/ethermint/x/evm/statedb" "github.com/zeta-chain/node/cmd/zetacored/config" "github.com/zeta-chain/node/precompiles/prototype" + ptypes "github.com/zeta-chain/node/precompiles/types" "github.com/zeta-chain/node/testutil/keeper" "github.com/zeta-chain/node/testutil/sample" @@ -238,7 +239,8 @@ func Test_InvalidABI(t *testing.T) { } func Test_Stake(t *testing.T) { - t.Run("should fail if validator doesn't exist", func(t *testing.T) { + // Disabled until further notice, check https://github.com/zeta-chain/node/issues/3005. + t.Run("should fail with error disabled", func(t *testing.T) { // ARRANGE ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) methodID := abi.Methods[StakeMethodName] @@ -263,520 +265,290 @@ func Test_Stake(t *testing.T) { // ASSERT require.Error(t, err) + require.ErrorIs(t, err, ptypes.ErrDisabledMethod{ + Method: StakeMethodName, + }) }) - t.Run("should stake", func(t *testing.T) { - // ARRANGE - ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) - methodID := abi.Methods[StakeMethodName] - r := rand.New(rand.NewSource(42)) - validator := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - - staker := sample.Bech32AccAddress() - stakerEthAddr := common.BytesToAddress(staker.Bytes()) - coins := sample.Coins() - err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) - require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) - require.NoError(t, err) - - stakerAddr := common.BytesToAddress(staker.Bytes()) - mockVMContract.CallerAddress = stakerAddr - args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} - mockVMContract.Input = packInputArgs(t, methodID, args...) - - // ACT - _, err = contract.Run(mockEVM, mockVMContract, false) - - // ASSERT - require.NoError(t, err) - }) - - t.Run("should fail if no input args", func(t *testing.T) { - // ARRANGE - ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) - methodID := abi.Methods[StakeMethodName] - r := rand.New(rand.NewSource(42)) - validator := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - - staker := sample.Bech32AccAddress() - coins := sample.Coins() - err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) - require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) - require.NoError(t, err) - - stakerAddr := common.BytesToAddress(staker.Bytes()) - mockVMContract.CallerAddress = stakerAddr - mockVMContract.Input = methodID.ID - - // ACT - _, err = contract.Run(mockEVM, mockVMContract, false) - - // ASSERT - require.Error(t, err) - }) - - t.Run("should fail if caller is not staker", func(t *testing.T) { - // ARRANGE - ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) - methodID := abi.Methods[StakeMethodName] - r := rand.New(rand.NewSource(42)) - validator := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - - staker := sample.Bech32AccAddress() - coins := sample.Coins() - err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) - require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) - require.NoError(t, err) - - stakerAddr := common.BytesToAddress(staker.Bytes()) - mockVMContract.CallerAddress = stakerAddr - - nonStakerAddr := common.BytesToAddress(sample.Bech32AccAddress().Bytes()) - args := []interface{}{nonStakerAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} - mockVMContract.Input = packInputArgs(t, methodID, args...) - - // ACT - _, err = contract.Run(mockEVM, mockVMContract, false) - - // ASSERT - require.ErrorContains(t, err, "caller is not staker address") - }) - - t.Run("should fail if staking fails", func(t *testing.T) { - // ARRANGE - ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) - methodID := abi.Methods[StakeMethodName] - r := rand.New(rand.NewSource(42)) - validator := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - - // staker without funds - staker := sample.Bech32AccAddress() - stakerEthAddr := common.BytesToAddress(staker.Bytes()) - coins := sample.Coins() - - stakerAddr := common.BytesToAddress(staker.Bytes()) - mockVMContract.CallerAddress = stakerAddr - - args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} - mockVMContract.Input = packInputArgs(t, methodID, args...) - - // ACT - _, err := contract.Run(mockEVM, mockVMContract, false) - - // ASSERT - require.Error(t, err) - }) - - t.Run("should fail if wrong args amount", func(t *testing.T) { - // ARRANGE - ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) - methodID := abi.Methods[StakeMethodName] - r := rand.New(rand.NewSource(42)) - validator := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - - staker := sample.Bech32AccAddress() - stakerEthAddr := common.BytesToAddress(staker.Bytes()) - coins := sample.Coins() - err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) - require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) - require.NoError(t, err) - - stakerAddr := common.BytesToAddress(staker.Bytes()) - - args := []interface{}{stakerEthAddr, validator.OperatorAddress} - - // ACT - _, err = contract.Stake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, args) - - // ASSERT - require.Error(t, err) - }) - - t.Run("should fail if staker is not eth addr", func(t *testing.T) { - // ARRANGE - ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) - methodID := abi.Methods[StakeMethodName] - r := rand.New(rand.NewSource(42)) - validator := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - - staker := sample.Bech32AccAddress() - coins := sample.Coins() - err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) - require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) - require.NoError(t, err) - - stakerAddr := common.BytesToAddress(staker.Bytes()) - - args := []interface{}{staker, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} - - // ACT - _, err = contract.Stake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, args) - - // ASSERT - require.Error(t, err) - }) - - t.Run("should fail if validator is not valid string", func(t *testing.T) { - // ARRANGE - ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) - methodID := abi.Methods[StakeMethodName] - r := rand.New(rand.NewSource(42)) - validator := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - - staker := sample.Bech32AccAddress() - stakerEthAddr := common.BytesToAddress(staker.Bytes()) - coins := sample.Coins() - err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) - require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) - require.NoError(t, err) - - stakerAddr := common.BytesToAddress(staker.Bytes()) - - args := []interface{}{stakerEthAddr, 42, coins.AmountOf(config.BaseDenom).BigInt()} - - // ACT - _, err = contract.Stake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, args) - - // ASSERT - require.Error(t, err) - }) - - t.Run("should fail if amount is not int64", func(t *testing.T) { - // ARRANGE - ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) - methodID := abi.Methods[StakeMethodName] - r := rand.New(rand.NewSource(42)) - validator := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - - staker := sample.Bech32AccAddress() - stakerEthAddr := common.BytesToAddress(staker.Bytes()) - coins := sample.Coins() - err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) - require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) - require.NoError(t, err) - - stakerAddr := common.BytesToAddress(staker.Bytes()) - - args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Uint64()} - - // ACT - _, err = contract.Stake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, args) - - // ASSERT - require.Error(t, err) - }) -} - -func Test_Unstake(t *testing.T) { - t.Run("should fail if validator doesn't exist", func(t *testing.T) { - // ARRANGE - ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) - methodID := abi.Methods[UnstakeMethodName] - r := rand.New(rand.NewSource(42)) - validator := sample.Validator(t, r) - - staker := sample.Bech32AccAddress() - stakerEthAddr := common.BytesToAddress(staker.Bytes()) - coins := sample.Coins() - err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) - require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) - require.NoError(t, err) - - stakerAddr := common.BytesToAddress(staker.Bytes()) - mockVMContract.CallerAddress = stakerAddr - - args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} - mockVMContract.Input = packInputArgs(t, methodID, args...) - - // ACT - _, err = contract.Run(mockEVM, mockVMContract, false) - - // ASSERT - require.Error(t, err) - }) - - t.Run("should unstake", func(t *testing.T) { - // ARRANGE - ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) - methodID := abi.Methods[UnstakeMethodName] - r := rand.New(rand.NewSource(42)) - validator := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - - staker := sample.Bech32AccAddress() - stakerEthAddr := common.BytesToAddress(staker.Bytes()) - coins := sample.Coins() - err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) - require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) - require.NoError(t, err) - - stakerAddr := common.BytesToAddress(staker.Bytes()) - mockVMContract.CallerAddress = stakerAddr - - args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} - - // stake first - stakeMethodID := abi.Methods[StakeMethodName] - mockVMContract.Input = packInputArgs(t, stakeMethodID, args...) - _, err = contract.Run(mockEVM, mockVMContract, false) - require.NoError(t, err) - - // ACT - mockVMContract.Input = packInputArgs(t, methodID, args...) - _, err = contract.Run(mockEVM, mockVMContract, false) - - // ASSERT - require.NoError(t, err) - }) - - t.Run("should fail if caller is not staker", func(t *testing.T) { - // ARRANGE - ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) - methodID := abi.Methods[UnstakeMethodName] - r := rand.New(rand.NewSource(42)) - validator := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - - staker := sample.Bech32AccAddress() - stakerEthAddr := common.BytesToAddress(staker.Bytes()) - coins := sample.Coins() - err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) - require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) - require.NoError(t, err) - - stakerAddr := common.BytesToAddress(staker.Bytes()) - mockVMContract.CallerAddress = stakerAddr - - args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} - // stake first - stakeMethodID := abi.Methods[StakeMethodName] - mockVMContract.Input = packInputArgs(t, stakeMethodID, args...) - _, err = contract.Run(mockEVM, mockVMContract, false) - require.NoError(t, err) - - callerEthAddr := common.BytesToAddress(sample.Bech32AccAddress().Bytes()) - mockVMContract.CallerAddress = callerEthAddr - mockVMContract.Input = packInputArgs(t, methodID, args...) - - // ACT - _, err = contract.Run(mockEVM, mockVMContract, false) - - // ASSERT - require.ErrorContains(t, err, "caller is not staker address") - }) - - t.Run("should fail if no previous staking", func(t *testing.T) { - // ARRANGE - ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) - methodID := abi.Methods[UnstakeMethodName] - r := rand.New(rand.NewSource(42)) - validator := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - - staker := sample.Bech32AccAddress() - stakerEthAddr := common.BytesToAddress(staker.Bytes()) - coins := sample.Coins() - err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) - require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) - require.NoError(t, err) - - stakerAddr := common.BytesToAddress(staker.Bytes()) - mockVMContract.CallerAddress = stakerAddr - - args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} - mockVMContract.Input = packInputArgs(t, methodID, args...) - - // ACT - _, err = contract.Run(mockEVM, mockVMContract, false) - - // ASSERT - require.Error(t, err) - }) - - t.Run("should fail if wrong args amount", func(t *testing.T) { - // ARRANGE - ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) - methodID := abi.Methods[UnstakeMethodName] - r := rand.New(rand.NewSource(42)) - validator := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - - staker := sample.Bech32AccAddress() - stakerEthAddr := common.BytesToAddress(staker.Bytes()) - coins := sample.Coins() - err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) - require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) - require.NoError(t, err) - - stakerAddr := common.BytesToAddress(staker.Bytes()) - - args := []interface{}{stakerEthAddr, validator.OperatorAddress} - - // ACT - _, err = contract.Unstake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, args) - - // ASSERT - require.Error(t, err) - }) - - t.Run("should fail if staker is not eth addr", func(t *testing.T) { - // ARRANGE - ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) - methodID := abi.Methods[UnstakeMethodName] - r := rand.New(rand.NewSource(42)) - validator := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - - staker := sample.Bech32AccAddress() - coins := sample.Coins() - err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) - require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) - require.NoError(t, err) - - stakerAddr := common.BytesToAddress(staker.Bytes()) - - args := []interface{}{staker, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} - - // ACT - _, err = contract.Unstake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, args) - - // ASSERT - require.Error(t, err) - }) - - t.Run("should fail if validator is not valid string", func(t *testing.T) { - // ARRANGE - ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) - methodID := abi.Methods[UnstakeMethodName] - r := rand.New(rand.NewSource(42)) - validator := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - - staker := sample.Bech32AccAddress() - stakerEthAddr := common.BytesToAddress(staker.Bytes()) - coins := sample.Coins() - err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) - require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) - require.NoError(t, err) - - stakerAddr := common.BytesToAddress(staker.Bytes()) - - args := []interface{}{stakerEthAddr, 42, coins.AmountOf(config.BaseDenom).BigInt()} - - // ACT - _, err = contract.Unstake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, args) - - // ASSERT - require.Error(t, err) - }) - - t.Run("should fail if amount is not int64", func(t *testing.T) { - // ARRANGE - ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) - methodID := abi.Methods[UnstakeMethodName] - r := rand.New(rand.NewSource(42)) - validator := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - - staker := sample.Bech32AccAddress() - stakerEthAddr := common.BytesToAddress(staker.Bytes()) - coins := sample.Coins() - err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) - require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) - require.NoError(t, err) - - stakerAddr := common.BytesToAddress(staker.Bytes()) - - args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Uint64()} - - // ACT - _, err = contract.Unstake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, args) - - // ASSERT - require.Error(t, err) - }) -} - -func Test_MoveStake(t *testing.T) { - t.Run("should fail if validator dest doesn't exist", func(t *testing.T) { - // ARRANGE - ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) - methodID := abi.Methods[MoveStakeMethodName] - r := rand.New(rand.NewSource(42)) - validatorSrc := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) - validatorDest := sample.Validator(t, r) - - staker := sample.Bech32AccAddress() - stakerEthAddr := common.BytesToAddress(staker.Bytes()) - coins := sample.Coins() - err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) - require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) - require.NoError(t, err) - - stakerAddr := common.BytesToAddress(staker.Bytes()) - mockVMContract.CallerAddress = stakerAddr - - argsStake := []interface{}{ - stakerEthAddr, - validatorSrc.OperatorAddress, - coins.AmountOf(config.BaseDenom).BigInt(), - } - - // stake to validator src - stakeMethodID := abi.Methods[StakeMethodName] - mockVMContract.Input = packInputArgs(t, stakeMethodID, argsStake...) - _, err = contract.Run(mockEVM, mockVMContract, false) - require.NoError(t, err) - - argsMoveStake := []interface{}{ - stakerEthAddr, - validatorSrc.OperatorAddress, - validatorDest.OperatorAddress, - coins.AmountOf(config.BaseDenom).BigInt(), - } - mockVMContract.Input = packInputArgs(t, methodID, argsMoveStake...) - - // ACT - _, err = contract.Run(mockEVM, mockVMContract, false) - - // ASSERT - require.Error(t, err) - }) - - t.Run("should move stake", func(t *testing.T) { + // t.Run("should fail in read only mode", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) + // methodID := abi.Methods[StakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validator := sample.Validator(t, r) + + // staker := sample.Bech32AccAddress() + // stakerEthAddr := common.BytesToAddress(staker.Bytes()) + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + // mockVMContract.CallerAddress = stakerAddr + // args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} + // mockVMContract.Input = packInputArgs(t, methodID, args...) + + // // ACT + // _, err = contract.Run(mockEVM, mockVMContract, true) + + // // ASSERT + // require.ErrorIs(t, err, ptypes.ErrWriteMethod{Method: StakeMethodName}) + // }) + + // t.Run("should fail if validator doesn't exist", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) + // methodID := abi.Methods[StakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validator := sample.Validator(t, r) + + // staker := sample.Bech32AccAddress() + // stakerEthAddr := common.BytesToAddress(staker.Bytes()) + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + // mockVMContract.CallerAddress = stakerAddr + // args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} + // mockVMContract.Input = packInputArgs(t, methodID, args...) + + // // ACT + // _, err = contract.Run(mockEVM, mockVMContract, false) + + // // ASSERT + // require.Error(t, err) + // }) + + // t.Run("should stake", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) + // methodID := abi.Methods[StakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validator := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + // staker := sample.Bech32AccAddress() + // stakerEthAddr := common.BytesToAddress(staker.Bytes()) + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + // mockVMContract.CallerAddress = stakerAddr + // args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} + // mockVMContract.Input = packInputArgs(t, methodID, args...) + + // // ACT + // _, err = contract.Run(mockEVM, mockVMContract, false) + + // // ASSERT + // require.NoError(t, err) + // }) + + // t.Run("should fail if no input args", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) + // methodID := abi.Methods[StakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validator := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + // staker := sample.Bech32AccAddress() + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + // mockVMContract.CallerAddress = stakerAddr + // mockVMContract.Input = methodID.ID + + // // ACT + // _, err = contract.Run(mockEVM, mockVMContract, false) + + // // ASSERT + // require.Error(t, err) + // }) + + // t.Run("should fail if caller is not staker", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) + // methodID := abi.Methods[StakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validator := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + // staker := sample.Bech32AccAddress() + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + // mockVMContract.CallerAddress = stakerAddr + + // nonStakerAddr := common.BytesToAddress(sample.Bech32AccAddress().Bytes()) + // args := []interface{}{nonStakerAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} + // mockVMContract.Input = packInputArgs(t, methodID, args...) + + // // ACT + // _, err = contract.Run(mockEVM, mockVMContract, false) + + // // ASSERT + // require.ErrorContains(t, err, "caller is not staker address") + // }) + + // t.Run("should fail if staking fails", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) + // methodID := abi.Methods[StakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validator := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + // // staker without funds + // staker := sample.Bech32AccAddress() + // stakerEthAddr := common.BytesToAddress(staker.Bytes()) + // coins := sample.Coins() + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + // mockVMContract.CallerAddress = stakerAddr + + // args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} + // mockVMContract.Input = packInputArgs(t, methodID, args...) + + // // ACT + // _, err := contract.Run(mockEVM, mockVMContract, false) + + // // ASSERT + // require.Error(t, err) + // }) + + // t.Run("should fail if wrong args amount", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) + // methodID := abi.Methods[StakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validator := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + // staker := sample.Bech32AccAddress() + // stakerEthAddr := common.BytesToAddress(staker.Bytes()) + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + + // args := []interface{}{stakerEthAddr, validator.OperatorAddress} + + // // ACT + // _, err = contract.Stake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, args) + + // // ASSERT + // require.Error(t, err) + // }) + + // t.Run("should fail if staker is not eth addr", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) + // methodID := abi.Methods[StakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validator := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + // staker := sample.Bech32AccAddress() + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + + // args := []interface{}{staker, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} + + // // ACT + // _, err = contract.Stake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, args) + + // // ASSERT + // require.Error(t, err) + // }) + + // t.Run("should fail if validator is not valid string", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) + // methodID := abi.Methods[StakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validator := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + // staker := sample.Bech32AccAddress() + // stakerEthAddr := common.BytesToAddress(staker.Bytes()) + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + + // args := []interface{}{stakerEthAddr, 42, coins.AmountOf(config.BaseDenom).BigInt()} + + // // ACT + // _, err = contract.Stake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, args) + + // // ASSERT + // require.Error(t, err) + // }) + + // t.Run("should fail if amount is not int64", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) + // methodID := abi.Methods[StakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validator := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + // staker := sample.Bech32AccAddress() + // stakerEthAddr := common.BytesToAddress(staker.Bytes()) + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + + // args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Uint64()} + + // // ACT + // _, err = contract.Stake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, args) + + // // ASSERT + // require.Error(t, err) + // }) +} + +func Test_Unstake(t *testing.T) { + // Disabled until further notice, check https://github.com/zeta-chain/node/issues/3005. + t.Run("should fail with error disabled", func(t *testing.T) { // ARRANGE ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) - methodID := abi.Methods[MoveStakeMethodName] + methodID := abi.Methods[UnstakeMethodName] r := rand.New(rand.NewSource(42)) - validatorSrc := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) - validatorDest := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) + validator := sample.Validator(t, r) staker := sample.Bech32AccAddress() stakerEthAddr := common.BytesToAddress(staker.Bytes()) @@ -788,255 +560,288 @@ func Test_MoveStake(t *testing.T) { stakerAddr := common.BytesToAddress(staker.Bytes()) mockVMContract.CallerAddress = stakerAddr - argsStake := []interface{}{ - stakerEthAddr, - validatorSrc.OperatorAddress, - coins.AmountOf(config.BaseDenom).BigInt(), - } - // stake to validator src - stakeMethodID := abi.Methods[StakeMethodName] - mockVMContract.Input = packInputArgs(t, stakeMethodID, argsStake...) - _, err = contract.Run(mockEVM, mockVMContract, false) - require.NoError(t, err) - - argsMoveStake := []interface{}{ - stakerEthAddr, - validatorSrc.OperatorAddress, - validatorDest.OperatorAddress, - coins.AmountOf(config.BaseDenom).BigInt(), - } - mockVMContract.Input = packInputArgs(t, methodID, argsMoveStake...) + args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} + mockVMContract.Input = packInputArgs(t, methodID, args...) // ACT - // move stake to validator dest _, err = contract.Run(mockEVM, mockVMContract, false) - // ASSERT - require.NoError(t, err) - }) - - t.Run("should fail if staker is invalid arg", func(t *testing.T) { - // ARRANGE - ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) - methodID := abi.Methods[MoveStakeMethodName] - r := rand.New(rand.NewSource(42)) - validatorSrc := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) - validatorDest := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) - - staker := sample.Bech32AccAddress() - stakerEthAddr := common.BytesToAddress(staker.Bytes()) - coins := sample.Coins() - err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) - require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) - require.NoError(t, err) - - stakerAddr := common.BytesToAddress(staker.Bytes()) - - argsStake := []interface{}{ - stakerEthAddr, - validatorSrc.OperatorAddress, - coins.AmountOf(config.BaseDenom).BigInt(), - } - - // stake to validator src - stakeMethodID := abi.Methods[StakeMethodName] - _, err = contract.Stake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &stakeMethodID, argsStake) - require.NoError(t, err) - - argsMoveStake := []interface{}{ - 42, - validatorSrc.OperatorAddress, - validatorDest.OperatorAddress, - coins.AmountOf(config.BaseDenom).BigInt(), - } - - // ACT - _, err = contract.MoveStake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, argsMoveStake) - - // ASSERT - require.Error(t, err) - }) - - t.Run("should fail if validator src is invalid arg", func(t *testing.T) { - // ARRANGE - ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) - methodID := abi.Methods[MoveStakeMethodName] - r := rand.New(rand.NewSource(42)) - validatorSrc := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) - validatorDest := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) - - staker := sample.Bech32AccAddress() - stakerEthAddr := common.BytesToAddress(staker.Bytes()) - coins := sample.Coins() - err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) - require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) - require.NoError(t, err) - - stakerAddr := common.BytesToAddress(staker.Bytes()) - - argsStake := []interface{}{ - stakerEthAddr, - validatorSrc.OperatorAddress, - coins.AmountOf(config.BaseDenom).BigInt(), - } - - // stake to validator src - stakeMethodID := abi.Methods[StakeMethodName] - _, err = contract.Stake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &stakeMethodID, argsStake) - require.NoError(t, err) - - argsMoveStake := []interface{}{ - stakerEthAddr, - 42, - validatorDest.OperatorAddress, - coins.AmountOf(config.BaseDenom).BigInt(), - } - - // ACT - _, err = contract.MoveStake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, argsMoveStake) - - // ASSERT - require.Error(t, err) - }) - - t.Run("should fail if validator dest is invalid arg", func(t *testing.T) { - // ARRANGE - ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) - methodID := abi.Methods[MoveStakeMethodName] - r := rand.New(rand.NewSource(42)) - validatorSrc := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) - validatorDest := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) - - staker := sample.Bech32AccAddress() - stakerEthAddr := common.BytesToAddress(staker.Bytes()) - coins := sample.Coins() - err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) - require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) - require.NoError(t, err) - - stakerAddr := common.BytesToAddress(staker.Bytes()) - - argsStake := []interface{}{ - stakerEthAddr, - validatorSrc.OperatorAddress, - coins.AmountOf(config.BaseDenom).BigInt(), - } - - // stake to validator src - stakeMethodID := abi.Methods[StakeMethodName] - _, err = contract.Stake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &stakeMethodID, argsStake) - require.NoError(t, err) - - argsMoveStake := []interface{}{ - stakerEthAddr, - validatorSrc.OperatorAddress, - 42, - coins.AmountOf(config.BaseDenom).BigInt(), - } - - // ACT - _, err = contract.MoveStake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, argsMoveStake) - - // ASSERT - require.Error(t, err) - }) - - t.Run("should fail if amount is invalid arg", func(t *testing.T) { - // ARRANGE - ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) - methodID := abi.Methods[MoveStakeMethodName] - r := rand.New(rand.NewSource(42)) - validatorSrc := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) - validatorDest := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) - - staker := sample.Bech32AccAddress() - stakerEthAddr := common.BytesToAddress(staker.Bytes()) - coins := sample.Coins() - err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) - require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) - require.NoError(t, err) - - stakerAddr := common.BytesToAddress(staker.Bytes()) - - argsStake := []interface{}{ - stakerEthAddr, - validatorSrc.OperatorAddress, - coins.AmountOf(config.BaseDenom).BigInt(), - } - - // stake to validator src - stakeMethodID := abi.Methods[StakeMethodName] - _, err = contract.Stake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &stakeMethodID, argsStake) - require.NoError(t, err) - - argsMoveStake := []interface{}{ - stakerEthAddr, - validatorSrc.OperatorAddress, - validatorDest.OperatorAddress, - coins.AmountOf(config.BaseDenom).Uint64(), - } - - // ACT - _, err = contract.MoveStake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, argsMoveStake) - // ASSERT require.Error(t, err) + require.ErrorIs(t, err, ptypes.ErrDisabledMethod{ + Method: UnstakeMethodName, + }) }) - t.Run("should fail if wrong args amount", func(t *testing.T) { - // ARRANGE - ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) - methodID := abi.Methods[MoveStakeMethodName] - r := rand.New(rand.NewSource(42)) - validatorSrc := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) - validatorDest := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) - - staker := sample.Bech32AccAddress() - stakerEthAddr := common.BytesToAddress(staker.Bytes()) - coins := sample.Coins() - err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) - require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) - require.NoError(t, err) - - stakerAddr := common.BytesToAddress(staker.Bytes()) - - argsStake := []interface{}{ - stakerEthAddr, - validatorSrc.OperatorAddress, - coins.AmountOf(config.BaseDenom).BigInt(), - } - - // stake to validator src - stakeMethodID := abi.Methods[StakeMethodName] - _, err = contract.Stake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &stakeMethodID, argsStake) - require.NoError(t, err) - - argsMoveStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress} - - // ACT - _, err = contract.MoveStake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, argsMoveStake) - - // ASSERT - require.Error(t, err) - }) + // t.Run("should fail in read only method", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) + // methodID := abi.Methods[UnstakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validator := sample.Validator(t, r) + + // staker := sample.Bech32AccAddress() + // stakerEthAddr := common.BytesToAddress(staker.Bytes()) + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + // mockVMContract.CallerAddress = stakerAddr + + // args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} + // mockVMContract.Input = packInputArgs(t, methodID, args...) + + // // ACT + // _, err = contract.Run(mockEVM, mockVMContract, true) + + // // ASSERT + // require.ErrorIs(t, err, ptypes.ErrWriteMethod{Method: UnstakeMethodName}) + // }) + + // t.Run("should fail if validator doesn't exist", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) + // methodID := abi.Methods[UnstakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validator := sample.Validator(t, r) + + // staker := sample.Bech32AccAddress() + // stakerEthAddr := common.BytesToAddress(staker.Bytes()) + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + // mockVMContract.CallerAddress = stakerAddr + + // args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} + // mockVMContract.Input = packInputArgs(t, methodID, args...) + + // // ACT + // _, err = contract.Run(mockEVM, mockVMContract, false) + + // // ASSERT + // require.Error(t, err) + // }) + + // t.Run("should unstake", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) + // methodID := abi.Methods[UnstakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validator := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + // staker := sample.Bech32AccAddress() + // stakerEthAddr := common.BytesToAddress(staker.Bytes()) + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + // mockVMContract.CallerAddress = stakerAddr + + // args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} + + // // stake first + // stakeMethodID := abi.Methods[StakeMethodName] + // mockVMContract.Input = packInputArgs(t, stakeMethodID, args...) + // _, err = contract.Run(mockEVM, mockVMContract, false) + // require.NoError(t, err) + + // // ACT + // mockVMContract.Input = packInputArgs(t, methodID, args...) + // _, err = contract.Run(mockEVM, mockVMContract, false) + + // // ASSERT + // require.NoError(t, err) + // }) + + // t.Run("should fail if caller is not staker", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) + // methodID := abi.Methods[UnstakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validator := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + // staker := sample.Bech32AccAddress() + // stakerEthAddr := common.BytesToAddress(staker.Bytes()) + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + // mockVMContract.CallerAddress = stakerAddr + + // args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} + // // stake first + // stakeMethodID := abi.Methods[StakeMethodName] + // mockVMContract.Input = packInputArgs(t, stakeMethodID, args...) + // _, err = contract.Run(mockEVM, mockVMContract, false) + // require.NoError(t, err) + + // callerEthAddr := common.BytesToAddress(sample.Bech32AccAddress().Bytes()) + // mockVMContract.CallerAddress = callerEthAddr + // mockVMContract.Input = packInputArgs(t, methodID, args...) + + // // ACT + // _, err = contract.Run(mockEVM, mockVMContract, false) + + // // ASSERT + // require.ErrorContains(t, err, "caller is not staker address") + // }) + + // t.Run("should fail if no previous staking", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) + // methodID := abi.Methods[UnstakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validator := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + // staker := sample.Bech32AccAddress() + // stakerEthAddr := common.BytesToAddress(staker.Bytes()) + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + // mockVMContract.CallerAddress = stakerAddr + + // args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} + // mockVMContract.Input = packInputArgs(t, methodID, args...) + + // // ACT + // _, err = contract.Run(mockEVM, mockVMContract, false) + + // // ASSERT + // require.Error(t, err) + // }) + + // t.Run("should fail if wrong args amount", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) + // methodID := abi.Methods[UnstakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validator := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + // staker := sample.Bech32AccAddress() + // stakerEthAddr := common.BytesToAddress(staker.Bytes()) + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + + // args := []interface{}{stakerEthAddr, validator.OperatorAddress} + + // // ACT + // _, err = contract.Unstake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, args) + + // // ASSERT + // require.Error(t, err) + // }) + + // t.Run("should fail if staker is not eth addr", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) + // methodID := abi.Methods[UnstakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validator := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + // staker := sample.Bech32AccAddress() + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + + // args := []interface{}{staker, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} + + // // ACT + // _, err = contract.Unstake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, args) + + // // ASSERT + // require.Error(t, err) + // }) + + // t.Run("should fail if validator is not valid string", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) + // methodID := abi.Methods[UnstakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validator := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + // staker := sample.Bech32AccAddress() + // stakerEthAddr := common.BytesToAddress(staker.Bytes()) + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + + // args := []interface{}{stakerEthAddr, 42, coins.AmountOf(config.BaseDenom).BigInt()} + + // // ACT + // _, err = contract.Unstake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, args) + + // // ASSERT + // require.Error(t, err) + // }) + + // t.Run("should fail if amount is not int64", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) + // methodID := abi.Methods[UnstakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validator := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + // staker := sample.Bech32AccAddress() + // stakerEthAddr := common.BytesToAddress(staker.Bytes()) + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + + // args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Uint64()} + + // // ACT + // _, err = contract.Unstake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, args) + + // // ASSERT + // require.Error(t, err) + // }) +} - t.Run("should fail if caller is not staker", func(t *testing.T) { +func Test_MoveStake(t *testing.T) { + // Disabled until further notice, check https://github.com/zeta-chain/node/issues/3005. + t.Run("should fail with error disabled", func(t *testing.T) { // ARRANGE ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) methodID := abi.Methods[MoveStakeMethodName] @@ -1044,7 +849,6 @@ func Test_MoveStake(t *testing.T) { validatorSrc := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) validatorDest := sample.Validator(t, r) - sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) staker := sample.Bech32AccAddress() stakerEthAddr := common.BytesToAddress(staker.Bytes()) @@ -1056,6 +860,7 @@ func Test_MoveStake(t *testing.T) { stakerAddr := common.BytesToAddress(staker.Bytes()) mockVMContract.CallerAddress = stakerAddr + argsStake := []interface{}{ stakerEthAddr, validatorSrc.OperatorAddress, @@ -1066,7 +871,10 @@ func Test_MoveStake(t *testing.T) { stakeMethodID := abi.Methods[StakeMethodName] mockVMContract.Input = packInputArgs(t, stakeMethodID, argsStake...) _, err = contract.Run(mockEVM, mockVMContract, false) - require.NoError(t, err) + require.Error(t, err) + require.ErrorIs(t, err, ptypes.ErrDisabledMethod{ + Method: StakeMethodName, + }) argsMoveStake := []interface{}{ stakerEthAddr, @@ -1076,15 +884,427 @@ func Test_MoveStake(t *testing.T) { } mockVMContract.Input = packInputArgs(t, methodID, argsMoveStake...) - callerEthAddr := common.BytesToAddress(sample.Bech32AccAddress().Bytes()) - mockVMContract.CallerAddress = callerEthAddr - // ACT _, err = contract.Run(mockEVM, mockVMContract, false) // ASSERT - require.ErrorContains(t, err, "caller is not staker") + require.Error(t, err) + require.ErrorIs(t, err, ptypes.ErrDisabledMethod{ + Method: MoveStakeMethodName, + }) }) + + // t.Run("should fail in read only method", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) + // methodID := abi.Methods[MoveStakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validatorSrc := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) + // validatorDest := sample.Validator(t, r) + + // staker := sample.Bech32AccAddress() + // stakerEthAddr := common.BytesToAddress(staker.Bytes()) + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + // mockVMContract.CallerAddress = stakerAddr + + // argsStake := []interface{}{ + // stakerEthAddr, + // validatorSrc.OperatorAddress, + // coins.AmountOf(config.BaseDenom).BigInt(), + // } + + // // stake to validator src + // stakeMethodID := abi.Methods[StakeMethodName] + // mockVMContract.Input = packInputArgs(t, stakeMethodID, argsStake...) + // _, err = contract.Run(mockEVM, mockVMContract, false) + // require.NoError(t, err) + + // argsMoveStake := []interface{}{ + // stakerEthAddr, + // validatorSrc.OperatorAddress, + // validatorDest.OperatorAddress, + // coins.AmountOf(config.BaseDenom).BigInt(), + // } + // mockVMContract.Input = packInputArgs(t, methodID, argsMoveStake...) + + // // ACT + // _, err = contract.Run(mockEVM, mockVMContract, true) + + // // ASSERT + // require.ErrorIs(t, err, ptypes.ErrWriteMethod{Method: MoveStakeMethodName}) + // }) + + // t.Run("should fail if validator dest doesn't exist", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) + // methodID := abi.Methods[MoveStakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validatorSrc := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) + // validatorDest := sample.Validator(t, r) + + // staker := sample.Bech32AccAddress() + // stakerEthAddr := common.BytesToAddress(staker.Bytes()) + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + // mockVMContract.CallerAddress = stakerAddr + + // argsStake := []interface{}{ + // stakerEthAddr, + // validatorSrc.OperatorAddress, + // coins.AmountOf(config.BaseDenom).BigInt(), + // } + + // // stake to validator src + // stakeMethodID := abi.Methods[StakeMethodName] + // mockVMContract.Input = packInputArgs(t, stakeMethodID, argsStake...) + // _, err = contract.Run(mockEVM, mockVMContract, false) + // require.NoError(t, err) + + // argsMoveStake := []interface{}{ + // stakerEthAddr, + // validatorSrc.OperatorAddress, + // validatorDest.OperatorAddress, + // coins.AmountOf(config.BaseDenom).BigInt(), + // } + // mockVMContract.Input = packInputArgs(t, methodID, argsMoveStake...) + + // // ACT + // _, err = contract.Run(mockEVM, mockVMContract, false) + + // // ASSERT + // require.Error(t, err) + // }) + + // t.Run("should move stake", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) + // methodID := abi.Methods[MoveStakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validatorSrc := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) + // validatorDest := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) + + // staker := sample.Bech32AccAddress() + // stakerEthAddr := common.BytesToAddress(staker.Bytes()) + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + // mockVMContract.CallerAddress = stakerAddr + // argsStake := []interface{}{ + // stakerEthAddr, + // validatorSrc.OperatorAddress, + // coins.AmountOf(config.BaseDenom).BigInt(), + // } + + // // stake to validator src + // stakeMethodID := abi.Methods[StakeMethodName] + // mockVMContract.Input = packInputArgs(t, stakeMethodID, argsStake...) + // _, err = contract.Run(mockEVM, mockVMContract, false) + // require.NoError(t, err) + + // argsMoveStake := []interface{}{ + // stakerEthAddr, + // validatorSrc.OperatorAddress, + // validatorDest.OperatorAddress, + // coins.AmountOf(config.BaseDenom).BigInt(), + // } + // mockVMContract.Input = packInputArgs(t, methodID, argsMoveStake...) + + // // ACT + // // move stake to validator dest + // _, err = contract.Run(mockEVM, mockVMContract, false) + + // // ASSERT + // require.NoError(t, err) + // }) + + // t.Run("should fail if staker is invalid arg", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) + // methodID := abi.Methods[MoveStakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validatorSrc := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) + // validatorDest := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) + + // staker := sample.Bech32AccAddress() + // stakerEthAddr := common.BytesToAddress(staker.Bytes()) + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + + // argsStake := []interface{}{ + // stakerEthAddr, + // validatorSrc.OperatorAddress, + // coins.AmountOf(config.BaseDenom).BigInt(), + // } + + // // stake to validator src + // stakeMethodID := abi.Methods[StakeMethodName] + // _, err = contract.Stake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &stakeMethodID, argsStake) + // require.NoError(t, err) + + // argsMoveStake := []interface{}{ + // 42, + // validatorSrc.OperatorAddress, + // validatorDest.OperatorAddress, + // coins.AmountOf(config.BaseDenom).BigInt(), + // } + + // // ACT + // _, err = contract.MoveStake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, argsMoveStake) + + // // ASSERT + // require.Error(t, err) + // }) + + // t.Run("should fail if validator src is invalid arg", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) + // methodID := abi.Methods[MoveStakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validatorSrc := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) + // validatorDest := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) + + // staker := sample.Bech32AccAddress() + // stakerEthAddr := common.BytesToAddress(staker.Bytes()) + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + + // argsStake := []interface{}{ + // stakerEthAddr, + // validatorSrc.OperatorAddress, + // coins.AmountOf(config.BaseDenom).BigInt(), + // } + + // // stake to validator src + // stakeMethodID := abi.Methods[StakeMethodName] + // _, err = contract.Stake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &stakeMethodID, argsStake) + // require.NoError(t, err) + + // argsMoveStake := []interface{}{ + // stakerEthAddr, + // 42, + // validatorDest.OperatorAddress, + // coins.AmountOf(config.BaseDenom).BigInt(), + // } + + // // ACT + // _, err = contract.MoveStake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, argsMoveStake) + + // // ASSERT + // require.Error(t, err) + // }) + + // t.Run("should fail if validator dest is invalid arg", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) + // methodID := abi.Methods[MoveStakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validatorSrc := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) + // validatorDest := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) + + // staker := sample.Bech32AccAddress() + // stakerEthAddr := common.BytesToAddress(staker.Bytes()) + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + + // argsStake := []interface{}{ + // stakerEthAddr, + // validatorSrc.OperatorAddress, + // coins.AmountOf(config.BaseDenom).BigInt(), + // } + + // // stake to validator src + // stakeMethodID := abi.Methods[StakeMethodName] + // _, err = contract.Stake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &stakeMethodID, argsStake) + // require.NoError(t, err) + + // argsMoveStake := []interface{}{ + // stakerEthAddr, + // validatorSrc.OperatorAddress, + // 42, + // coins.AmountOf(config.BaseDenom).BigInt(), + // } + + // // ACT + // _, err = contract.MoveStake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, argsMoveStake) + + // // ASSERT + // require.Error(t, err) + // }) + + // t.Run("should fail if amount is invalid arg", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) + // methodID := abi.Methods[MoveStakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validatorSrc := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) + // validatorDest := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) + + // staker := sample.Bech32AccAddress() + // stakerEthAddr := common.BytesToAddress(staker.Bytes()) + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + + // argsStake := []interface{}{ + // stakerEthAddr, + // validatorSrc.OperatorAddress, + // coins.AmountOf(config.BaseDenom).BigInt(), + // } + + // // stake to validator src + // stakeMethodID := abi.Methods[StakeMethodName] + // _, err = contract.Stake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &stakeMethodID, argsStake) + // require.NoError(t, err) + + // argsMoveStake := []interface{}{ + // stakerEthAddr, + // validatorSrc.OperatorAddress, + // validatorDest.OperatorAddress, + // coins.AmountOf(config.BaseDenom).Uint64(), + // } + + // // ACT + // _, err = contract.MoveStake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, argsMoveStake) + + // // ASSERT + // require.Error(t, err) + // }) + + // t.Run("should fail if wrong args amount", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, _ := setup(t) + // methodID := abi.Methods[MoveStakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validatorSrc := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) + // validatorDest := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) + + // staker := sample.Bech32AccAddress() + // stakerEthAddr := common.BytesToAddress(staker.Bytes()) + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + + // argsStake := []interface{}{ + // stakerEthAddr, + // validatorSrc.OperatorAddress, + // coins.AmountOf(config.BaseDenom).BigInt(), + // } + + // // stake to validator src + // stakeMethodID := abi.Methods[StakeMethodName] + // _, err = contract.Stake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &stakeMethodID, argsStake) + // require.NoError(t, err) + + // argsMoveStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress} + + // // ACT + // _, err = contract.MoveStake(ctx, mockEVM, &vm.Contract{CallerAddress: stakerAddr}, &methodID, argsMoveStake) + + // // ASSERT + // require.Error(t, err) + // }) + + // t.Run("should fail if caller is not staker", func(t *testing.T) { + // // ARRANGE + // ctx, contract, abi, sdkKeepers, mockEVM, mockVMContract := setup(t) + // methodID := abi.Methods[MoveStakeMethodName] + // r := rand.New(rand.NewSource(42)) + // validatorSrc := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) + // validatorDest := sample.Validator(t, r) + // sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) + + // staker := sample.Bech32AccAddress() + // stakerEthAddr := common.BytesToAddress(staker.Bytes()) + // coins := sample.Coins() + // err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + // require.NoError(t, err) + // err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + // require.NoError(t, err) + + // stakerAddr := common.BytesToAddress(staker.Bytes()) + // mockVMContract.CallerAddress = stakerAddr + // argsStake := []interface{}{ + // stakerEthAddr, + // validatorSrc.OperatorAddress, + // coins.AmountOf(config.BaseDenom).BigInt(), + // } + + // // stake to validator src + // stakeMethodID := abi.Methods[StakeMethodName] + // mockVMContract.Input = packInputArgs(t, stakeMethodID, argsStake...) + // _, err = contract.Run(mockEVM, mockVMContract, false) + // require.NoError(t, err) + + // argsMoveStake := []interface{}{ + // stakerEthAddr, + // validatorSrc.OperatorAddress, + // validatorDest.OperatorAddress, + // coins.AmountOf(config.BaseDenom).BigInt(), + // } + // mockVMContract.Input = packInputArgs(t, methodID, argsMoveStake...) + + // callerEthAddr := common.BytesToAddress(sample.Bech32AccAddress().Bytes()) + // mockVMContract.CallerAddress = callerEthAddr + + // // ACT + // _, err = contract.Run(mockEVM, mockVMContract, false) + + // // ASSERT + // require.ErrorContains(t, err, "caller is not staker") + // }) } func Test_GetAllValidators(t *testing.T) { diff --git a/precompiles/types/errors.go b/precompiles/types/errors.go index 03e397fa14..b1ad69d258 100644 --- a/precompiles/types/errors.go +++ b/precompiles/types/errors.go @@ -94,6 +94,22 @@ func (e ErrInvalidMethod) Error() string { return fmt.Sprintf("invalid method: %s", e.Method) } +type ErrDisabledMethod struct { + Method string +} + +func (e ErrDisabledMethod) Error() string { + return fmt.Sprintf("method %s is disabled", e.Method) +} + +type ErrWriteMethod struct { + Method string +} + +func (e ErrWriteMethod) Error() string { + return fmt.Sprintf("method not allowed in read-only mode: %s", e.Method) +} + type ErrUnexpected struct { When string Got string diff --git a/precompiles/types/errors_test.go b/precompiles/types/errors_test.go index c80647a08d..d432aa901c 100644 --- a/precompiles/types/errors_test.go +++ b/precompiles/types/errors_test.go @@ -13,9 +13,7 @@ func Test_ErrInvalidAddr(t *testing.T) { } got := e.Error() expect := "invalid address foo, reason: bar" - if got != expect { - t.Errorf("Expected %v, got %v", expect, got) - } + require.Equal(t, expect, got) require.ErrorIs(t, ErrInvalidAddr{"foo", "bar"}, e) } @@ -26,9 +24,7 @@ func Test_ErrInvalidNumberOfArgs(t *testing.T) { } got := e.Error() expect := "invalid number of arguments; expected 2; got: 1" - if got != expect { - t.Errorf("Expected %v, got %v", expect, got) - } + require.Equal(t, expect, got) require.ErrorIs(t, ErrInvalidNumberOfArgs{1, 2}, e) } @@ -38,9 +34,7 @@ func Test_ErrInvalidArgument(t *testing.T) { } got := e.Error() expect := "invalid argument: foo" - if got != expect { - t.Errorf("Expected %v, got %v", expect, got) - } + require.Equal(t, expect, got) require.ErrorIs(t, ErrInvalidArgument{"foo"}, e) } @@ -50,9 +44,7 @@ func Test_ErrInvalidMethod(t *testing.T) { } got := e.Error() expect := "invalid method: foo" - if got != expect { - t.Errorf("Expected %v, got %v", expect, got) - } + require.Equal(t, expect, got) require.ErrorIs(t, ErrInvalidMethod{"foo"}, e) } @@ -65,9 +57,7 @@ func Test_ErrInvalidCoin(t *testing.T) { } got := e.Error() expect := "invalid coin: denom: foo, is negative: true, is nil: false, is empty: false" - if got != expect { - t.Errorf("Expected %v, got %v", expect, got) - } + require.Equal(t, expect, got) require.ErrorIs(t, ErrInvalidCoin{"foo", true, false, false}, e) } @@ -77,9 +67,7 @@ func Test_ErrInvalidAmount(t *testing.T) { } got := e.Error() expect := "invalid token amount: foo" - if got != expect { - t.Errorf("Expected %v, got %v", expect, got) - } + require.Equal(t, expect, got) require.ErrorIs(t, ErrInvalidAmount{"foo"}, e) } @@ -90,9 +78,7 @@ func Test_ErrUnexpected(t *testing.T) { } got := e.Error() expect := "unexpected error in foo: bar" - if got != expect { - t.Errorf("Expected %v, got %v", expect, got) - } + require.Equal(t, expect, got) require.ErrorIs(t, ErrUnexpected{"foo", "bar"}, e) } @@ -103,9 +89,7 @@ func Test_ErrInsufficientBalance(t *testing.T) { } got := e.Error() expect := "insufficient balance: requested foo, current bar" - if got != expect { - t.Errorf("Expected %v, got %v", expect, got) - } + require.Equal(t, expect, got) require.ErrorIs(t, ErrInsufficientBalance{"foo", "bar"}, e) } @@ -116,8 +100,16 @@ func Test_ErrInvalidToken(t *testing.T) { } got := e.Error() expect := "invalid token foo: bar" - if got != expect { - t.Errorf("Expected %v, got %v", expect, got) - } + require.Equal(t, expect, got) require.ErrorIs(t, ErrInvalidToken{"foo", "bar"}, e) } + +func Test_ErrWriteMethod(t *testing.T) { + e := ErrWriteMethod{ + Method: "foo", + } + got := e.Error() + expect := "method not allowed in read-only mode: foo" + require.Equal(t, expect, got) + require.ErrorIs(t, ErrWriteMethod{"foo"}, e) +} diff --git a/proto/zetachain/zetacore/crosschain/cross_chain_tx.proto b/proto/zetachain/zetacore/crosschain/cross_chain_tx.proto index 0ca1f56f7d..8feb44d382 100644 --- a/proto/zetachain/zetacore/crosschain/cross_chain_tx.proto +++ b/proto/zetachain/zetacore/crosschain/cross_chain_tx.proto @@ -17,7 +17,7 @@ enum CctxStatus { Reverted = 5; // inbound reverted. Aborted = 6; // inbound tx error or invalid paramters and cannot revert; just abort. - // But the amount can be refunded to zetachain using and admin proposal + // But the amount can be refunded to zetachain using and admin proposal } enum TxFinalizationStatus { @@ -43,6 +43,10 @@ message InboundParams { string ballot_index = 9; uint64 finalized_zeta_height = 10; TxFinalizationStatus tx_finalization_status = 11; + + // this field describes if a smart contract call should be made for a inbound + // with assets only used for protocol contract version 2 + bool is_cross_chain_call = 12; } message ZetaAccounting { @@ -94,7 +98,12 @@ message OutboundParams { message Status { CctxStatus status = 1; + // status_message carries information about the status transitions: + // why they were triggered, old and new status. string status_message = 2; + // error_message carries information about the error that caused the tx + // to be PendingRevert, Reverted or Aborted. + string error_message = 6; int64 lastUpdate_timestamp = 3; bool isAbortRefunded = 4; // when the CCTX was created. only populated on new transactions. diff --git a/proto/zetachain/zetacore/crosschain/legacy_msgs.proto b/proto/zetachain/zetacore/crosschain/legacy_msgs.proto new file mode 100644 index 0000000000..452ce0d94d --- /dev/null +++ b/proto/zetachain/zetacore/crosschain/legacy_msgs.proto @@ -0,0 +1,102 @@ +syntax = "proto3"; +package zetachain.zetacore.crosschain; + +import "gogoproto/gogo.proto"; +import "zetachain/zetacore/pkg/chains/chains.proto"; +import "zetachain/zetacore/pkg/coin/coin.proto"; +import "zetachain/zetacore/pkg/proofs/proofs.proto"; +import "zetachain/zetacore/crosschain/rate_limiter_flags.proto"; +import "zetachain/zetacore/crosschain/cross_chain_tx.proto"; + +option go_package = "github.com/zeta-chain/node/x/crosschain/types"; + +// legacy MsgAddOutboundTracker +// defined to keep codec compatibility +message MsgAddToOutTxTracker { + string creator = 1; + int64 chain_id = 2; + uint64 nonce = 3; + string tx_hash = 4; + pkg.proofs.Proof proof = 5; + string block_hash = 6; + int64 tx_index = 7; +} + +// legacy MsgAddInboundTracker +// defined to keep codec compatibility +message MsgAddToInTxTracker { + string creator = 1; + int64 chain_id = 2; + string tx_hash = 3; + pkg.coin.CoinType coin_type = 4; + pkg.proofs.Proof proof = 5; + string block_hash = 6; + int64 tx_index = 7; +} + +// legacy MsgRemoveOutboundTracker +// defined to keep codec compatibility +message MsgRemoveFromOutTxTracker { + string creator = 1; + int64 chain_id = 2; + uint64 nonce = 3; +} + +// legacy MsgVoteOutbound +// defined to keep codec compatibility +message MsgVoteOnObservedOutboundTx { + string creator = 1; + string cctx_hash = 2; + string observed_outTx_hash = 3; + uint64 observed_outTx_blockHeight = 4; + uint64 observed_outTx_gas_used = 10; + string observed_outTx_effective_gas_price = 11 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; + uint64 observed_outTx_effective_gas_limit = 12; + string value_received = 5 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + (gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"value_received\"" + ]; + pkg.chains.ReceiveStatus status = 6; + int64 outTx_chain = 7; + uint64 outTx_tss_nonce = 8; + pkg.coin.CoinType coin_type = 9; +} + +// legacy MsgVoteInbound +// defined to keep codec compatibility +message MsgVoteOnObservedInboundTx { + string creator = 1; + string sender = 2; + int64 sender_chain_id = 3; + string receiver = 4; + int64 receiver_chain = 5; + // string zeta_burnt = 6; + string amount = 6 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + (gogoproto.nullable) = false + ]; + // string mMint = 7; + string message = 8; + string in_tx_hash = 9; + uint64 in_block_height = 10; + uint64 gas_limit = 11; + pkg.coin.CoinType coin_type = 12; + string tx_origin = 13; + string asset = 14; + // event index of the sent asset in the observed tx + uint64 event_index = 15; +} + +// legacy MsgVoteGasPrice +// defined to keep codec compatibility +message MsgGasPriceVoter { + string creator = 1; + int64 chain_id = 2; + uint64 price = 3; + uint64 block_number = 4; + string supply = 5; +} diff --git a/proto/zetachain/zetacore/crosschain/tx.proto b/proto/zetachain/zetacore/crosschain/tx.proto index 59a449ddfc..cb76e53d0a 100644 --- a/proto/zetachain/zetacore/crosschain/tx.proto +++ b/proto/zetachain/zetacore/crosschain/tx.proto @@ -119,7 +119,7 @@ message MsgVoteGasPrice { uint64 block_number = 4; - reserved 5; // deprecated `string supply` + string supply = 5 [ deprecated = true ]; } message MsgVoteGasPriceResponse {} @@ -178,6 +178,9 @@ message MsgVoteInbound { RevertOptions revert_options = 17 [ (gogoproto.nullable) = false ]; CallOptions call_options = 18; + + // define if a smart contract call should be made with asset + bool is_cross_chain_call = 19; } message MsgVoteInboundResponse {} diff --git a/proto/zetachain/zetacore/pkg/chains/chains.proto b/proto/zetachain/zetacore/pkg/chains/chains.proto index 63b146cf42..dca1db9fa8 100644 --- a/proto/zetachain/zetacore/pkg/chains/chains.proto +++ b/proto/zetachain/zetacore/pkg/chains/chains.proto @@ -63,6 +63,7 @@ enum Network { optimism = 5; base = 6; solana = 7; + ton = 8; } // NetworkType represents the network type of the chain @@ -82,6 +83,7 @@ enum Vm { no_vm = 0; evm = 1; svm = 2; + tvm = 3; } // Consensus represents the consensus algorithm used by the chain @@ -94,6 +96,7 @@ enum Consensus { bitcoin = 2; op_stack = 3; solana_consensus = 4; + catchain_consensus = 5; // ton } // CCTXGateway describes for the chain the gateway used to handle CCTX outbounds diff --git a/readme.md b/readme.md index 89a5befb3a..5e7230fe00 100644 --- a/readme.md +++ b/readme.md @@ -19,12 +19,22 @@ modular framework for building blockchain and [Ethermint](https://github.com/zeta-chain/ethermint), a module that implements EVM-compatibility (ZetaChain fork). -- [zeta-node](https://github.com/zeta-chain/zeta-node) (this repository) - contains the source code for the ZetaChain node (`zetacored`) and the - ZetaChain client (`zetaclientd`). -- [protocol-contracts](https://github.com/zeta-chain/protocol-contracts) - contains the source code for the Solidity smart contracts that implement the - core functionality of ZetaChain. +This repository contains the core components: + +* [Blockchain Modules (ZetaCore)](x): +This section contains the core logic of the ZetaChain blockchain, built using Cosmos SDK modules. These modules are responsible for managing the state, state transitions, and overall functionality of the ZetaChain network. +* [ZetaClient](zetaclient): +The ZetaClient is a specialized client designed to act as an observer and signer for the ZetaChain network. It is responsible for communicating with the blockchain, relaying messages, and performing signature tasks to ensure the network operates cross-chain transactions. + +### Protocol Contracts + +In addition to the blockchain codebase, ZetaChain’s architecture includes a set of protocol contracts that serve as an interface for developers to interact with the blockchain. These smart contracts are deployed across various blockchain networks. The smart contract source code is maintained in separate repositories, depending on the network they are deployed on: + +* [ZetaChain EVM and EVM connected chains](https://github.com/zeta-chain/protocol-contracts) +* [Solana connected chains (SVM)](https://github.com/zeta-chain/protocol-contracts-solana) +* [TON connected chains (TVM)](https://github.com/zeta-chain/protocol-contracts-ton) + +These repositories contain the necessary code and tools to deploy, interact with, and extend the functionality of ZetaChain’s cross-chain protocol on each respective blockchain network. ## Building the `zetacored`/`zetaclientd` binaries diff --git a/rpc/backend/blocks.go b/rpc/backend/blocks.go index 6607fcbc07..a91118168a 100644 --- a/rpc/backend/blocks.go +++ b/rpc/backend/blocks.go @@ -339,6 +339,7 @@ func (b *Backend) parseSyntheticTxFromBlockResults( return nil, nil } if additional == nil || res == nil { + b.logger.Debug("synthetic ethereum tx not found in msgs: block %d, index %d", block.Height, i) return nil, nil } return b.parseSyntethicTxFromAdditionalFields(additional), additional diff --git a/rpc/types/events.go b/rpc/types/events.go index 886b0e6a87..3026d13158 100644 --- a/rpc/types/events.go +++ b/rpc/types/events.go @@ -271,7 +271,7 @@ func ParseTxBlockResult( } if len(txs.Txs) == 0 { - return nil, nil, fmt.Errorf("ethereum tx not found in msgs: block %d, index %d", height, txIndex) + return nil, nil, nil } parsedTx := txs.Txs[0] if parsedTx.Type == CosmosEVMTxType { diff --git a/testutil/keeper/crosschain.go b/testutil/keeper/crosschain.go index aade2afdeb..8418bbdaaf 100644 --- a/testutil/keeper/crosschain.go +++ b/testutil/keeper/crosschain.go @@ -390,6 +390,7 @@ func MockRevertForHandleEVMDeposit( coin.CoinType_ERC20, mock.Anything, mock.Anything, + mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{VmError: "reverted"}, false, errDeposit) } diff --git a/testutil/keeper/keeper.go b/testutil/keeper/keeper.go index 0582c4232f..ed3a107dac 100644 --- a/testutil/keeper/keeper.go +++ b/testutil/keeper/keeper.go @@ -136,7 +136,7 @@ var moduleAccountPerms = map[string][]string{ fungibletypes.ModuleName: {authtypes.Minter, authtypes.Burner}, emissionstypes.ModuleName: {authtypes.Minter}, emissionstypes.UndistributedObserverRewardsPool: nil, - emissionstypes.UndistributedTssRewardsPool: nil, + emissionstypes.UndistributedTSSRewardsPool: nil, ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, ibccrosschaintypes.ModuleName: nil, } diff --git a/testutil/keeper/mocks/crosschain/fungible.go b/testutil/keeper/mocks/crosschain/fungible.go index d2de2b7a98..856e3e8313 100644 --- a/testutil/keeper/mocks/crosschain/fungible.go +++ b/testutil/keeper/mocks/crosschain/fungible.go @@ -697,9 +697,9 @@ func (_m *CrosschainFungibleKeeper) ZETARevertAndCallContract(ctx types.Context, return r0, r1 } -// ZRC20DepositAndCallContract provides a mock function with given fields: ctx, from, to, amount, senderChainID, data, coinType, asset, protocolContractVersion -func (_m *CrosschainFungibleKeeper) ZRC20DepositAndCallContract(ctx types.Context, from []byte, to common.Address, amount *big.Int, senderChainID int64, data []byte, coinType coin.CoinType, asset string, protocolContractVersion crosschaintypes.ProtocolContractVersion) (*evmtypes.MsgEthereumTxResponse, bool, error) { - ret := _m.Called(ctx, from, to, amount, senderChainID, data, coinType, asset, protocolContractVersion) +// ZRC20DepositAndCallContract provides a mock function with given fields: ctx, from, to, amount, senderChainID, data, coinType, asset, protocolContractVersion, isCrossChainCall +func (_m *CrosschainFungibleKeeper) ZRC20DepositAndCallContract(ctx types.Context, from []byte, to common.Address, amount *big.Int, senderChainID int64, data []byte, coinType coin.CoinType, asset string, protocolContractVersion crosschaintypes.ProtocolContractVersion, isCrossChainCall bool) (*evmtypes.MsgEthereumTxResponse, bool, error) { + ret := _m.Called(ctx, from, to, amount, senderChainID, data, coinType, asset, protocolContractVersion, isCrossChainCall) if len(ret) == 0 { panic("no return value specified for ZRC20DepositAndCallContract") @@ -708,25 +708,25 @@ func (_m *CrosschainFungibleKeeper) ZRC20DepositAndCallContract(ctx types.Contex var r0 *evmtypes.MsgEthereumTxResponse var r1 bool var r2 error - if rf, ok := ret.Get(0).(func(types.Context, []byte, common.Address, *big.Int, int64, []byte, coin.CoinType, string, crosschaintypes.ProtocolContractVersion) (*evmtypes.MsgEthereumTxResponse, bool, error)); ok { - return rf(ctx, from, to, amount, senderChainID, data, coinType, asset, protocolContractVersion) + if rf, ok := ret.Get(0).(func(types.Context, []byte, common.Address, *big.Int, int64, []byte, coin.CoinType, string, crosschaintypes.ProtocolContractVersion, bool) (*evmtypes.MsgEthereumTxResponse, bool, error)); ok { + return rf(ctx, from, to, amount, senderChainID, data, coinType, asset, protocolContractVersion, isCrossChainCall) } - if rf, ok := ret.Get(0).(func(types.Context, []byte, common.Address, *big.Int, int64, []byte, coin.CoinType, string, crosschaintypes.ProtocolContractVersion) *evmtypes.MsgEthereumTxResponse); ok { - r0 = rf(ctx, from, to, amount, senderChainID, data, coinType, asset, protocolContractVersion) + if rf, ok := ret.Get(0).(func(types.Context, []byte, common.Address, *big.Int, int64, []byte, coin.CoinType, string, crosschaintypes.ProtocolContractVersion, bool) *evmtypes.MsgEthereumTxResponse); ok { + r0 = rf(ctx, from, to, amount, senderChainID, data, coinType, asset, protocolContractVersion, isCrossChainCall) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*evmtypes.MsgEthereumTxResponse) } } - if rf, ok := ret.Get(1).(func(types.Context, []byte, common.Address, *big.Int, int64, []byte, coin.CoinType, string, crosschaintypes.ProtocolContractVersion) bool); ok { - r1 = rf(ctx, from, to, amount, senderChainID, data, coinType, asset, protocolContractVersion) + if rf, ok := ret.Get(1).(func(types.Context, []byte, common.Address, *big.Int, int64, []byte, coin.CoinType, string, crosschaintypes.ProtocolContractVersion, bool) bool); ok { + r1 = rf(ctx, from, to, amount, senderChainID, data, coinType, asset, protocolContractVersion, isCrossChainCall) } else { r1 = ret.Get(1).(bool) } - if rf, ok := ret.Get(2).(func(types.Context, []byte, common.Address, *big.Int, int64, []byte, coin.CoinType, string, crosschaintypes.ProtocolContractVersion) error); ok { - r2 = rf(ctx, from, to, amount, senderChainID, data, coinType, asset, protocolContractVersion) + if rf, ok := ret.Get(2).(func(types.Context, []byte, common.Address, *big.Int, int64, []byte, coin.CoinType, string, crosschaintypes.ProtocolContractVersion, bool) error); ok { + r2 = rf(ctx, from, to, amount, senderChainID, data, coinType, asset, protocolContractVersion, isCrossChainCall) } else { r2 = ret.Error(2) } diff --git a/testutil/sample/crosschain.go b/testutil/sample/crosschain.go index 8f09271e23..25733e1fef 100644 --- a/testutil/sample/crosschain.go +++ b/testutil/sample/crosschain.go @@ -191,6 +191,7 @@ func Status(t *testing.T, index string) *types.Status { return &types.Status{ Status: types.CctxStatus(r.Intn(100)), StatusMessage: String(), + ErrorMessage: String(), CreatedTimestamp: createdAt, LastUpdateTimestamp: createdAt, } @@ -304,30 +305,34 @@ func ZRC20Withdrawal(to []byte, value *big.Int) *zrc20.ZRC20Withdrawal { } } +// InvalidZRC20WithdrawToExternalReceipt is a receipt for a invalid ZRC20 withdrawal to an external address // receiver is 1EYVvXLusCxtVuEwoYvWRyN5EZTXwPVvo3 -func GetInvalidZRC20WithdrawToExternal(t *testing.T) (receipt ethtypes.Receipt) { +func InvalidZRC20WithdrawToExternalReceipt(t *testing.T) (receipt ethtypes.Receipt) { block := "{\n \"type\": \"0x2\",\n \"root\": \"0x\",\n \"status\": \"0x1\",\n \"cumulativeGasUsed\": \"0x4e7a38\",\n \"logsBloom\": \"0x00000000000000000000010000020000000000000000000000000000000000020000000100000000000000000000000080000000000000000000000400200000200000000002000000000008000000000000000000000000000000000000000000000000020000000000000000800800000040000000000000000010000000000000000000000000000000000000000000000000000004000000000000000000020000000000000000000000000000000000000000000000000000000000010000000002000000000000000000000000000000000000000000000000000020000010000000000000000001000000000000000000040200000000000000000000\",\n \"logs\": [\n {\n \"address\": \"0x13a0c5930c028511dc02665e7285134b6d11a5f4\",\n \"topics\": [\n \"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\",\n \"0x000000000000000000000000313e74f7755afbae4f90e02ca49f8f09ff934a37\",\n \"0x000000000000000000000000735b14bb79463307aacbed86daf3322b1e6226ab\"\n ],\n \"data\": \"0x0000000000000000000000000000000000000000000000000000000000003790\",\n \"blockNumber\": \"0x1a2ad3\",\n \"transactionHash\": \"0x81126c18c7ca7d1fb7ded6644a87802e91bf52154ee4af7a5b379354e24fb6e0\",\n \"transactionIndex\": \"0x10\",\n \"blockHash\": \"0x5cb338544f64a226f4bfccb7a8d977f861c13ad73f7dd4317b66b00dd95de51c\",\n \"logIndex\": \"0x46\",\n \"removed\": false\n },\n {\n \"address\": \"0x13a0c5930c028511dc02665e7285134b6d11a5f4\",\n \"topics\": [\n \"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925\",\n \"0x000000000000000000000000313e74f7755afbae4f90e02ca49f8f09ff934a37\",\n \"0x00000000000000000000000013a0c5930c028511dc02665e7285134b6d11a5f4\"\n ],\n \"data\": \"0x00000000000000000000000000000000000000000000000000000000006a1217\",\n \"blockNumber\": \"0x1a2ad3\",\n \"transactionHash\": \"0x81126c18c7ca7d1fb7ded6644a87802e91bf52154ee4af7a5b379354e24fb6e0\",\n \"transactionIndex\": \"0x10\",\n \"blockHash\": \"0x5cb338544f64a226f4bfccb7a8d977f861c13ad73f7dd4317b66b00dd95de51c\",\n \"logIndex\": \"0x47\",\n \"removed\": false\n },\n {\n \"address\": \"0x13a0c5930c028511dc02665e7285134b6d11a5f4\",\n \"topics\": [\n \"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\",\n \"0x000000000000000000000000313e74f7755afbae4f90e02ca49f8f09ff934a37\",\n \"0x0000000000000000000000000000000000000000000000000000000000000000\"\n ],\n \"data\": \"0x00000000000000000000000000000000000000000000000000000000006a0c70\",\n \"blockNumber\": \"0x1a2ad3\",\n \"transactionHash\": \"0x81126c18c7ca7d1fb7ded6644a87802e91bf52154ee4af7a5b379354e24fb6e0\",\n \"transactionIndex\": \"0x10\",\n \"blockHash\": \"0x5cb338544f64a226f4bfccb7a8d977f861c13ad73f7dd4317b66b00dd95de51c\",\n \"logIndex\": \"0x48\",\n \"removed\": false\n },\n {\n \"address\": \"0x13a0c5930c028511dc02665e7285134b6d11a5f4\",\n \"topics\": [\n \"0x9ffbffc04a397460ee1dbe8c9503e098090567d6b7f4b3c02a8617d800b6d955\",\n \"0x000000000000000000000000313e74f7755afbae4f90e02ca49f8f09ff934a37\"\n ],\n \"data\": \"0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000006a0c700000000000000000000000000000000000000000000000000000000000003790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000223145595676584c7573437874567545776f59765752794e35455a5458775056766f33000000000000000000000000000000000000000000000000000000000000\",\n \"blockNumber\": \"0x1a2ad3\",\n \"transactionHash\": \"0x81126c18c7ca7d1fb7ded6644a87802e91bf52154ee4af7a5b379354e24fb6e0\",\n \"transactionIndex\": \"0x10\",\n \"blockHash\": \"0x5cb338544f64a226f4bfccb7a8d977f861c13ad73f7dd4317b66b00dd95de51c\",\n \"logIndex\": \"0x49\",\n \"removed\": false\n }\n ],\n \"transactionHash\": \"0x81126c18c7ca7d1fb7ded6644a87802e91bf52154ee4af7a5b379354e24fb6e0\",\n \"contractAddress\": \"0x0000000000000000000000000000000000000000\",\n \"gasUsed\": \"0x12521\",\n \"blockHash\": \"0x5cb338544f64a226f4bfccb7a8d977f861c13ad73f7dd4317b66b00dd95de51c\",\n \"blockNumber\": \"0x1a2ad3\",\n \"transactionIndex\": \"0x10\"\n}\n" err := json.Unmarshal([]byte(block), &receipt) require.NoError(t, err) return } -func GetValidZrc20WithdrawToETH(t *testing.T) (receipt ethtypes.Receipt) { +// ValidZrc20WithdrawToETHReceipt is a receipt for a ZRC20 withdrawal to an external address +func ValidZrc20WithdrawToETHReceipt(t *testing.T) (receipt ethtypes.Receipt) { block := "{\n \"type\": \"0x2\",\n \"root\": \"0x\",\n \"status\": \"0x1\",\n \"cumulativeGasUsed\": \"0xdbedca\",\n \"logsBloom\": \"0x00200000001000000000000088020001000001000000000000000000000000000000020100000000000000000000000080000000000000000000000400640000000000000000000008000008020000200000000000000002000000008000000000000000020000000200000000800801000000080000000000000010000000000000000000000000000000000000001000000001000004080001404000000000028002000000000000000040000000000000000000000000000000000000000000000002000000000000008000000000000000800800001000000002000021000010000100000000000010800400000000020000000100400880000000004000\",\n \"logs\": [\n {\n \"address\": \"0x3f641963f3d9adf82d890fd8142313dcec807ba5\",\n \"topics\": [\n \"0x3d0ce9bfc3ed7d6862dbb28b2dea94561fe714a1b4d019aa8af39730d1ad7c3d\",\n \"0x0000000000000000000000008e0f8e7e9e121403e72151d00f4937eacb2d9ef3\"\n ],\n \"data\": \"0x00000000000000000000000000000000000000000000000045400a8fd5330000\",\n \"blockNumber\": \"0x17ef22\",\n \"transactionHash\": \"0x87229bb05d67f42017a697b34ed52d95afc9f5e3285479e845fe088b4c77d8f0\",\n \"transactionIndex\": \"0x1f\",\n \"blockHash\": \"0xf49e7039c7f1a81cd46de150980d92fa869cc0d2e2f1fe46aedc6400396137ff\",\n \"logIndex\": \"0x57\",\n \"removed\": false\n },\n {\n \"address\": \"0x5f0b1a82749cb4e2278ec87f8bf6b618dc71a8bf\",\n \"topics\": [\n \"0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c\",\n \"0x0000000000000000000000008e0f8e7e9e121403e72151d00f4937eacb2d9ef3\"\n ],\n \"data\": \"0x00000000000000000000000000000000000000000000001ac7c4159f72b90000\",\n \"blockNumber\": \"0x17ef22\",\n \"transactionHash\": \"0x87229bb05d67f42017a697b34ed52d95afc9f5e3285479e845fe088b4c77d8f0\",\n \"transactionIndex\": \"0x1f\",\n \"blockHash\": \"0xf49e7039c7f1a81cd46de150980d92fa869cc0d2e2f1fe46aedc6400396137ff\",\n \"logIndex\": \"0x58\",\n \"removed\": false\n },\n {\n \"address\": \"0x5f0b1a82749cb4e2278ec87f8bf6b618dc71a8bf\",\n \"topics\": [\n \"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925\",\n \"0x0000000000000000000000008e0f8e7e9e121403e72151d00f4937eacb2d9ef3\",\n \"0x0000000000000000000000002ca7d64a7efe2d62a725e2b35cf7230d6677ffee\"\n ],\n \"data\": \"0x00000000000000000000000000000000000000000000001ac7c4159f72b90000\",\n \"blockNumber\": \"0x17ef22\",\n \"transactionHash\": \"0x87229bb05d67f42017a697b34ed52d95afc9f5e3285479e845fe088b4c77d8f0\",\n \"transactionIndex\": \"0x1f\",\n \"blockHash\": \"0xf49e7039c7f1a81cd46de150980d92fa869cc0d2e2f1fe46aedc6400396137ff\",\n \"logIndex\": \"0x59\",\n \"removed\": false\n },\n {\n \"address\": \"0x5f0b1a82749cb4e2278ec87f8bf6b618dc71a8bf\",\n \"topics\": [\n \"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\",\n \"0x0000000000000000000000008e0f8e7e9e121403e72151d00f4937eacb2d9ef3\",\n \"0x00000000000000000000000016ef1b018026e389fda93c1e993e987cf6e852e7\"\n ],\n \"data\": \"0x00000000000000000000000000000000000000000000001ac7c4159f72b90000\",\n \"blockNumber\": \"0x17ef22\",\n \"transactionHash\": \"0x87229bb05d67f42017a697b34ed52d95afc9f5e3285479e845fe088b4c77d8f0\",\n \"transactionIndex\": \"0x1f\",\n \"blockHash\": \"0xf49e7039c7f1a81cd46de150980d92fa869cc0d2e2f1fe46aedc6400396137ff\",\n \"logIndex\": \"0x5a\",\n \"removed\": false\n },\n {\n \"address\": \"0xd97b1de3619ed2c6beb3860147e30ca8a7dc9891\",\n \"topics\": [\n \"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\",\n \"0x00000000000000000000000016ef1b018026e389fda93c1e993e987cf6e852e7\",\n \"0x0000000000000000000000008e0f8e7e9e121403e72151d00f4937eacb2d9ef3\"\n ],\n \"data\": \"0x00000000000000000000000000000000000000000000000002e640d76638740f\",\n \"blockNumber\": \"0x17ef22\",\n \"transactionHash\": \"0x87229bb05d67f42017a697b34ed52d95afc9f5e3285479e845fe088b4c77d8f0\",\n \"transactionIndex\": \"0x1f\",\n \"blockHash\": \"0xf49e7039c7f1a81cd46de150980d92fa869cc0d2e2f1fe46aedc6400396137ff\",\n \"logIndex\": \"0x5b\",\n \"removed\": false\n },\n {\n \"address\": \"0x16ef1b018026e389fda93c1e993e987cf6e852e7\",\n \"topics\": [\n \"0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1\"\n ],\n \"data\": \"0x000000000000000000000000000000000000000000000b3f1da425061770a11600000000000000000000000000000000000000000000000135be3952e251aa40\",\n \"blockNumber\": \"0x17ef22\",\n \"transactionHash\": \"0x87229bb05d67f42017a697b34ed52d95afc9f5e3285479e845fe088b4c77d8f0\",\n \"transactionIndex\": \"0x1f\",\n \"blockHash\": \"0xf49e7039c7f1a81cd46de150980d92fa869cc0d2e2f1fe46aedc6400396137ff\",\n \"logIndex\": \"0x5c\",\n \"removed\": false\n },\n {\n \"address\": \"0x16ef1b018026e389fda93c1e993e987cf6e852e7\",\n \"topics\": [\n \"0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822\",\n \"0x0000000000000000000000002ca7d64a7efe2d62a725e2b35cf7230d6677ffee\",\n \"0x0000000000000000000000008e0f8e7e9e121403e72151d00f4937eacb2d9ef3\"\n ],\n \"data\": \"0x00000000000000000000000000000000000000000000001ac7c4159f72b900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e640d76638740f\",\n \"blockNumber\": \"0x17ef22\",\n \"transactionHash\": \"0x87229bb05d67f42017a697b34ed52d95afc9f5e3285479e845fe088b4c77d8f0\",\n \"transactionIndex\": \"0x1f\",\n \"blockHash\": \"0xf49e7039c7f1a81cd46de150980d92fa869cc0d2e2f1fe46aedc6400396137ff\",\n \"logIndex\": \"0x5d\",\n \"removed\": false\n },\n {\n \"address\": \"0xd97b1de3619ed2c6beb3860147e30ca8a7dc9891\",\n \"topics\": [\n \"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925\",\n \"0x0000000000000000000000008e0f8e7e9e121403e72151d00f4937eacb2d9ef3\",\n \"0x000000000000000000000000d97b1de3619ed2c6beb3860147e30ca8a7dc9891\"\n ],\n \"data\": \"0x00000000000000000000000000000000000000000000000000015059f36c8ec0\",\n \"blockNumber\": \"0x17ef22\",\n \"transactionHash\": \"0x87229bb05d67f42017a697b34ed52d95afc9f5e3285479e845fe088b4c77d8f0\",\n \"transactionIndex\": \"0x1f\",\n \"blockHash\": \"0xf49e7039c7f1a81cd46de150980d92fa869cc0d2e2f1fe46aedc6400396137ff\",\n \"logIndex\": \"0x5e\",\n \"removed\": false\n },\n {\n \"address\": \"0xd97b1de3619ed2c6beb3860147e30ca8a7dc9891\",\n \"topics\": [\n \"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\",\n \"0x0000000000000000000000008e0f8e7e9e121403e72151d00f4937eacb2d9ef3\",\n \"0x000000000000000000000000735b14bb79463307aacbed86daf3322b1e6226ab\"\n ],\n \"data\": \"0x00000000000000000000000000000000000000000000000000015059f36c8ec0\",\n \"blockNumber\": \"0x17ef22\",\n \"transactionHash\": \"0x87229bb05d67f42017a697b34ed52d95afc9f5e3285479e845fe088b4c77d8f0\",\n \"transactionIndex\": \"0x1f\",\n \"blockHash\": \"0xf49e7039c7f1a81cd46de150980d92fa869cc0d2e2f1fe46aedc6400396137ff\",\n \"logIndex\": \"0x5f\",\n \"removed\": false\n },\n {\n \"address\": \"0xd97b1de3619ed2c6beb3860147e30ca8a7dc9891\",\n \"topics\": [\n \"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925\",\n \"0x0000000000000000000000008e0f8e7e9e121403e72151d00f4937eacb2d9ef3\",\n \"0x000000000000000000000000d97b1de3619ed2c6beb3860147e30ca8a7dc9891\"\n ],\n \"data\": \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n \"blockNumber\": \"0x17ef22\",\n \"transactionHash\": \"0x87229bb05d67f42017a697b34ed52d95afc9f5e3285479e845fe088b4c77d8f0\",\n \"transactionIndex\": \"0x1f\",\n \"blockHash\": \"0xf49e7039c7f1a81cd46de150980d92fa869cc0d2e2f1fe46aedc6400396137ff\",\n \"logIndex\": \"0x60\",\n \"removed\": false\n },\n {\n \"address\": \"0xd97b1de3619ed2c6beb3860147e30ca8a7dc9891\",\n \"topics\": [\n \"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\",\n \"0x0000000000000000000000008e0f8e7e9e121403e72151d00f4937eacb2d9ef3\",\n \"0x0000000000000000000000000000000000000000000000000000000000000000\"\n ],\n \"data\": \"0x00000000000000000000000000000000000000000000000002e4f07d72cbe54f\",\n \"blockNumber\": \"0x17ef22\",\n \"transactionHash\": \"0x87229bb05d67f42017a697b34ed52d95afc9f5e3285479e845fe088b4c77d8f0\",\n \"transactionIndex\": \"0x1f\",\n \"blockHash\": \"0xf49e7039c7f1a81cd46de150980d92fa869cc0d2e2f1fe46aedc6400396137ff\",\n \"logIndex\": \"0x61\",\n \"removed\": false\n },\n {\n \"address\": \"0xd97b1de3619ed2c6beb3860147e30ca8a7dc9891\",\n \"topics\": [\n \"0x9ffbffc04a397460ee1dbe8c9503e098090567d6b7f4b3c02a8617d800b6d955\",\n \"0x0000000000000000000000008e0f8e7e9e121403e72151d00f4937eacb2d9ef3\"\n ],\n \"data\": \"0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000002e4f07d72cbe54f00000000000000000000000000000000000000000000000000015059f36c8ec0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000005dabfdd153aaab4a970fd953dcfeee8bf6bb946e\",\n \"blockNumber\": \"0x17ef22\",\n \"transactionHash\": \"0x87229bb05d67f42017a697b34ed52d95afc9f5e3285479e845fe088b4c77d8f0\",\n \"transactionIndex\": \"0x1f\",\n \"blockHash\": \"0xf49e7039c7f1a81cd46de150980d92fa869cc0d2e2f1fe46aedc6400396137ff\",\n \"logIndex\": \"0x62\",\n \"removed\": false\n },\n {\n \"address\": \"0x8e0f8e7e9e121403e72151d00f4937eacb2d9ef3\",\n \"topics\": [\n \"0x97eb75cc53ffa3f4560fc62e4912dda10ac56c3d12dbc48dc8c27d5ab225cf66\"\n ],\n \"data\": \"0x0000000000000000000000005f0b1a82749cb4e2278ec87f8bf6b618dc71a8bf000000000000000000000000d97b1de3619ed2c6beb3860147e30ca8a7dc989100000000000000000000000000000000000000000000001b0d04202f47ec000000000000000000000000000000000000000000000000001ac7c4159f72b900000000000000000000000000005dabfdd153aaab4a970fd953dcfeee8bf6bb946e00000000000000000000000000000000000000000000000045400a8fd5330000\",\n \"blockNumber\": \"0x17ef22\",\n \"transactionHash\": \"0x87229bb05d67f42017a697b34ed52d95afc9f5e3285479e845fe088b4c77d8f0\",\n \"transactionIndex\": \"0x1f\",\n \"blockHash\": \"0xf49e7039c7f1a81cd46de150980d92fa869cc0d2e2f1fe46aedc6400396137ff\",\n \"logIndex\": \"0x63\",\n \"removed\": false\n }\n ],\n \"transactionHash\": \"0x87229bb05d67f42017a697b34ed52d95afc9f5e3285479e845fe088b4c77d8f0\",\n \"contractAddress\": \"0x0000000000000000000000000000000000000000\",\n \"gasUsed\": \"0x41c3c\",\n \"blockHash\": \"0xf49e7039c7f1a81cd46de150980d92fa869cc0d2e2f1fe46aedc6400396137ff\",\n \"blockNumber\": \"0x17ef22\",\n \"transactionIndex\": \"0x1f\"\n}" err := json.Unmarshal([]byte(block), &receipt) require.NoError(t, err) return } +// ValidZRC20WithdrawToBTCReceipt is a receipt for a ZRC20 withdrawal to a BTC address // receiver is bc1qysd4sp9q8my59ul9wsf5rvs9p387hf8vfwatzu -func GetValidZRC20WithdrawToBTC(t *testing.T) (receipt ethtypes.Receipt) { +func ValidZRC20WithdrawToBTCReceipt(t *testing.T) (receipt ethtypes.Receipt) { block := "{\"type\":\"0x2\",\"root\":\"0x\",\"status\":\"0x1\",\"cumulativeGasUsed\":\"0x1f25ed\",\"logsBloom\":\"0x00000000000000000000000000020000000000000000000000000000000000020000000100000000000000000040000080000000000000000000000400200000200000000002000000000008000000000000000000000000000000000000000000000000020000000000000000800800000000000000000000000010000000000000000000000000000000000000000000000000000004000000000000000000020000000001000000000000000000000000000000000000000000000000010000000002000000000000000010000000000000000000000000000000000020000010000000000000000000000000000000000000040200000000000000000000\",\"logs\":[{\"address\":\"0x13a0c5930c028511dc02665e7285134b6d11a5f4\",\"topics\":[\"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\",\"0x00000000000000000000000033ead83db0d0c682b05ead61e8d8f481bb1b4933\",\"0x000000000000000000000000735b14bb79463307aacbed86daf3322b1e6226ab\"],\"data\":\"0x0000000000000000000000000000000000000000000000000000000000003d84\",\"blockNumber\":\"0x1a00f3\",\"transactionHash\":\"0x9aaefece38fd2bd87077038a63fffb7c84cc8dd1ed01de134a8504a1f9a410c3\",\"transactionIndex\":\"0x8\",\"blockHash\":\"0x9517356f0b3877990590421266f02a4ff349b7476010ee34dd5f0dfc85c2684f\",\"logIndex\":\"0x28\",\"removed\":false},{\"address\":\"0x13a0c5930c028511dc02665e7285134b6d11a5f4\",\"topics\":[\"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925\",\"0x00000000000000000000000033ead83db0d0c682b05ead61e8d8f481bb1b4933\",\"0x00000000000000000000000013a0c5930c028511dc02665e7285134b6d11a5f4\"],\"data\":\"0x0000000000000000000000000000000000000000000000000000000000978c98\",\"blockNumber\":\"0x1a00f3\",\"transactionHash\":\"0x9aaefece38fd2bd87077038a63fffb7c84cc8dd1ed01de134a8504a1f9a410c3\",\"transactionIndex\":\"0x8\",\"blockHash\":\"0x9517356f0b3877990590421266f02a4ff349b7476010ee34dd5f0dfc85c2684f\",\"logIndex\":\"0x29\",\"removed\":false},{\"address\":\"0x13a0c5930c028511dc02665e7285134b6d11a5f4\",\"topics\":[\"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\",\"0x00000000000000000000000033ead83db0d0c682b05ead61e8d8f481bb1b4933\",\"0x0000000000000000000000000000000000000000000000000000000000000000\"],\"data\":\"0x0000000000000000000000000000000000000000000000000000000000003039\",\"blockNumber\":\"0x1a00f3\",\"transactionHash\":\"0x9aaefece38fd2bd87077038a63fffb7c84cc8dd1ed01de134a8504a1f9a410c3\",\"transactionIndex\":\"0x8\",\"blockHash\":\"0x9517356f0b3877990590421266f02a4ff349b7476010ee34dd5f0dfc85c2684f\",\"logIndex\":\"0x2a\",\"removed\":false},{\"address\":\"0x13a0c5930c028511dc02665e7285134b6d11a5f4\",\"topics\":[\"0x9ffbffc04a397460ee1dbe8c9503e098090567d6b7f4b3c02a8617d800b6d955\",\"0x00000000000000000000000033ead83db0d0c682b05ead61e8d8f481bb1b4933\"],\"data\":\"0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000030390000000000000000000000000000000000000000000000000000000000003d840000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a626331717973643473703971386d793539756c3977736635727673397033383768663876667761747a7500000000000000000000000000000000000000000000\",\"blockNumber\":\"0x1a00f3\",\"transactionHash\":\"0x9aaefece38fd2bd87077038a63fffb7c84cc8dd1ed01de134a8504a1f9a410c3\",\"transactionIndex\":\"0x8\",\"blockHash\":\"0x9517356f0b3877990590421266f02a4ff349b7476010ee34dd5f0dfc85c2684f\",\"logIndex\":\"0x2b\",\"removed\":false}],\"transactionHash\":\"0x9aaefece38fd2bd87077038a63fffb7c84cc8dd1ed01de134a8504a1f9a410c3\",\"contractAddress\":\"0x0000000000000000000000000000000000000000\",\"gasUsed\":\"0x12575\",\"blockHash\":\"0x9517356f0b3877990590421266f02a4ff349b7476010ee34dd5f0dfc85c2684f\",\"blockNumber\":\"0x1a00f3\",\"transactionIndex\":\"0x8\"}\n" err := json.Unmarshal([]byte(block), &receipt) require.NoError(t, err) return } -func GetValidZetaSentDestinationExternal(t *testing.T) (receipt ethtypes.Receipt) { +// ValidZetaSentDestinationExternalReceipt is a receipt for a Zeta sent to an external destination +func ValidZetaSentDestinationExternalReceipt(t *testing.T) (receipt ethtypes.Receipt) { block := "{\"root\":\"0x\",\"status\":\"0x1\",\"cumulativeGasUsed\":\"0xd75f4f\",\"logsBloom\":\"0x00000000000000000000000000000000800800000000000000000000100000000000002000000100000000000000000000000000000000000000000000240000000000000000000000000008000000000800000000440000000000008080000000000000000000000000000000000000000000000000040000000010000000000000000000000000000000000000000200000001000000000000000040000000020000000000000000000000008200000000000000000000000000000000000000000002000000000000008000000000000000000000000000080002000041000010000000000000000000000000000000000000000000400000000000000000\",\"logs\":[{\"address\":\"0x5f0b1a82749cb4e2278ec87f8bf6b618dc71a8bf\",\"topics\":[\"0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c\",\"0x000000000000000000000000f0a3f93ed1b126142e61423f9546bf1323ff82df\"],\"data\":\"0x000000000000000000000000000000000000000000000003cb71f51fc5580000\",\"blockNumber\":\"0x1bedc8\",\"transactionHash\":\"0x19d8a67a05998f1cb19fe731b96d817d5b186b62c9430c51679664959c952ef0\",\"transactionIndex\":\"0x5f\",\"blockHash\":\"0x198fdd1f4bc6b910db978602cb15bdb2bcc6fd960e9324e9b9675dc062133794\",\"logIndex\":\"0x13b\",\"removed\":false},{\"address\":\"0x5f0b1a82749cb4e2278ec87f8bf6b618dc71a8bf\",\"topics\":[\"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925\",\"0x000000000000000000000000f0a3f93ed1b126142e61423f9546bf1323ff82df\",\"0x000000000000000000000000239e96c8f17c85c30100ac26f635ea15f23e9c67\"],\"data\":\"0x000000000000000000000000000000000000000000000003cb71f51fc5580000\",\"blockNumber\":\"0x1bedc8\",\"transactionHash\":\"0x19d8a67a05998f1cb19fe731b96d817d5b186b62c9430c51679664959c952ef0\",\"transactionIndex\":\"0x5f\",\"blockHash\":\"0x198fdd1f4bc6b910db978602cb15bdb2bcc6fd960e9324e9b9675dc062133794\",\"logIndex\":\"0x13c\",\"removed\":false},{\"address\":\"0x5f0b1a82749cb4e2278ec87f8bf6b618dc71a8bf\",\"topics\":[\"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\",\"0x000000000000000000000000f0a3f93ed1b126142e61423f9546bf1323ff82df\",\"0x000000000000000000000000239e96c8f17c85c30100ac26f635ea15f23e9c67\"],\"data\":\"0x000000000000000000000000000000000000000000000003cb71f51fc5580000\",\"blockNumber\":\"0x1bedc8\",\"transactionHash\":\"0x19d8a67a05998f1cb19fe731b96d817d5b186b62c9430c51679664959c952ef0\",\"transactionIndex\":\"0x5f\",\"blockHash\":\"0x198fdd1f4bc6b910db978602cb15bdb2bcc6fd960e9324e9b9675dc062133794\",\"logIndex\":\"0x13d\",\"removed\":false},{\"address\":\"0x5f0b1a82749cb4e2278ec87f8bf6b618dc71a8bf\",\"topics\":[\"0x7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65\",\"0x000000000000000000000000239e96c8f17c85c30100ac26f635ea15f23e9c67\"],\"data\":\"0x000000000000000000000000000000000000000000000003cb71f51fc5580000\",\"blockNumber\":\"0x1bedc8\",\"transactionHash\":\"0x19d8a67a05998f1cb19fe731b96d817d5b186b62c9430c51679664959c952ef0\",\"transactionIndex\":\"0x5f\",\"blockHash\":\"0x198fdd1f4bc6b910db978602cb15bdb2bcc6fd960e9324e9b9675dc062133794\",\"logIndex\":\"0x13e\",\"removed\":false},{\"address\":\"0x239e96c8f17c85c30100ac26f635ea15f23e9c67\",\"topics\":[\"0x7ec1c94701e09b1652f3e1d307e60c4b9ebf99aff8c2079fd1d8c585e031c4e4\",\"0x000000000000000000000000f0a3f93ed1b126142e61423f9546bf1323ff82df\",\"0x0000000000000000000000000000000000000000000000000000000000000001\"],\"data\":\"0x00000000000000000000000060983881bdf302dcfa96603a58274d15d596620900000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000003cb71f51fc558000000000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000001460983881bdf302dcfa96603a58274d15d59662090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000\",\"blockNumber\":\"0x1bedc8\",\"transactionHash\":\"0x19d8a67a05998f1cb19fe731b96d817d5b186b62c9430c51679664959c952ef0\",\"transactionIndex\":\"0x5f\",\"blockHash\":\"0x198fdd1f4bc6b910db978602cb15bdb2bcc6fd960e9324e9b9675dc062133794\",\"logIndex\":\"0x13f\",\"removed\":false}],\"transactionHash\":\"0x19d8a67a05998f1cb19fe731b96d817d5b186b62c9430c51679664959c952ef0\",\"contractAddress\":\"0x0000000000000000000000000000000000000000\",\"gasUsed\":\"0x2406d\",\"blockHash\":\"0x198fdd1f4bc6b910db978602cb15bdb2bcc6fd960e9324e9b9675dc062133794\",\"blockNumber\":\"0x1bedc8\",\"transactionIndex\":\"0x5f\"}\n" err := json.Unmarshal([]byte(block), &receipt) require.NoError(t, err) diff --git a/testutil/sample/sample_ton.go b/testutil/sample/sample_ton.go new file mode 100644 index 0000000000..01ce8724c5 --- /dev/null +++ b/testutil/sample/sample_ton.go @@ -0,0 +1,272 @@ +package sample + +import ( + "crypto/rand" + "reflect" + "testing" + "time" + "unsafe" + + "cosmossdk.io/math" + eth "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/tlb" + "github.com/tonkeeper/tongo/ton" + + toncontracts "github.com/zeta-chain/node/pkg/contracts/ton" +) + +const ( + tonWorkchainID = 0 + tonShardID = 123 + tonDepositFee = 10_000_000 // 0.01 TON + tonSampleGasUsage = 50_000_000 // 0.05 TON +) + +type TONTransactionProps struct { + Account ton.AccountID + GasUsed uint64 + BlockID ton.BlockIDExt + + // For simplicity let's have only one input + // and one output (both optional) + Input *tlb.Message + Output *tlb.Message +} + +type intMsgInfo struct { + IhrDisabled bool + Bounce bool + Bounced bool + Src tlb.MsgAddress + Dest tlb.MsgAddress + Value tlb.CurrencyCollection + IhrFee tlb.Grams + FwdFee tlb.Grams + CreatedLt uint64 + CreatedAt uint32 +} + +func TONDonation(t *testing.T, acc ton.AccountID, d toncontracts.Donation) ton.Transaction { + return TONTransaction(t, TONDonateProps(t, acc, d)) +} + +func TONDonateProps(t *testing.T, acc ton.AccountID, d toncontracts.Donation) TONTransactionProps { + body, err := d.AsBody() + require.NoError(t, err) + + deposited := tonSampleGasUsage + d.Amount.Uint64() + + return TONTransactionProps{ + Account: acc, + Input: &tlb.Message{ + Info: internalMessageInfo(&intMsgInfo{ + Bounce: true, + Src: d.Sender.ToMsgAddress(), + Dest: acc.ToMsgAddress(), + Value: tlb.CurrencyCollection{Grams: tlb.Grams(deposited)}, + }), + Body: tlb.EitherRef[tlb.Any]{Value: tlb.Any(*body)}, + }, + } +} + +func TONDeposit(t *testing.T, acc ton.AccountID, d toncontracts.Deposit) ton.Transaction { + return TONTransaction(t, TONDepositProps(t, acc, d)) +} + +func TONDepositProps(t *testing.T, acc ton.AccountID, d toncontracts.Deposit) TONTransactionProps { + body, err := d.AsBody() + require.NoError(t, err) + + logBody := depositLogMock(t, d.Sender, d.Amount.Uint64(), d.Recipient, nil) + + return TONTransactionProps{ + Account: acc, + Input: &tlb.Message{ + Info: internalMessageInfo(&intMsgInfo{ + Bounce: true, + Src: d.Sender.ToMsgAddress(), + Dest: acc.ToMsgAddress(), + Value: tlb.CurrencyCollection{Grams: fakeDepositAmount(d.Amount)}, + }), + Body: tlb.EitherRef[tlb.Any]{Value: tlb.Any(*body)}, + }, + Output: &tlb.Message{ + Body: tlb.EitherRef[tlb.Any]{IsRight: true, Value: tlb.Any(*logBody)}, + }, + } +} + +func TONDepositAndCall(t *testing.T, acc ton.AccountID, d toncontracts.DepositAndCall) ton.Transaction { + return TONTransaction(t, TONDepositAndCallProps(t, acc, d)) +} + +func TONDepositAndCallProps(t *testing.T, acc ton.AccountID, d toncontracts.DepositAndCall) TONTransactionProps { + body, err := d.AsBody() + require.NoError(t, err) + + logBody := depositLogMock(t, d.Sender, d.Amount.Uint64(), d.Recipient, d.CallData) + + return TONTransactionProps{ + Account: acc, + Input: &tlb.Message{ + Info: internalMessageInfo(&intMsgInfo{ + Bounce: true, + Src: d.Sender.ToMsgAddress(), + Dest: acc.ToMsgAddress(), + Value: tlb.CurrencyCollection{Grams: fakeDepositAmount(d.Amount)}, + }), + Body: tlb.EitherRef[tlb.Any]{Value: tlb.Any(*body)}, + }, + Output: &tlb.Message{ + Body: tlb.EitherRef[tlb.Any]{IsRight: true, Value: tlb.Any(*logBody)}, + }, + } +} + +// TONTransaction creates a sample TON transaction. +func TONTransaction(t *testing.T, p TONTransactionProps) ton.Transaction { + require.False(t, p.Account.IsZero(), "account address is empty") + require.False(t, p.Input == nil && p.Output == nil, "both input and output are empty") + + now := time.Now().UTC() + + if p.GasUsed == 0 { + p.GasUsed = tonSampleGasUsage + } + + if p.BlockID.BlockID.Seqno == 0 { + p.BlockID = tonBlockID(now) + } + + // Simulate logical time as `2 * now()` + lt := uint64(2 * now.Unix()) + + input := tlb.Maybe[tlb.Ref[tlb.Message]]{} + if p.Input != nil { + input.Exists = true + input.Value.Value = *p.Input + } + + var outputs tlb.HashmapE[tlb.Uint15, tlb.Ref[tlb.Message]] + if p.Output != nil { + outputs = tlb.NewHashmapE( + []tlb.Uint15{0}, + []tlb.Ref[tlb.Message]{{*p.Output}}, + ) + } + + type messages struct { + InMsg tlb.Maybe[tlb.Ref[tlb.Message]] + OutMsgs tlb.HashmapE[tlb.Uint15, tlb.Ref[tlb.Message]] + } + + tx := ton.Transaction{ + BlockID: p.BlockID, + Transaction: tlb.Transaction{ + AccountAddr: p.Account.Address, + Lt: lt, + Now: uint32(now.Unix()), + OutMsgCnt: tlb.Uint15(len(outputs.Keys())), + TotalFees: tlb.CurrencyCollection{Grams: tlb.Grams(p.GasUsed)}, + Msgs: messages{InMsg: input, OutMsgs: outputs}, + }, + } + + setTXHash(&tx.Transaction, Hash()) + + return tx +} + +func GenerateTONAccountID() ton.AccountID { + var addr [32]byte + + //nolint:errcheck // test code + rand.Read(addr[:]) + + return *ton.NewAccountID(0, addr) +} + +func internalMessageInfo(info *intMsgInfo) tlb.CommonMsgInfo { + return tlb.CommonMsgInfo{ + SumType: "IntMsgInfo", + IntMsgInfo: (*struct { + IhrDisabled bool + Bounce bool + Bounced bool + Src tlb.MsgAddress + Dest tlb.MsgAddress + Value tlb.CurrencyCollection + IhrFee tlb.Grams + FwdFee tlb.Grams + CreatedLt uint64 + CreatedAt uint32 + })(info), + } +} + +func tonBlockID(now time.Time) ton.BlockIDExt { + // simulate shard seqno as unix timestamp + seqno := uint32(now.Unix()) + + return ton.BlockIDExt{ + BlockID: ton.BlockID{ + Workchain: tonWorkchainID, + Shard: tonShardID, + Seqno: seqno, + }, + } +} + +func fakeDepositAmount(v math.Uint) tlb.Grams { + return tlb.Grams(v.Uint64() + tonDepositFee) +} + +func depositLogMock( + t *testing.T, + sender ton.AccountID, + amount uint64, + recipient eth.Address, + callData []byte, +) *boc.Cell { + // cell log = begin_cell() + // .store_uint(op::internal::deposit_and_call, size::op_code_size) + // .store_uint(0, size::query_id_size) + // .store_slice(sender) + // .store_coins(deposit_amount) + // .store_uint(evm_recipient, size::evm_address) + // .store_ref(call_data) // only for DepositAndCall + // .end_cell(); + + b := boc.NewCell() + require.NoError(t, b.WriteUint(0, 32+64)) + + // skip + msgAddr := sender.ToMsgAddress() + require.NoError(t, tlb.Marshal(b, msgAddr)) + + coins := tlb.Grams(amount) + require.NoError(t, coins.MarshalTLB(b, nil)) + + require.NoError(t, b.WriteBytes(recipient.Bytes())) + + if callData != nil { + callDataCell, err := toncontracts.MarshalSnakeCell(callData) + require.NoError(t, err) + require.NoError(t, b.AddRef(callDataCell)) + } + + return b +} + +// well, tlb.Transaction has unexported field `hash` that we need to set OUTSIDE tlb package. +// It's a hack, but it works for testing purposes. +func setTXHash(tx *tlb.Transaction, hash [32]byte) { + field := reflect.ValueOf(tx).Elem().FieldByName("hash") + ptr := unsafe.Pointer(field.UnsafeAddr()) + + arrPtr := (*[32]byte)(ptr) + *arrPtr = hash +} diff --git a/testutil/sample/sample_ton_test.go b/testutil/sample/sample_ton_test.go new file mode 100644 index 0000000000..73474db0d4 --- /dev/null +++ b/testutil/sample/sample_ton_test.go @@ -0,0 +1,94 @@ +package sample + +import ( + "testing" + + sdkmath "cosmossdk.io/math" + "github.com/stretchr/testify/require" + "github.com/tonkeeper/tongo/ton" + toncontracts "github.com/zeta-chain/node/pkg/contracts/ton" +) + +func TestTONSamples(t *testing.T) { + var ( + gatewayID = ton.MustParseAccountID("0:997d889c815aeac21c47f86ae0e38383efc3c3463067582f6263ad48c5a1485b") + gw = toncontracts.NewGateway(gatewayID) + ) + + t.Run("Donate", func(t *testing.T) { + // ARRANGE + d := toncontracts.Donation{ + Sender: GenerateTONAccountID(), + Amount: sdkmath.NewUint(100_000_000), + } + + tx := TONTransaction(t, TONDonateProps(t, gatewayID, d)) + + // ACT + parsedTX, err := gw.ParseTransaction(tx) + + // ASSERT + require.NoError(t, err) + + d2, err := parsedTX.Donation() + require.NoError(t, err) + + require.Equal(t, int(d.Amount.Uint64()), int(d2.Amount.Uint64())) + require.Equal(t, d.Sender.ToRaw(), d2.Sender.ToRaw()) + }) + + t.Run("Deposit", func(t *testing.T) { + // ARRANGE + d := toncontracts.Deposit{ + Sender: GenerateTONAccountID(), + Amount: sdkmath.NewUint(200_000_000), + Recipient: EthAddress(), + } + + tx := TONTransaction(t, TONDepositProps(t, gatewayID, d)) + + // ACT + parsedTX, err := gw.ParseTransaction(tx) + + // ASSERT + require.NoError(t, err) + + d2, err := parsedTX.Deposit() + require.NoError(t, err) + + require.Equal(t, int(d.Amount.Uint64()), int(d2.Amount.Uint64())) + require.Equal(t, d.Sender.ToRaw(), d2.Sender.ToRaw()) + require.Equal(t, d.Recipient.Hex(), d2.Recipient.Hex()) + require.Equal(t, d.Memo(), d2.Memo()) + }) + + t.Run("Deposit and call", func(t *testing.T) { + // ARRANGE + d := toncontracts.DepositAndCall{ + Deposit: toncontracts.Deposit{ + Sender: GenerateTONAccountID(), + Amount: sdkmath.NewUint(300_000_000), + Recipient: EthAddress(), + }, + CallData: []byte("Evidently, the most known and used kind of dictionaries in TON is hashmap."), + } + + tx := TONTransaction(t, TONDepositAndCallProps(t, gatewayID, d)) + + // ACT + parsedTX, err := gw.ParseTransaction(tx) + + // ASSERT + require.NoError(t, err) + + d2, err := parsedTX.DepositAndCall() + require.NoError(t, err) + + require.Equal(t, int(d.Amount.Uint64()), int(d2.Amount.Uint64())) + require.Equal(t, d.Sender.ToRaw(), d2.Sender.ToRaw()) + require.Equal(t, d.Recipient.Hex(), d2.Recipient.Hex()) + require.Equal(t, d.CallData, d2.CallData) + require.Equal(t, d.Memo(), d2.Memo()) + }) + +} diff --git a/typescript/zetachain/zetacore/crosschain/cross_chain_tx_pb.d.ts b/typescript/zetachain/zetacore/crosschain/cross_chain_tx_pb.d.ts index 1ffa04eaca..4ac84ac5e7 100644 --- a/typescript/zetachain/zetacore/crosschain/cross_chain_tx_pb.d.ts +++ b/typescript/zetachain/zetacore/crosschain/cross_chain_tx_pb.d.ts @@ -165,6 +165,14 @@ export declare class InboundParams extends Message { */ txFinalizationStatus: TxFinalizationStatus; + /** + * this field describes if a smart contract call should be made for a inbound + * with assets only used for protocol contract version 2 + * + * @generated from field: bool is_cross_chain_call = 12; + */ + isCrossChainCall: boolean; + constructor(data?: PartialMessage); static readonly runtime: typeof proto3; @@ -355,10 +363,21 @@ export declare class Status extends Message { status: CctxStatus; /** + * status_message carries information about the status transitions: + * why they were triggered, old and new status. + * * @generated from field: string status_message = 2; */ statusMessage: string; + /** + * error_message carries information about the error that caused the tx + * to be PendingRevert, Reverted or Aborted. + * + * @generated from field: string error_message = 6; + */ + errorMessage: string; + /** * @generated from field: int64 lastUpdate_timestamp = 3; */ diff --git a/typescript/zetachain/zetacore/crosschain/index.d.ts b/typescript/zetachain/zetacore/crosschain/index.d.ts index d5819411ec..8f9ae8e678 100644 --- a/typescript/zetachain/zetacore/crosschain/index.d.ts +++ b/typescript/zetachain/zetacore/crosschain/index.d.ts @@ -5,6 +5,7 @@ export * from "./genesis_pb"; export * from "./inbound_hash_to_cctx_pb"; export * from "./inbound_tracker_pb"; export * from "./last_block_height_pb"; +export * from "./legacy_msgs_pb"; export * from "./outbound_tracker_pb"; export * from "./query_pb"; export * from "./rate_limiter_flags_pb"; diff --git a/typescript/zetachain/zetacore/crosschain/legacy_msgs_pb.d.ts b/typescript/zetachain/zetacore/crosschain/legacy_msgs_pb.d.ts new file mode 100644 index 0000000000..fe99443d97 --- /dev/null +++ b/typescript/zetachain/zetacore/crosschain/legacy_msgs_pb.d.ts @@ -0,0 +1,389 @@ +// @generated by protoc-gen-es v1.3.0 with parameter "target=dts" +// @generated from file zetachain/zetacore/crosschain/legacy_msgs.proto (package zetachain.zetacore.crosschain, syntax proto3) +/* eslint-disable */ +// @ts-nocheck + +import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; +import { Message, proto3 } from "@bufbuild/protobuf"; +import type { Proof } from "../pkg/proofs/proofs_pb.js"; +import type { CoinType } from "../pkg/coin/coin_pb.js"; +import type { ReceiveStatus } from "../pkg/chains/chains_pb.js"; + +/** + * legacy MsgAddOutboundTracker + * defined to keep codec compatibility + * + * @generated from message zetachain.zetacore.crosschain.MsgAddToOutTxTracker + */ +export declare class MsgAddToOutTxTracker extends Message { + /** + * @generated from field: string creator = 1; + */ + creator: string; + + /** + * @generated from field: int64 chain_id = 2; + */ + chainId: bigint; + + /** + * @generated from field: uint64 nonce = 3; + */ + nonce: bigint; + + /** + * @generated from field: string tx_hash = 4; + */ + txHash: string; + + /** + * @generated from field: zetachain.zetacore.pkg.proofs.Proof proof = 5; + */ + proof?: Proof; + + /** + * @generated from field: string block_hash = 6; + */ + blockHash: string; + + /** + * @generated from field: int64 tx_index = 7; + */ + txIndex: bigint; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.crosschain.MsgAddToOutTxTracker"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): MsgAddToOutTxTracker; + + static fromJson(jsonValue: JsonValue, options?: Partial): MsgAddToOutTxTracker; + + static fromJsonString(jsonString: string, options?: Partial): MsgAddToOutTxTracker; + + static equals(a: MsgAddToOutTxTracker | PlainMessage | undefined, b: MsgAddToOutTxTracker | PlainMessage | undefined): boolean; +} + +/** + * legacy MsgAddInboundTracker + * defined to keep codec compatibility + * + * @generated from message zetachain.zetacore.crosschain.MsgAddToInTxTracker + */ +export declare class MsgAddToInTxTracker extends Message { + /** + * @generated from field: string creator = 1; + */ + creator: string; + + /** + * @generated from field: int64 chain_id = 2; + */ + chainId: bigint; + + /** + * @generated from field: string tx_hash = 3; + */ + txHash: string; + + /** + * @generated from field: zetachain.zetacore.pkg.coin.CoinType coin_type = 4; + */ + coinType: CoinType; + + /** + * @generated from field: zetachain.zetacore.pkg.proofs.Proof proof = 5; + */ + proof?: Proof; + + /** + * @generated from field: string block_hash = 6; + */ + blockHash: string; + + /** + * @generated from field: int64 tx_index = 7; + */ + txIndex: bigint; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.crosschain.MsgAddToInTxTracker"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): MsgAddToInTxTracker; + + static fromJson(jsonValue: JsonValue, options?: Partial): MsgAddToInTxTracker; + + static fromJsonString(jsonString: string, options?: Partial): MsgAddToInTxTracker; + + static equals(a: MsgAddToInTxTracker | PlainMessage | undefined, b: MsgAddToInTxTracker | PlainMessage | undefined): boolean; +} + +/** + * legacy MsgRemoveOutboundTracker + * defined to keep codec compatibility + * + * @generated from message zetachain.zetacore.crosschain.MsgRemoveFromOutTxTracker + */ +export declare class MsgRemoveFromOutTxTracker extends Message { + /** + * @generated from field: string creator = 1; + */ + creator: string; + + /** + * @generated from field: int64 chain_id = 2; + */ + chainId: bigint; + + /** + * @generated from field: uint64 nonce = 3; + */ + nonce: bigint; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.crosschain.MsgRemoveFromOutTxTracker"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): MsgRemoveFromOutTxTracker; + + static fromJson(jsonValue: JsonValue, options?: Partial): MsgRemoveFromOutTxTracker; + + static fromJsonString(jsonString: string, options?: Partial): MsgRemoveFromOutTxTracker; + + static equals(a: MsgRemoveFromOutTxTracker | PlainMessage | undefined, b: MsgRemoveFromOutTxTracker | PlainMessage | undefined): boolean; +} + +/** + * legacy MsgVoteOutbound + * defined to keep codec compatibility + * + * @generated from message zetachain.zetacore.crosschain.MsgVoteOnObservedOutboundTx + */ +export declare class MsgVoteOnObservedOutboundTx extends Message { + /** + * @generated from field: string creator = 1; + */ + creator: string; + + /** + * @generated from field: string cctx_hash = 2; + */ + cctxHash: string; + + /** + * @generated from field: string observed_outTx_hash = 3; + */ + observedOutTxHash: string; + + /** + * @generated from field: uint64 observed_outTx_blockHeight = 4; + */ + observedOutTxBlockHeight: bigint; + + /** + * @generated from field: uint64 observed_outTx_gas_used = 10; + */ + observedOutTxGasUsed: bigint; + + /** + * @generated from field: string observed_outTx_effective_gas_price = 11; + */ + observedOutTxEffectiveGasPrice: string; + + /** + * @generated from field: uint64 observed_outTx_effective_gas_limit = 12; + */ + observedOutTxEffectiveGasLimit: bigint; + + /** + * @generated from field: string value_received = 5; + */ + valueReceived: string; + + /** + * @generated from field: zetachain.zetacore.pkg.chains.ReceiveStatus status = 6; + */ + status: ReceiveStatus; + + /** + * @generated from field: int64 outTx_chain = 7; + */ + outTxChain: bigint; + + /** + * @generated from field: uint64 outTx_tss_nonce = 8; + */ + outTxTssNonce: bigint; + + /** + * @generated from field: zetachain.zetacore.pkg.coin.CoinType coin_type = 9; + */ + coinType: CoinType; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.crosschain.MsgVoteOnObservedOutboundTx"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): MsgVoteOnObservedOutboundTx; + + static fromJson(jsonValue: JsonValue, options?: Partial): MsgVoteOnObservedOutboundTx; + + static fromJsonString(jsonString: string, options?: Partial): MsgVoteOnObservedOutboundTx; + + static equals(a: MsgVoteOnObservedOutboundTx | PlainMessage | undefined, b: MsgVoteOnObservedOutboundTx | PlainMessage | undefined): boolean; +} + +/** + * legacy MsgVoteInbound + * defined to keep codec compatibility + * + * @generated from message zetachain.zetacore.crosschain.MsgVoteOnObservedInboundTx + */ +export declare class MsgVoteOnObservedInboundTx extends Message { + /** + * @generated from field: string creator = 1; + */ + creator: string; + + /** + * @generated from field: string sender = 2; + */ + sender: string; + + /** + * @generated from field: int64 sender_chain_id = 3; + */ + senderChainId: bigint; + + /** + * @generated from field: string receiver = 4; + */ + receiver: string; + + /** + * @generated from field: int64 receiver_chain = 5; + */ + receiverChain: bigint; + + /** + * string zeta_burnt = 6; + * + * @generated from field: string amount = 6; + */ + amount: string; + + /** + * string mMint = 7; + * + * @generated from field: string message = 8; + */ + message: string; + + /** + * @generated from field: string in_tx_hash = 9; + */ + inTxHash: string; + + /** + * @generated from field: uint64 in_block_height = 10; + */ + inBlockHeight: bigint; + + /** + * @generated from field: uint64 gas_limit = 11; + */ + gasLimit: bigint; + + /** + * @generated from field: zetachain.zetacore.pkg.coin.CoinType coin_type = 12; + */ + coinType: CoinType; + + /** + * @generated from field: string tx_origin = 13; + */ + txOrigin: string; + + /** + * @generated from field: string asset = 14; + */ + asset: string; + + /** + * event index of the sent asset in the observed tx + * + * @generated from field: uint64 event_index = 15; + */ + eventIndex: bigint; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.crosschain.MsgVoteOnObservedInboundTx"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): MsgVoteOnObservedInboundTx; + + static fromJson(jsonValue: JsonValue, options?: Partial): MsgVoteOnObservedInboundTx; + + static fromJsonString(jsonString: string, options?: Partial): MsgVoteOnObservedInboundTx; + + static equals(a: MsgVoteOnObservedInboundTx | PlainMessage | undefined, b: MsgVoteOnObservedInboundTx | PlainMessage | undefined): boolean; +} + +/** + * legacy MsgVoteGasPrice + * defined to keep codec compatibility + * + * @generated from message zetachain.zetacore.crosschain.MsgGasPriceVoter + */ +export declare class MsgGasPriceVoter extends Message { + /** + * @generated from field: string creator = 1; + */ + creator: string; + + /** + * @generated from field: int64 chain_id = 2; + */ + chainId: bigint; + + /** + * @generated from field: uint64 price = 3; + */ + price: bigint; + + /** + * @generated from field: uint64 block_number = 4; + */ + blockNumber: bigint; + + /** + * @generated from field: string supply = 5; + */ + supply: string; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.crosschain.MsgGasPriceVoter"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): MsgGasPriceVoter; + + static fromJson(jsonValue: JsonValue, options?: Partial): MsgGasPriceVoter; + + static fromJsonString(jsonString: string, options?: Partial): MsgGasPriceVoter; + + static equals(a: MsgGasPriceVoter | PlainMessage | undefined, b: MsgGasPriceVoter | PlainMessage | undefined): boolean; +} + diff --git a/typescript/zetachain/zetacore/crosschain/tx_pb.d.ts b/typescript/zetachain/zetacore/crosschain/tx_pb.d.ts index 751371fa03..f830e00a48 100644 --- a/typescript/zetachain/zetacore/crosschain/tx_pb.d.ts +++ b/typescript/zetachain/zetacore/crosschain/tx_pb.d.ts @@ -430,6 +430,12 @@ export declare class MsgVoteGasPrice extends Message { */ blockNumber: bigint; + /** + * @generated from field: string supply = 5 [deprecated = true]; + * @deprecated + */ + supply: string; + constructor(data?: PartialMessage); static readonly runtime: typeof proto3; @@ -663,6 +669,13 @@ export declare class MsgVoteInbound extends Message { */ callOptions?: CallOptions; + /** + * define if a smart contract call should be made with asset + * + * @generated from field: bool is_cross_chain_call = 19; + */ + isCrossChainCall: boolean; + constructor(data?: PartialMessage); static readonly runtime: typeof proto3; diff --git a/typescript/zetachain/zetacore/pkg/chains/chains_pb.d.ts b/typescript/zetachain/zetacore/pkg/chains/chains_pb.d.ts index af161efd7b..9e68854962 100644 --- a/typescript/zetachain/zetacore/pkg/chains/chains_pb.d.ts +++ b/typescript/zetachain/zetacore/pkg/chains/chains_pb.d.ts @@ -197,6 +197,11 @@ export declare enum Network { * @generated from enum value: solana = 7; */ solana = 7, + + /** + * @generated from enum value: ton = 8; + */ + ton = 8, } /** @@ -248,6 +253,11 @@ export declare enum Vm { * @generated from enum value: svm = 2; */ svm = 2, + + /** + * @generated from enum value: tvm = 3; + */ + tvm = 3, } /** @@ -282,6 +292,13 @@ export declare enum Consensus { * @generated from enum value: solana_consensus = 4; */ solana_consensus = 4, + + /** + * ton + * + * @generated from enum value: catchain_consensus = 5; + */ + catchain_consensus = 5, } /** diff --git a/version.sh b/version.sh index ac8987e430..aedbfa5eeb 100755 --- a/version.sh +++ b/version.sh @@ -1,5 +1,12 @@ #!/bin/bash +# if NODE_VERSION is set, just return it immediately +# this allows you to do docker builds without the git directory +if [[ -n $NODE_VERSION ]]; then + echo $NODE_VERSION + exit +fi + # --exact-match will ensure the tag is only returned if our commit is a tag version=$(git describe --exact-match --tags 2>/dev/null) if [[ $? -eq 0 ]]; then @@ -9,14 +16,14 @@ if [[ $? -eq 0 ]]; then exit fi -# use current timestamp for dirty builds +# develop build and use commit timestamp for version +commit_timestamp=$(git show --no-patch --format=%at) +short_commit=$(git rev-parse --short HEAD) + +# append -dirty for dirty builds if ! git diff --no-ext-diff --quiet --exit-code ; then - current_timestamp=$(date +"%s") - echo "0.0.${current_timestamp}-dirty" + echo "0.0.${commit_timestamp}-${short_commit}-dirty" exit fi -# otherwise assume we are on a develop build and use commit timestamp for version -commit_timestamp=$(git show --no-patch --format=%at) - -echo "0.0.${commit_timestamp}-develop" \ No newline at end of file +echo "0.0.${commit_timestamp}-${short_commit}" \ No newline at end of file diff --git a/x/crosschain/keeper/cctx_gateway_observers.go b/x/crosschain/keeper/cctx_gateway_observers.go index 39a33f4edc..9576bcfe32 100644 --- a/x/crosschain/keeper/cctx_gateway_observers.go +++ b/x/crosschain/keeper/cctx_gateway_observers.go @@ -75,7 +75,7 @@ func (c CCTXGatewayObservers) InitiateOutbound( }() if err != nil { // do not commit anything here as the CCTX should be aborted - config.CCTX.SetAbort(err.Error()) + config.CCTX.SetAbort("internal error", err.Error()) return types.CctxStatus_Aborted, err } commit() diff --git a/x/crosschain/keeper/cctx_gateway_zevm.go b/x/crosschain/keeper/cctx_gateway_zevm.go index 81366d6af3..fc247ad7a4 100644 --- a/x/crosschain/keeper/cctx_gateway_zevm.go +++ b/x/crosschain/keeper/cctx_gateway_zevm.go @@ -28,7 +28,9 @@ func (c CCTXGatewayZEVM) InitiateOutbound( if err != nil && !isContractReverted { // exceptional case; internal error; should abort CCTX - config.CCTX.SetAbort(err.Error()) + config.CCTX.SetAbort( + "error during deposit that is not smart contract revert", + err.Error()) return types.CctxStatus_Aborted, err } diff --git a/x/crosschain/keeper/cctx_orchestrator_validate_outbound.go b/x/crosschain/keeper/cctx_orchestrator_validate_outbound.go index 17c848613a..2f3acdd744 100644 --- a/x/crosschain/keeper/cctx_orchestrator_validate_outbound.go +++ b/x/crosschain/keeper/cctx_orchestrator_validate_outbound.go @@ -53,7 +53,9 @@ func (k Keeper) ValidateOutboundZEVM( cctx.InboundParams.Amount, ) if err != nil { - cctx.SetAbort(fmt.Sprintf("%s : %s", depositErr, err.Error())) + cctx.SetAbort( + "revert failed", + fmt.Sprintf("deposit error: %s, processing error: %s", depositErr.Error(), err.Error())) return types.CctxStatus_Aborted } @@ -122,7 +124,7 @@ func (k Keeper) processFailedOutboundObservers(ctx sdk.Context, cctx *types.Cros if cctx.InboundParams.CoinType == coin.CoinType_Cmd { // if the cctx is of coin type cmd or the sender chain is zeta chain, then we do not revert, the cctx is aborted cctx.GetCurrentOutboundParam().TxFinalizationStatus = types.TxFinalizationStatus_Executed - cctx.SetAbort("Outbound failed, cmd cctx reverted") + cctx.SetAbort("", "outbound failed for admin tx") } else if chains.IsZetaChain(cctx.InboundParams.SenderChainId, k.GetAuthorityKeeper().GetAdditionalChainList(ctx)) { switch cctx.InboundParams.CoinType { // Try revert if the coin-type is ZETA @@ -137,7 +139,7 @@ func (k Keeper) processFailedOutboundObservers(ctx sdk.Context, cctx *types.Cros default: { cctx.GetCurrentOutboundParam().TxFinalizationStatus = types.TxFinalizationStatus_Executed - cctx.SetAbort("Outbound failed for non-ZETA cctx") + cctx.SetAbort("", "outbound failed for non-ZETA cctx") } } } else { @@ -195,10 +197,10 @@ func (k Keeper) processFailedOutboundOnExternalChain( return err } // Not setting the finalization status here, the required changes have been made while creating the revert tx - cctx.SetPendingRevert(revertMsg) + cctx.SetPendingRevert("", revertMsg) case types.CctxStatus_PendingRevert: cctx.GetCurrentOutboundParam().TxFinalizationStatus = types.TxFinalizationStatus_Executed - cctx.SetAbort("Outbound failed: revert failed; abort TX") + cctx.SetAbort("aborted while processing failed outbound", "outbound and revert failed") } return nil } @@ -225,9 +227,9 @@ func (k Keeper) processSuccessfulOutbound( oldStatus := cctx.CctxStatus.Status switch oldStatus { case types.CctxStatus_PendingRevert: - cctx.SetReverted("Outbound succeeded, revert executed") + cctx.SetReverted("", "revert executed") case types.CctxStatus_PendingOutbound: - cctx.SetOutboundMined("Outbound succeeded, mined") + cctx.SetOutboundMined("") default: return } @@ -256,7 +258,7 @@ func (k Keeper) processFailedZETAOutboundOnZEVM(ctx sdk.Context, cctx *types.Cro } // Trying to revert the transaction this would get set to a finalized status in the same block as this does not need a TSS singing - cctx.SetPendingRevert("Outbound failed, trying revert") + cctx.SetPendingRevert("", "outbound failed") data, err := base64.StdEncoding.DecodeString(cctx.RelayedMessage) if err != nil { return fmt.Errorf("failed decoding relayed message: %s", err.Error()) @@ -290,7 +292,7 @@ func (k Keeper) processFailedZETAOutboundOnZEVM(ctx sdk.Context, cctx *types.Cro return fmt.Errorf("failed ZETARevertAndCallContract: %s", err.Error()) } - cctx.SetReverted("Outbound failed, revert executed") + cctx.SetReverted("", "outbound failed") if len(ctx.TxBytes()) > 0 { // add event for tendermint transaction hash format hash := tmbytes.HexBytes(tmtypes.Tx(ctx.TxBytes()).Hash()) @@ -336,7 +338,7 @@ func (k Keeper) processFailedOutboundV2(ctx sdk.Context, cctx *types.CrossChainT } // update status - cctx.SetPendingRevert("Outbound failed, trying revert") + cctx.SetPendingRevert("", "outbound failed") // process the revert on ZEVM if err := k.fungibleKeeper.ProcessV2RevertDeposit( @@ -354,7 +356,7 @@ func (k Keeper) processFailedOutboundV2(ctx sdk.Context, cctx *types.CrossChainT } // tx is reverted - cctx.SetReverted("Outbound failed, revert executed") + cctx.SetReverted("", "outbound failed") // add event for tendermint transaction hash format if len(ctx.TxBytes()) > 0 { @@ -367,7 +369,7 @@ func (k Keeper) processFailedOutboundV2(ctx sdk.Context, cctx *types.CrossChainT cctx.GetCurrentOutboundParam().TxFinalizationStatus = types.TxFinalizationStatus_Executed case types.CctxStatus_PendingRevert: cctx.GetCurrentOutboundParam().TxFinalizationStatus = types.TxFinalizationStatus_Executed - cctx.SetAbort("Outbound failed: revert failed; abort TX") + cctx.SetAbort("aborted while processing failed outbound", "outbound and revert failed") } return nil } diff --git a/x/crosschain/keeper/cctx_test.go b/x/crosschain/keeper/cctx_test.go index 8a51b4cf2a..d1e3f6fafc 100644 --- a/x/crosschain/keeper/cctx_test.go +++ b/x/crosschain/keeper/cctx_test.go @@ -170,7 +170,6 @@ func TestCCTXs(t *testing.T) { require.True(t, found) require.Equal(t, s, send) } - }) } } diff --git a/x/crosschain/keeper/evm_deposit.go b/x/crosschain/keeper/evm_deposit.go index c873e9b636..e57dc16d1b 100644 --- a/x/crosschain/keeper/evm_deposit.go +++ b/x/crosschain/keeper/evm_deposit.go @@ -14,6 +14,7 @@ import ( "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/coin" + "github.com/zeta-chain/node/pkg/memo" "github.com/zeta-chain/node/x/crosschain/types" fungibletypes "github.com/zeta-chain/node/x/fungible/types" ) @@ -78,7 +79,7 @@ func (k Keeper) HandleEVMDeposit(ctx sdk.Context, cctx *types.CrossChainTx) (boo // in protocol version 2, the destination of the deposit is always the to address, the message is the data to be sent to the contract if cctx.ProtocolContractVersion == types.ProtocolContractVersion_V1 { var parsedAddress ethcommon.Address - parsedAddress, message, err = chains.ParseAddressAndData(cctx.RelayedMessage) + parsedAddress, message, err = memo.DecodeLegacyMemoHex(cctx.RelayedMessage) if err != nil { return false, errors.Wrap(types.ErrUnableToParseAddress, err.Error()) } @@ -96,7 +97,7 @@ func (k Keeper) HandleEVMDeposit(ctx sdk.Context, cctx *types.CrossChainTx) (boo from, err := chains.DecodeAddressFromChainID(inboundSenderChainID, inboundSender, k.GetAuthorityKeeper().GetAdditionalChainList(ctx)) if err != nil { - return false, fmt.Errorf("HandleEVMDeposit: unable to decode address: %s", err.Error()) + return false, fmt.Errorf("HandleEVMDeposit: unable to decode address: %w", err) } evmTxResponse, contractCall, err := k.fungibleKeeper.ZRC20DepositAndCallContract( @@ -109,6 +110,7 @@ func (k Keeper) HandleEVMDeposit(ctx sdk.Context, cctx *types.CrossChainTx) (boo inboundCoinType, cctx.InboundParams.Asset, cctx.ProtocolContractVersion, + cctx.InboundParams.IsCrossChainCall, ) if fungibletypes.IsContractReverted(evmTxResponse, err) || errShouldRevertCctx(err) { return true, err diff --git a/x/crosschain/keeper/evm_deposit_test.go b/x/crosschain/keeper/evm_deposit_test.go index e933d168aa..45c0a0aecc 100644 --- a/x/crosschain/keeper/evm_deposit_test.go +++ b/x/crosschain/keeper/evm_deposit_test.go @@ -107,6 +107,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { coin.CoinType_ERC20, mock.Anything, mock.Anything, + mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{}, false, nil) // call HandleEVMDeposit @@ -153,6 +154,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { coin.CoinType_ERC20, mock.Anything, mock.Anything, + mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{ Logs: []*evmtypes.Log{ { @@ -216,6 +218,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { coin.CoinType_ERC20, mock.Anything, mock.Anything, + mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{ Logs: []*evmtypes.Log{ { @@ -306,6 +309,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { coin.CoinType_ERC20, mock.Anything, mock.Anything, + mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{}, false, errDeposit) // call HandleEVMDeposit @@ -351,6 +355,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { coin.CoinType_ERC20, mock.Anything, mock.Anything, + mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{VmError: "reverted"}, false, errDeposit) // call HandleEVMDeposit @@ -395,6 +400,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { coin.CoinType_ERC20, mock.Anything, mock.Anything, + mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{}, false, fungibletypes.ErrForeignCoinCapReached) // call HandleEVMDeposit @@ -439,6 +445,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { coin.CoinType_ERC20, mock.Anything, mock.Anything, + mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{}, false, fungibletypes.ErrPausedZRC20) // call HandleEVMDeposit @@ -483,6 +490,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { coin.CoinType_ERC20, mock.Anything, mock.Anything, + mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{}, false, fungibletypes.ErrCallNonContract) // call HandleEVMDeposit @@ -553,6 +561,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { coin.CoinType_ERC20, mock.Anything, mock.Anything, + mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{}, false, nil) cctx.GetCurrentOutboundParam().Receiver = sample.EthAddress().String() @@ -596,6 +605,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { coin.CoinType_ERC20, mock.Anything, mock.Anything, + mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{}, false, nil) cctx := sample.CrossChainTx(t, "foo") diff --git a/x/crosschain/keeper/evm_hooks_test.go b/x/crosschain/keeper/evm_hooks_test.go index 3f4e049d33..fff7e31f02 100644 --- a/x/crosschain/keeper/evm_hooks_test.go +++ b/x/crosschain/keeper/evm_hooks_test.go @@ -102,7 +102,7 @@ func SetupStateForProcessLogs( func TestParseZRC20WithdrawalEvent(t *testing.T) { t.Run("unable to parse an event with an invalid address in event log", func(t *testing.T) { - for i, log := range sample.GetInvalidZRC20WithdrawToExternal(t).Logs { + for i, log := range sample.InvalidZRC20WithdrawToExternalReceipt(t).Logs { event, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*log) if i < 3 { require.ErrorContains(t, err, "event signature mismatch") @@ -116,7 +116,7 @@ func TestParseZRC20WithdrawalEvent(t *testing.T) { }) t.Run("successfully parse event for a valid BTC withdrawal", func(t *testing.T) { - for i, log := range sample.GetValidZRC20WithdrawToBTC(t).Logs { + for i, log := range sample.ValidZRC20WithdrawToBTCReceipt(t).Logs { event, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*log) if i < 3 { require.ErrorContains(t, err, "event signature mismatch") @@ -131,7 +131,7 @@ func TestParseZRC20WithdrawalEvent(t *testing.T) { }) t.Run("successfully parse valid event for ETH withdrawal", func(t *testing.T) { - for i, log := range sample.GetValidZrc20WithdrawToETH(t).Logs { + for i, log := range sample.ValidZrc20WithdrawToETHReceipt(t).Logs { event, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*log) if i != 11 { require.ErrorContains(t, err, "event signature mismatch") @@ -146,7 +146,7 @@ func TestParseZRC20WithdrawalEvent(t *testing.T) { }) t.Run("failed to parse event with a valid address but no topic present", func(t *testing.T) { - for _, log := range sample.GetValidZRC20WithdrawToBTC(t).Logs { + for _, log := range sample.ValidZRC20WithdrawToBTCReceipt(t).Logs { log.Topics = nil event, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*log) require.ErrorContains(t, err, "invalid log - no topics") @@ -158,7 +158,7 @@ func TestValidateZrc20WithdrawEvent(t *testing.T) { t.Run("successfully validate a valid event", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) btcMainNetWithdrawalEvent, err := crosschainkeeper.ParseZRC20WithdrawalEvent( - *sample.GetValidZRC20WithdrawToBTC(t).Logs[3], + *sample.ValidZRC20WithdrawToBTCReceipt(t).Logs[3], ) require.NoError(t, err) err = k.ValidateZrc20WithdrawEvent(ctx, btcMainNetWithdrawalEvent, chains.BitcoinMainnet.ChainId) @@ -168,7 +168,7 @@ func TestValidateZrc20WithdrawEvent(t *testing.T) { t.Run("unable to validate a btc withdrawal event with an invalid amount", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) btcMainNetWithdrawalEvent, err := crosschainkeeper.ParseZRC20WithdrawalEvent( - *sample.GetValidZRC20WithdrawToBTC(t).Logs[3], + *sample.ValidZRC20WithdrawToBTCReceipt(t).Logs[3], ) require.NoError(t, err) @@ -186,7 +186,7 @@ func TestValidateZrc20WithdrawEvent(t *testing.T) { t.Run("unable to validate a event with an invalid chain ID", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) btcMainNetWithdrawalEvent, err := crosschainkeeper.ParseZRC20WithdrawalEvent( - *sample.GetValidZRC20WithdrawToBTC(t).Logs[3], + *sample.ValidZRC20WithdrawToBTCReceipt(t).Logs[3], ) require.NoError(t, err) err = k.ValidateZrc20WithdrawEvent(ctx, btcMainNetWithdrawalEvent, chains.BitcoinTestnet.ChainId) @@ -196,7 +196,7 @@ func TestValidateZrc20WithdrawEvent(t *testing.T) { t.Run("unable to validate an unsupported address type", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) btcMainNetWithdrawalEvent, err := crosschainkeeper.ParseZRC20WithdrawalEvent( - *sample.GetValidZRC20WithdrawToBTC(t).Logs[3], + *sample.ValidZRC20WithdrawToBTCReceipt(t).Logs[3], ) require.NoError(t, err) btcMainNetWithdrawalEvent.To = []byte("04b2891ba8cb491828db3ebc8a780d43b169e7b3974114e6e50f9bab6ec" + @@ -248,7 +248,7 @@ func TestKeeper_ProcessZRC20WithdrawalEvent(t *testing.T) { setSupportedChain(ctx, zk, []int64{chainID, senderChain.ChainId}...) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - event, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*sample.GetValidZRC20WithdrawToBTC(t).Logs[3]) + event, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*sample.ValidZRC20WithdrawToBTCReceipt(t).Logs[3]) require.NoError(t, err) zrc20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "bitcoin", "BTC") event.Raw.Address = zrc20 @@ -274,7 +274,7 @@ func TestKeeper_ProcessZRC20WithdrawalEvent(t *testing.T) { setSupportedChain(ctx, zk, []int64{chainID, senderChain.ChainId}...) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - event, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*sample.GetValidZrc20WithdrawToETH(t).Logs[11]) + event, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*sample.ValidZrc20WithdrawToETHReceipt(t).Logs[11]) require.NoError(t, err) zrc20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "ethereum", "ETH") event.Raw.Address = zrc20 @@ -299,7 +299,7 @@ func TestKeeper_ProcessZRC20WithdrawalEvent(t *testing.T) { setSupportedChain(ctx, zk, chainID) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - event, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*sample.GetValidZrc20WithdrawToETH(t).Logs[11]) + event, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*sample.ValidZrc20WithdrawToETHReceipt(t).Logs[11]) require.NoError(t, err) setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "ethereum", "ETH") emittingContract := sample.EthAddress() @@ -318,7 +318,7 @@ func TestKeeper_ProcessZRC20WithdrawalEvent(t *testing.T) { chainID := chain.ChainId SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - event, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*sample.GetValidZrc20WithdrawToETH(t).Logs[11]) + event, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*sample.ValidZrc20WithdrawToETHReceipt(t).Logs[11]) require.NoError(t, err) zrc20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "ethereum", "ETH") event.Raw.Address = zrc20 @@ -339,7 +339,7 @@ func TestKeeper_ProcessZRC20WithdrawalEvent(t *testing.T) { setSupportedChain(ctx, zk, chainID) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - event, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*sample.GetValidZrc20WithdrawToETH(t).Logs[11]) + event, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*sample.ValidZrc20WithdrawToETHReceipt(t).Logs[11]) require.NoError(t, err) zrc20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "ethereum", "ETH") event.Raw.Address = zrc20 @@ -362,7 +362,7 @@ func TestKeeper_ProcessZRC20WithdrawalEvent(t *testing.T) { setSupportedChain(ctx, zk, chainID) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - event, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*sample.GetValidZrc20WithdrawToETH(t).Logs[11]) + event, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*sample.ValidZrc20WithdrawToETHReceipt(t).Logs[11]) require.NoError(t, err) zrc20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "ethereum", "ETH") event.Raw.Address = zrc20 @@ -387,7 +387,7 @@ func TestKeeper_ProcessZRC20WithdrawalEvent(t *testing.T) { setSupportedChain(ctx, zk, chainID) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - event, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*sample.GetValidZrc20WithdrawToETH(t).Logs[11]) + event, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*sample.ValidZrc20WithdrawToETHReceipt(t).Logs[11]) require.NoError(t, err) zrc20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "ethereum", "ETH") event.Raw.Address = zrc20 @@ -415,7 +415,7 @@ func TestKeeper_ProcessZRC20WithdrawalEvent(t *testing.T) { SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) k.RemoveGasPrice(ctx, strconv.FormatInt(chainID, 10)) - event, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*sample.GetValidZrc20WithdrawToETH(t).Logs[11]) + event, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*sample.ValidZrc20WithdrawToETHReceipt(t).Logs[11]) require.NoError(t, err) zrc20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "ethereum", "ETH") event.Raw.Address = zrc20 @@ -441,7 +441,7 @@ func TestKeeper_ProcessZRC20WithdrawalEvent(t *testing.T) { Nonce: 1, }) - event, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*sample.GetValidZrc20WithdrawToETH(t).Logs[11]) + event, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*sample.ValidZrc20WithdrawToETHReceipt(t).Logs[11]) require.NoError(t, err) zrc20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "ethereum", "ETH") event.Raw.Address = zrc20 @@ -456,7 +456,7 @@ func TestKeeper_ProcessZRC20WithdrawalEvent(t *testing.T) { func TestKeeper_ParseZetaSentEvent(t *testing.T) { t.Run("successfully parse a valid event", func(t *testing.T) { - logs := sample.GetValidZetaSentDestinationExternal(t).Logs + logs := sample.ValidZetaSentDestinationExternalReceipt(t).Logs for i, log := range logs { connector := log.Address event, err := crosschainkeeper.ParseZetaSentEvent(*log, connector) @@ -473,7 +473,7 @@ func TestKeeper_ParseZetaSentEvent(t *testing.T) { }) t.Run("unable to parse if topics field is empty", func(t *testing.T) { - logs := sample.GetValidZetaSentDestinationExternal(t).Logs + logs := sample.ValidZetaSentDestinationExternalReceipt(t).Logs for _, log := range logs { connector := log.Address log.Topics = nil @@ -484,7 +484,7 @@ func TestKeeper_ParseZetaSentEvent(t *testing.T) { }) t.Run("unable to parse if connector address does not match", func(t *testing.T) { - logs := sample.GetValidZetaSentDestinationExternal(t).Logs + logs := sample.ValidZetaSentDestinationExternalReceipt(t).Logs for i, log := range logs { event, err := crosschainkeeper.ParseZetaSentEvent(*log, sample.EthAddress()) if i < 4 { @@ -522,8 +522,8 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { require.NoError(t, err) event, err := crosschainkeeper.ParseZetaSentEvent( - *sample.GetValidZetaSentDestinationExternal(t).Logs[4], - sample.GetValidZetaSentDestinationExternal(t).Logs[4].Address, + *sample.ValidZetaSentDestinationExternalReceipt(t).Logs[4], + sample.ValidZetaSentDestinationExternalReceipt(t).Logs[4].Address, ) require.NoError(t, err) emittingContract := sample.EthAddress() @@ -558,8 +558,8 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { SetupStateForProcessLogsZetaSent(t, ctx, k, zk, sdkk, chain, admin) event, err := crosschainkeeper.ParseZetaSentEvent( - *sample.GetValidZetaSentDestinationExternal(t).Logs[4], - sample.GetValidZetaSentDestinationExternal(t).Logs[4].Address, + *sample.ValidZetaSentDestinationExternalReceipt(t).Logs[4], + sample.ValidZetaSentDestinationExternalReceipt(t).Logs[4].Address, ) require.NoError(t, err) emittingContract := sample.EthAddress() @@ -588,8 +588,8 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { require.NoError(t, err) event, err := crosschainkeeper.ParseZetaSentEvent( - *sample.GetValidZetaSentDestinationExternal(t).Logs[4], - sample.GetValidZetaSentDestinationExternal(t).Logs[4].Address, + *sample.ValidZetaSentDestinationExternalReceipt(t).Logs[4], + sample.ValidZetaSentDestinationExternalReceipt(t).Logs[4].Address, ) require.NoError(t, err) emittingContract := sample.EthAddress() @@ -620,8 +620,8 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { require.NoError(t, err) event, err := crosschainkeeper.ParseZetaSentEvent( - *sample.GetValidZetaSentDestinationExternal(t).Logs[4], - sample.GetValidZetaSentDestinationExternal(t).Logs[4].Address, + *sample.ValidZetaSentDestinationExternalReceipt(t).Logs[4], + sample.ValidZetaSentDestinationExternalReceipt(t).Logs[4].Address, ) require.NoError(t, err) emittingContract := sample.EthAddress() @@ -651,8 +651,8 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { ) require.NoError(t, err) event, err := crosschainkeeper.ParseZetaSentEvent( - *sample.GetValidZetaSentDestinationExternal(t).Logs[4], - sample.GetValidZetaSentDestinationExternal(t).Logs[4].Address, + *sample.ValidZetaSentDestinationExternalReceipt(t).Logs[4], + sample.ValidZetaSentDestinationExternalReceipt(t).Logs[4].Address, ) require.NoError(t, err) emittingContract := sample.EthAddress() @@ -689,8 +689,8 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { require.NoError(t, err) event, err := crosschainkeeper.ParseZetaSentEvent( - *sample.GetValidZetaSentDestinationExternal(t).Logs[4], - sample.GetValidZetaSentDestinationExternal(t).Logs[4].Address, + *sample.ValidZetaSentDestinationExternalReceipt(t).Logs[4], + sample.ValidZetaSentDestinationExternalReceipt(t).Logs[4].Address, ) require.NoError(t, err) emittingContract := sample.EthAddress() @@ -712,7 +712,7 @@ func TestKeeper_ProcessLogs(t *testing.T) { setSupportedChain(ctx, zk, []int64{chainID, senderChain.ChainId}...) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - block := sample.GetValidZRC20WithdrawToBTC(t) + block := sample.ValidZRC20WithdrawToBTCReceipt(t) gasZRC20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "bitcoin", "BTC") for _, log := range block.Logs { log.Address = gasZRC20 @@ -749,7 +749,7 @@ func TestKeeper_ProcessLogs(t *testing.T) { sdk.NewCoins(sdk.NewCoin(config.BaseDenom, amount)), ) require.NoError(t, err) - block := sample.GetValidZetaSentDestinationExternal(t) + block := sample.ValidZetaSentDestinationExternalReceipt(t) system, found := zk.FungibleKeeper.GetSystemContract(ctx) require.True(t, found) for _, log := range block.Logs { @@ -779,7 +779,7 @@ func TestKeeper_ProcessLogs(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) - err := k.ProcessLogs(ctx, sample.GetValidZRC20WithdrawToBTC(t).Logs, sample.EthAddress(), "") + err := k.ProcessLogs(ctx, sample.ValidZRC20WithdrawToBTCReceipt(t).Logs, sample.EthAddress(), "") require.ErrorContains(t, err, "cannot find system contract") cctxList := k.GetAllCrossChainTx(ctx) require.Len(t, cctxList, 0) @@ -794,7 +794,7 @@ func TestKeeper_ProcessLogs(t *testing.T) { setSupportedChain(ctx, zk, chainID) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - block := sample.GetValidZRC20WithdrawToBTC(t) + block := sample.ValidZRC20WithdrawToBTCReceipt(t) gasZRC20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "bitcoin", "BTC") for _, log := range block.Logs { log.Address = gasZRC20 @@ -817,7 +817,7 @@ func TestKeeper_ProcessLogs(t *testing.T) { setSupportedChain(ctx, zk, chainID) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - block := sample.GetValidZRC20WithdrawToBTC(t) + block := sample.ValidZRC20WithdrawToBTCReceipt(t) setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "bitcoin", "BTC") for _, log := range block.Logs { log.Address = sample.EthAddress() @@ -840,7 +840,7 @@ func TestKeeper_ProcessLogs(t *testing.T) { setSupportedChain(ctx, zk, []int64{chainID, senderChain.ChainId}...) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - block := sample.GetValidZRC20WithdrawToBTC(t) + block := sample.ValidZRC20WithdrawToBTCReceipt(t) gasZRC20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "bitcoin", "BTC") for _, log := range block.Logs { log.Address = gasZRC20 @@ -865,7 +865,7 @@ func TestKeeper_ProcessLogs(t *testing.T) { setSupportedChain(ctx, zk, chainID) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - block := sample.GetInvalidZRC20WithdrawToExternal(t) + block := sample.InvalidZRC20WithdrawToExternalReceipt(t) gasZRC20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "bitcoin", "BTC") for _, log := range block.Logs { log.Address = gasZRC20 @@ -886,7 +886,7 @@ func TestKeeper_ProcessLogs(t *testing.T) { setSupportedChain(ctx, zk, chainID) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - block := sample.GetValidZRC20WithdrawToBTC(t) + block := sample.ValidZRC20WithdrawToBTCReceipt(t) gasZRC20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "bitcoin", "BTC") for _, log := range block.Logs { log.Address = gasZRC20 diff --git a/x/crosschain/keeper/initiate_outbound_test.go b/x/crosschain/keeper/initiate_outbound_test.go index 55ca3e36f8..204179bea5 100644 --- a/x/crosschain/keeper/initiate_outbound_test.go +++ b/x/crosschain/keeper/initiate_outbound_test.go @@ -76,7 +76,7 @@ func TestKeeper_InitiateOutboundZEVMDeposit(t *testing.T) { require.ErrorContains(t, err, "deposit error") require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) require.Equal(t, types.CctxStatus_Aborted, newStatus) - require.Equal(t, "deposit error", cctx.CctxStatus.StatusMessage) + require.Equal(t, "deposit error", cctx.CctxStatus.ErrorMessage) }) t.Run( @@ -111,7 +111,7 @@ func TestKeeper_InitiateOutboundZEVMDeposit(t *testing.T) { require.Equal(t, types.CctxStatus_Aborted, newStatus) require.Contains( t, - cctx.CctxStatus.StatusMessage, + cctx.CctxStatus.ErrorMessage, "chain not supported", ) }, @@ -151,7 +151,7 @@ func TestKeeper_InitiateOutboundZEVMDeposit(t *testing.T) { require.Equal(t, types.CctxStatus_Aborted, newStatus) require.Contains( t, - cctx.CctxStatus.StatusMessage, + cctx.CctxStatus.ErrorMessage, "GetRevertGasLimit: foreign coin not found for sender chain", ) }) @@ -194,7 +194,7 @@ func TestKeeper_InitiateOutboundZEVMDeposit(t *testing.T) { require.Equal(t, types.CctxStatus_Aborted, newStatus) require.Contains( t, - cctx.CctxStatus.StatusMessage, + cctx.CctxStatus.ErrorMessage, "chain not supported", ) }, @@ -239,7 +239,7 @@ func TestKeeper_InitiateOutboundZEVMDeposit(t *testing.T) { require.Equal(t, types.CctxStatus_Aborted, newStatus) require.Contains( t, - cctx.CctxStatus.StatusMessage, + cctx.CctxStatus.ErrorMessage, "chain not supported", ) }, @@ -284,7 +284,7 @@ func TestKeeper_InitiateOutboundZEVMDeposit(t *testing.T) { require.NoError(t, err) require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) require.Equal(t, types.CctxStatus_Aborted, newStatus) - require.Contains(t, cctx.CctxStatus.StatusMessage, "cannot find receiver chain nonce") + require.Contains(t, cctx.CctxStatus.ErrorMessage, "cannot find receiver chain nonce") }) t.Run("unable to process zevm deposit HandleEVMDeposit revert successfully", func(t *testing.T) { @@ -321,7 +321,7 @@ func TestKeeper_InitiateOutboundZEVMDeposit(t *testing.T) { require.NoError(t, err) require.Equal(t, types.CctxStatus_PendingRevert, cctx.CctxStatus.Status) require.Equal(t, types.CctxStatus_PendingRevert, newStatus) - require.Equal(t, errDeposit.Error(), cctx.CctxStatus.StatusMessage) + require.Equal(t, errDeposit.Error(), cctx.CctxStatus.ErrorMessage) require.Equal(t, updatedNonce, cctx.GetCurrentOutboundParam().TssNonce) }) @@ -361,7 +361,7 @@ func TestKeeper_InitiateOutboundZEVMDeposit(t *testing.T) { require.Equal(t, types.CctxStatus_Aborted, newStatus) require.Contains( t, - cctx.CctxStatus.StatusMessage, + cctx.CctxStatus.ErrorMessage, "cannot revert a revert tx", ) }, @@ -427,7 +427,7 @@ func TestKeeper_InitiateOutboundProcessCrosschainMsgPassing(t *testing.T) { require.ErrorIs(t, err, observertypes.ErrSupportedChains) require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) require.Equal(t, types.CctxStatus_Aborted, newStatus) - require.Equal(t, observertypes.ErrSupportedChains.Error(), cctx.CctxStatus.StatusMessage) + require.Equal(t, observertypes.ErrSupportedChains.Error(), cctx.CctxStatus.ErrorMessage) }) t.Run("unable to process crosschain msg passing UpdateNonce fails", func(t *testing.T) { @@ -456,7 +456,7 @@ func TestKeeper_InitiateOutboundProcessCrosschainMsgPassing(t *testing.T) { require.ErrorContains(t, err, "cannot find receiver chain nonce") require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) require.Equal(t, types.CctxStatus_Aborted, newStatus) - require.Contains(t, cctx.CctxStatus.StatusMessage, "cannot find receiver chain nonce") + require.Contains(t, cctx.CctxStatus.ErrorMessage, "cannot find receiver chain nonce") }) } diff --git a/x/crosschain/keeper/msg_server_migrate_tss_funds.go b/x/crosschain/keeper/msg_server_migrate_tss_funds.go index c285033f02..8ecc4c47ff 100644 --- a/x/crosschain/keeper/msg_server_migrate_tss_funds.go +++ b/x/crosschain/keeper/msg_server_migrate_tss_funds.go @@ -9,7 +9,6 @@ import ( tmbytes "github.com/cometbft/cometbft/libs/bytes" tmtypes "github.com/cometbft/cometbft/types" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/errors" authoritytypes "github.com/zeta-chain/node/x/authority/types" "github.com/zeta-chain/node/x/crosschain/types" @@ -26,7 +25,7 @@ func (k msgServer) MigrateTssFunds( // check if authorized err := k.GetAuthorityKeeper().CheckAuthorization(ctx, msg) if err != nil { - return nil, errors.Wrap(authoritytypes.ErrUnauthorized, err.Error()) + return nil, errorsmod.Wrap(authoritytypes.ErrUnauthorized, err.Error()) } if k.zetaObserverKeeper.IsInboundEnabled(ctx) { diff --git a/x/crosschain/keeper/msg_server_vote_inbound_tx_test.go b/x/crosschain/keeper/msg_server_vote_inbound_tx_test.go index e824a01707..5aa70be3bd 100644 --- a/x/crosschain/keeper/msg_server_vote_inbound_tx_test.go +++ b/x/crosschain/keeper/msg_server_vote_inbound_tx_test.go @@ -265,7 +265,7 @@ func TestKeeper_VoteInbound(t *testing.T) { }) } -func TestStatus_ChangeStatus(t *testing.T) { +func TestStatus_UpdateCctxStatus(t *testing.T) { tt := []struct { Name string Status types.Status @@ -302,7 +302,7 @@ func TestStatus_ChangeStatus(t *testing.T) { for _, test := range tt { test := test t.Run(test.Name, func(t *testing.T) { - test.Status.ChangeStatus(test.NonErrStatus, test.Msg) + test.Status.UpdateStatusAndErrorMessages(test.NonErrStatus, test.Msg, "") if test.IsErr { require.Equal(t, test.ErrStatus, test.Status.Status) } else { diff --git a/x/crosschain/keeper/msg_server_vote_outbound_tx.go b/x/crosschain/keeper/msg_server_vote_outbound_tx.go index 6682c2c705..4bb65a3c8f 100644 --- a/x/crosschain/keeper/msg_server_vote_outbound_tx.go +++ b/x/crosschain/keeper/msg_server_vote_outbound_tx.go @@ -185,7 +185,7 @@ SaveFailedOutbound saves a failed outbound transaction.It does the following thi */ func (k Keeper) SaveFailedOutbound(ctx sdk.Context, cctx *types.CrossChainTx, errMessage string, tssPubkey string) { - cctx.SetAbort(errMessage) + cctx.SetAbort("", errMessage) ctx.Logger().Error(errMessage) k.SaveOutbound(ctx, cctx, tssPubkey) } diff --git a/x/crosschain/keeper/v2_zevm_inbound.go b/x/crosschain/keeper/v2_zevm_inbound.go index 9efc5e1e2e..8b4d4ed742 100644 --- a/x/crosschain/keeper/v2_zevm_inbound.go +++ b/x/crosschain/keeper/v2_zevm_inbound.go @@ -1,23 +1,16 @@ package keeper import ( - "encoding/hex" "fmt" "math/big" errorsmod "cosmossdk.io/errors" - "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/ethereum/go-ethereum/accounts/abi/bind" ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" - "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" - "github.com/zeta-chain/node/pkg/chains" - "github.com/zeta-chain/node/pkg/coin" "github.com/zeta-chain/node/x/crosschain/types" - fungibletypes "github.com/zeta-chain/node/x/fungible/types" observertypes "github.com/zeta-chain/node/x/observer/types" ) @@ -30,8 +23,8 @@ func (k Keeper) ProcessZEVMInboundV2( txOrigin string, ) error { // try to parse a withdrawal event from the log - withdrawalEvent, gatewayEvent, err := k.parseGatewayEvent(*log, gatewayAddr) - if err == nil && (withdrawalEvent != nil || gatewayEvent != nil) { + withdrawalEvent, callEvent, withdrawalAndCallEvent, err := types.ParseGatewayEvent(*log, gatewayAddr) + if err == nil && (withdrawalEvent != nil || callEvent != nil || withdrawalAndCallEvent != nil) { var inbound *types.MsgVoteInbound // parse data from event and validate @@ -44,21 +37,42 @@ func (k Keeper) ProcessZEVMInboundV2( value = withdrawalEvent.Value receiver = withdrawalEvent.Receiver contractAddress = withdrawalEvent.Raw.Address - } else { - zrc20 = gatewayEvent.Zrc20 + } else if callEvent != nil { + zrc20 = callEvent.Zrc20 value = big.NewInt(0) - receiver = gatewayEvent.Receiver - contractAddress = gatewayEvent.Raw.Address + receiver = callEvent.Receiver + contractAddress = callEvent.Raw.Address + } else { + zrc20 = withdrawalAndCallEvent.Zrc20 + value = withdrawalAndCallEvent.Value + receiver = withdrawalAndCallEvent.Receiver + contractAddress = withdrawalAndCallEvent.Raw.Address } k.Logger(ctx).Error(fmt.Sprintf("processing inbound. zrc20: %s", zrc20.Hex())) + // get several information necessary for processing the inbound foreignCoin, found := k.fungibleKeeper.GetForeignCoins(ctx, zrc20.Hex()) if !found { ctx.Logger(). Info(fmt.Sprintf("cannot find foreign coin with contract address %s", contractAddress.Hex())) return nil } + receiverChain, found := k.zetaObserverKeeper.GetSupportedChainFromChainID(ctx, foreignCoin.ForeignChainId) + if !found { + return errorsmod.Wrapf( + observertypes.ErrSupportedChains, + "chain with chainID %d not supported", + foreignCoin.ForeignChainId, + ) + } + gasLimitQueried, err := k.fungibleKeeper.QueryGasLimit( + ctx, + ethcommon.HexToAddress(foreignCoin.Zrc20ContractAddress), + ) + if err != nil { + return err + } // validate data of the withdrawal event if err := k.validateZRC20Withdrawal(ctx, foreignCoin.ForeignChainId, value, receiver); err != nil { @@ -67,12 +81,39 @@ func (k Keeper) ProcessZEVMInboundV2( // create inbound object depending on the event type if withdrawalEvent != nil { - inbound, err = k.newWithdrawalInbound(ctx, txOrigin, foreignCoin, withdrawalEvent) + inbound, err = types.NewWithdrawalInbound( + ctx, + txOrigin, + foreignCoin.CoinType, + foreignCoin.Asset, + withdrawalEvent, + receiverChain, + gasLimitQueried, + ) + if err != nil { + return err + } + } else if callEvent != nil { + inbound, err = types.NewCallInbound( + ctx, + txOrigin, + callEvent, + receiverChain, + gasLimitQueried, + ) if err != nil { return err } } else { - inbound, err = k.newCallInbound(ctx, txOrigin, foreignCoin, gatewayEvent) + inbound, err = types.NewWithdrawAndCallInbound( + ctx, + txOrigin, + foreignCoin.CoinType, + foreignCoin.Asset, + withdrawalAndCallEvent, + receiverChain, + gasLimitQueried, + ) if err != nil { return err } @@ -96,182 +137,3 @@ func (k Keeper) ProcessZEVMInboundV2( return nil } - -// parseGatewayEvent parses the event from the gateway contract -func (k Keeper) parseGatewayEvent( - log ethtypes.Log, - gatewayAddr ethcommon.Address, -) (*gatewayzevm.GatewayZEVMWithdrawn, *gatewayzevm.GatewayZEVMCalled, error) { - if len(log.Topics) == 0 { - return nil, nil, errors.New("ParseGatewayCallEvent: invalid log - no topics") - } - filterer, err := gatewayzevm.NewGatewayZEVMFilterer(log.Address, bind.ContractFilterer(nil)) - if err != nil { - return nil, nil, err - } - withdrawalEvent, err := k.parseGatewayWithdrawalEvent(log, gatewayAddr, filterer) - if err == nil { - return withdrawalEvent, nil, nil - } - callEvent, err := k.parseGatewayCallEvent(log, gatewayAddr, filterer) - if err == nil { - return nil, callEvent, nil - } - return nil, nil, errors.New("ParseGatewayEvent: invalid log - no event found") -} - -// parseGatewayWithdrawalEvent parses the GatewayZEVMWithdrawal event from the log -func (k Keeper) parseGatewayWithdrawalEvent( - log ethtypes.Log, - gatewayAddr ethcommon.Address, - filterer *gatewayzevm.GatewayZEVMFilterer, -) (*gatewayzevm.GatewayZEVMWithdrawn, error) { - event, err := filterer.ParseWithdrawn(log) - if err != nil { - return nil, err - } - if event.Raw.Address != gatewayAddr { - return nil, errors.New("ParseGatewayWithdrawalEvent: invalid log - wrong contract address") - } - return event, nil -} - -// parseGatewayCallEvent parses the GatewayZEVMCall event from the log -func (k Keeper) parseGatewayCallEvent( - log ethtypes.Log, - gatewayAddr ethcommon.Address, - filterer *gatewayzevm.GatewayZEVMFilterer, -) (*gatewayzevm.GatewayZEVMCalled, error) { - event, err := filterer.ParseCalled(log) - if err != nil { - return nil, err - } - if event.Raw.Address != gatewayAddr { - return nil, errors.New("ParseGatewayCallEvent: invalid log - wrong contract address") - } - return event, nil -} - -// newWithdrawalInbound creates a new inbound object for a withdrawal -// currently inbound data is represented with a MsgVoteInbound message -// TODO: replace with a more appropriate object -// https://github.com/zeta-chain/node/issues/2658 -func (k Keeper) newWithdrawalInbound( - ctx sdk.Context, - txOrigin string, - foreignCoin fungibletypes.ForeignCoins, - event *gatewayzevm.GatewayZEVMWithdrawn, -) (*types.MsgVoteInbound, error) { - receiverChain, found := k.zetaObserverKeeper.GetSupportedChainFromChainID(ctx, foreignCoin.ForeignChainId) - if !found { - return nil, errorsmod.Wrapf( - observertypes.ErrSupportedChains, - "chain with chainID %d not supported", - foreignCoin.ForeignChainId, - ) - } - - senderChain, err := chains.ZetaChainFromCosmosChainID(ctx.ChainID()) - if err != nil { - return nil, errors.Wrapf(err, "ProcessZEVMInboundV2: failed to convert chainID %s", ctx.ChainID()) - } - - toAddr, err := receiverChain.EncodeAddress(event.Receiver) - if err != nil { - return nil, errors.Wrapf(err, "cannot encode address %v", event.Receiver) - } - - gasLimit := event.CallOptions.GasLimit.Uint64() - if gasLimit == 0 { - gasLimitQueried, err := k.fungibleKeeper.QueryGasLimit( - ctx, - ethcommon.HexToAddress(foreignCoin.Zrc20ContractAddress), - ) - if err != nil { - return nil, errors.Wrap(err, "cannot query gas limit") - } - gasLimit = gasLimitQueried.Uint64() - } - - return types.NewMsgVoteInbound( - "", - event.Sender.Hex(), - senderChain.ChainId, - txOrigin, - toAddr, - foreignCoin.ForeignChainId, - math.NewUintFromBigInt(event.Value), - hex.EncodeToString(event.Message), - event.Raw.TxHash.String(), - event.Raw.BlockNumber, - gasLimit, - foreignCoin.CoinType, - foreignCoin.Asset, - event.Raw.Index, - types.ProtocolContractVersion_V2, - event.CallOptions.IsArbitraryCall, - types.WithZEVMRevertOptions(event.RevertOptions), - ), nil -} - -// newCallInbound creates a new inbound object for a call -// currently inbound data is represented with a MsgVoteInbound message -// TODO: replace with a more appropriate object -// https://github.com/zeta-chain/node/issues/2658 -func (k Keeper) newCallInbound( - ctx sdk.Context, - txOrigin string, - foreignCoin fungibletypes.ForeignCoins, - event *gatewayzevm.GatewayZEVMCalled, -) (*types.MsgVoteInbound, error) { - receiverChain, found := k.zetaObserverKeeper.GetSupportedChainFromChainID(ctx, foreignCoin.ForeignChainId) - if !found { - return nil, errorsmod.Wrapf( - observertypes.ErrSupportedChains, - "chain with chainID %d not supported", - foreignCoin.ForeignChainId, - ) - } - - senderChain, err := chains.ZetaChainFromCosmosChainID(ctx.ChainID()) - if err != nil { - return nil, errors.Wrapf(err, "ProcessZEVMInboundV2: failed to convert chainID %s", ctx.ChainID()) - } - - toAddr, err := receiverChain.EncodeAddress(event.Receiver) - if err != nil { - return nil, errors.Wrapf(err, "cannot encode address %v", event.Receiver) - } - - gasLimit := event.CallOptions.GasLimit.Uint64() - if gasLimit == 0 { - gasLimitQueried, err := k.fungibleKeeper.QueryGasLimit( - ctx, - ethcommon.HexToAddress(foreignCoin.Zrc20ContractAddress), - ) - if err != nil { - return nil, errors.Wrap(err, "cannot query gas limit") - } - gasLimit = gasLimitQueried.Uint64() - } - - return types.NewMsgVoteInbound( - "", - event.Sender.Hex(), - senderChain.ChainId, - txOrigin, - toAddr, - foreignCoin.ForeignChainId, - math.ZeroUint(), - hex.EncodeToString(event.Message), - event.Raw.TxHash.String(), - event.Raw.BlockNumber, - gasLimit, - coin.CoinType_NoAssetCall, - "", - event.Raw.Index, - types.ProtocolContractVersion_V2, - event.CallOptions.IsArbitraryCall, - types.WithZEVMRevertOptions(event.RevertOptions), - ), nil -} diff --git a/x/crosschain/migrations/v5/migrate.go b/x/crosschain/migrations/v5/migrate.go index aeeb22451f..0f21d8646b 100644 --- a/x/crosschain/migrations/v5/migrate.go +++ b/x/crosschain/migrations/v5/migrate.go @@ -137,6 +137,7 @@ func SetZetaAccounting( return nil } + func GetAbortedAmount(cctx types.CrossChainTx) sdkmath.Uint { if cctx.OutboundParams != nil && !cctx.GetCurrentOutboundParam().Amount.IsZero() { return cctx.GetCurrentOutboundParam().Amount diff --git a/x/crosschain/types/cctx.go b/x/crosschain/types/cctx.go index 0a235dffae..2b84b5f26c 100644 --- a/x/crosschain/types/cctx.go +++ b/x/crosschain/types/cctx.go @@ -171,28 +171,28 @@ func (m *CrossChainTx) AddOutbound( } // SetAbort sets the CCTX status to Aborted with the given error message. -func (m CrossChainTx) SetAbort(message string) { - m.CctxStatus.ChangeStatus(CctxStatus_Aborted, message) +func (m CrossChainTx) SetAbort(statusMsg, errorMsg string) { + m.CctxStatus.UpdateStatusAndErrorMessages(CctxStatus_Aborted, statusMsg, errorMsg) } // SetPendingRevert sets the CCTX status to PendingRevert with the given error message. -func (m CrossChainTx) SetPendingRevert(message string) { - m.CctxStatus.ChangeStatus(CctxStatus_PendingRevert, message) +func (m CrossChainTx) SetPendingRevert(statusMsg, errorMsg string) { + m.CctxStatus.UpdateStatusAndErrorMessages(CctxStatus_PendingRevert, statusMsg, errorMsg) } // SetPendingOutbound sets the CCTX status to PendingOutbound with the given error message. -func (m CrossChainTx) SetPendingOutbound(message string) { - m.CctxStatus.ChangeStatus(CctxStatus_PendingOutbound, message) +func (m CrossChainTx) SetPendingOutbound(statusMsg string) { + m.CctxStatus.UpdateStatusAndErrorMessages(CctxStatus_PendingOutbound, statusMsg, "") } // SetOutboundMined sets the CCTX status to OutboundMined with the given error message. -func (m CrossChainTx) SetOutboundMined(message string) { - m.CctxStatus.ChangeStatus(CctxStatus_OutboundMined, message) +func (m CrossChainTx) SetOutboundMined(statusMsg string) { + m.CctxStatus.UpdateStatusAndErrorMessages(CctxStatus_OutboundMined, statusMsg, "") } // SetReverted sets the CCTX status to Reverted with the given error message. -func (m CrossChainTx) SetReverted(message string) { - m.CctxStatus.ChangeStatus(CctxStatus_Reverted, message) +func (m CrossChainTx) SetReverted(statusMsg, errorMsg string) { + m.CctxStatus.UpdateStatusAndErrorMessages(CctxStatus_Reverted, statusMsg, errorMsg) } func (m CrossChainTx) GetCCTXIndexBytes() ([32]byte, error) { @@ -237,6 +237,7 @@ func NewCCTX(ctx sdk.Context, msg MsgVoteInbound, tssPubkey string) (CrossChainT FinalizedZetaHeight: 0, BallotIndex: index, CoinType: msg.CoinType, + IsCrossChainCall: msg.IsCrossChainCall, } outboundParams := &OutboundParams{ @@ -259,6 +260,7 @@ func NewCCTX(ctx sdk.Context, msg MsgVoteInbound, tssPubkey string) (CrossChainT status := &Status{ Status: CctxStatus_PendingInbound, StatusMessage: "", + ErrorMessage: "", CreatedTimestamp: ctx.BlockHeader().Time.Unix(), LastUpdateTimestamp: ctx.BlockHeader().Time.Unix(), IsAbortRefunded: false, diff --git a/x/crosschain/types/cctx_test.go b/x/crosschain/types/cctx_test.go index 00fbe2ac66..f49768f8a0 100644 --- a/x/crosschain/types/cctx_test.go +++ b/x/crosschain/types/cctx_test.go @@ -150,17 +150,20 @@ func Test_SetRevertOutboundValues(t *testing.T) { func TestCrossChainTx_SetAbort(t *testing.T) { cctx := sample.CrossChainTx(t, "test") - cctx.SetAbort("test") + cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound + cctx.SetAbort("test", "test") require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) - require.Equal(t, "test", "test") + require.Contains(t, cctx.CctxStatus.StatusMessage, "test") + require.Contains(t, cctx.CctxStatus.ErrorMessage, "test") } func TestCrossChainTx_SetPendingRevert(t *testing.T) { cctx := sample.CrossChainTx(t, "test") cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound - cctx.SetPendingRevert("test") + cctx.SetPendingRevert("test", "test") require.Equal(t, types.CctxStatus_PendingRevert, cctx.CctxStatus.Status) require.Contains(t, cctx.CctxStatus.StatusMessage, "test") + require.Contains(t, cctx.CctxStatus.ErrorMessage, "test") } func TestCrossChainTx_SetPendingOutbound(t *testing.T) { @@ -169,6 +172,7 @@ func TestCrossChainTx_SetPendingOutbound(t *testing.T) { cctx.SetPendingOutbound("test") require.Equal(t, types.CctxStatus_PendingOutbound, cctx.CctxStatus.Status) require.Contains(t, cctx.CctxStatus.StatusMessage, "test") + require.NotContains(t, cctx.CctxStatus.ErrorMessage, "test") } func TestCrossChainTx_SetOutboundMined(t *testing.T) { @@ -177,12 +181,14 @@ func TestCrossChainTx_SetOutboundMined(t *testing.T) { cctx.SetOutboundMined("test") require.Equal(t, types.CctxStatus_OutboundMined, cctx.CctxStatus.Status) require.Contains(t, cctx.CctxStatus.StatusMessage, "test") + require.NotContains(t, cctx.CctxStatus.ErrorMessage, "test") } func TestCrossChainTx_SetReverted(t *testing.T) { cctx := sample.CrossChainTx(t, "test") cctx.CctxStatus.Status = types.CctxStatus_PendingRevert - cctx.SetReverted("test") + cctx.SetReverted("test", "test") require.Equal(t, types.CctxStatus_Reverted, cctx.CctxStatus.Status) require.Contains(t, cctx.CctxStatus.StatusMessage, "test") + require.Contains(t, cctx.CctxStatus.ErrorMessage, "test") } diff --git a/x/crosschain/types/codec.go b/x/crosschain/types/codec.go index f45f745f5f..b93795aa3a 100644 --- a/x/crosschain/types/codec.go +++ b/x/crosschain/types/codec.go @@ -19,6 +19,14 @@ func RegisterCodec(cdc *codec.LegacyAmino) { cdc.RegisterConcrete(&MsgUpdateTssAddress{}, "crosschain/UpdateTssAddress", nil) cdc.RegisterConcrete(&MsgAbortStuckCCTX{}, "crosschain/AbortStuckCCTX", nil) cdc.RegisterConcrete(&MsgUpdateRateLimiterFlags{}, "crosschain/UpdateRateLimiterFlags", nil) + + // legacy messages defined for backward compatibility + cdc.RegisterConcrete(&MsgAddToInTxTracker{}, "crosschain/AddToInTxTracker", nil) + cdc.RegisterConcrete(&MsgAddToOutTxTracker{}, "crosschain/AddToOutTxTracker", nil) + cdc.RegisterConcrete(&MsgRemoveFromOutTxTracker{}, "crosschain/RemoveFromOutTxTracker", nil) + cdc.RegisterConcrete(&MsgVoteOnObservedOutboundTx{}, "crosschain/VoteOnObservedOutboundTx", nil) + cdc.RegisterConcrete(&MsgVoteOnObservedInboundTx{}, "crosschain/VoteOnObservedInboundTx", nil) + cdc.RegisterConcrete(&MsgGasPriceVoter{}, "crosschain/GasPriceVoter", nil) } func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { @@ -34,6 +42,14 @@ func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { &MsgUpdateTssAddress{}, &MsgAbortStuckCCTX{}, &MsgUpdateRateLimiterFlags{}, + + // legacy messages defined for backward compatibility + &MsgAddToInTxTracker{}, + &MsgAddToOutTxTracker{}, + &MsgRemoveFromOutTxTracker{}, + &MsgVoteOnObservedOutboundTx{}, + &MsgVoteOnObservedInboundTx{}, + &MsgGasPriceVoter{}, ) msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) diff --git a/x/crosschain/types/cross_chain_tx.pb.go b/x/crosschain/types/cross_chain_tx.pb.go index 842ba310ba..11ebc77bb3 100644 --- a/x/crosschain/types/cross_chain_tx.pb.go +++ b/x/crosschain/types/cross_chain_tx.pb.go @@ -130,6 +130,9 @@ type InboundParams struct { BallotIndex string `protobuf:"bytes,9,opt,name=ballot_index,json=ballotIndex,proto3" json:"ballot_index,omitempty"` FinalizedZetaHeight uint64 `protobuf:"varint,10,opt,name=finalized_zeta_height,json=finalizedZetaHeight,proto3" json:"finalized_zeta_height,omitempty"` TxFinalizationStatus TxFinalizationStatus `protobuf:"varint,11,opt,name=tx_finalization_status,json=txFinalizationStatus,proto3,enum=zetachain.zetacore.crosschain.TxFinalizationStatus" json:"tx_finalization_status,omitempty"` + // this field describes if a smart contract call should be made for a inbound + // with assets only used for protocol contract version 2 + IsCrossChainCall bool `protobuf:"varint,12,opt,name=is_cross_chain_call,json=isCrossChainCall,proto3" json:"is_cross_chain_call,omitempty"` } func (m *InboundParams) Reset() { *m = InboundParams{} } @@ -235,6 +238,13 @@ func (m *InboundParams) GetTxFinalizationStatus() TxFinalizationStatus { return TxFinalizationStatus_NotFinalized } +func (m *InboundParams) GetIsCrossChainCall() bool { + if m != nil { + return m.IsCrossChainCall + } + return false +} + type ZetaAccounting struct { // aborted_zeta_amount stores the total aborted amount for cctx of coin-type // ZETA @@ -488,10 +498,15 @@ func (m *OutboundParams) GetCallOptions() *CallOptions { } type Status struct { - Status CctxStatus `protobuf:"varint,1,opt,name=status,proto3,enum=zetachain.zetacore.crosschain.CctxStatus" json:"status,omitempty"` - StatusMessage string `protobuf:"bytes,2,opt,name=status_message,json=statusMessage,proto3" json:"status_message,omitempty"` - LastUpdateTimestamp int64 `protobuf:"varint,3,opt,name=lastUpdate_timestamp,json=lastUpdateTimestamp,proto3" json:"lastUpdate_timestamp,omitempty"` - IsAbortRefunded bool `protobuf:"varint,4,opt,name=isAbortRefunded,proto3" json:"isAbortRefunded,omitempty"` + Status CctxStatus `protobuf:"varint,1,opt,name=status,proto3,enum=zetachain.zetacore.crosschain.CctxStatus" json:"status,omitempty"` + // status_message carries information about the status transitions: + // why they were triggered, old and new status. + StatusMessage string `protobuf:"bytes,2,opt,name=status_message,json=statusMessage,proto3" json:"status_message,omitempty"` + // error_message carries information about the error that caused the tx + // to be PendingRevert, Reverted or Aborted. + ErrorMessage string `protobuf:"bytes,6,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"` + LastUpdateTimestamp int64 `protobuf:"varint,3,opt,name=lastUpdate_timestamp,json=lastUpdateTimestamp,proto3" json:"lastUpdate_timestamp,omitempty"` + IsAbortRefunded bool `protobuf:"varint,4,opt,name=isAbortRefunded,proto3" json:"isAbortRefunded,omitempty"` // when the CCTX was created. only populated on new transactions. CreatedTimestamp int64 `protobuf:"varint,5,opt,name=created_timestamp,json=createdTimestamp,proto3" json:"created_timestamp,omitempty"` } @@ -543,6 +558,13 @@ func (m *Status) GetStatusMessage() string { return "" } +func (m *Status) GetErrorMessage() string { + if m != nil { + return m.ErrorMessage + } + return "" +} + func (m *Status) GetLastUpdateTimestamp() int64 { if m != nil { return m.LastUpdateTimestamp @@ -753,92 +775,94 @@ func init() { } var fileDescriptor_d4c1966807fb5cb2 = []byte{ - // 1348 bytes of a gzipped FileDescriptorProto + // 1378 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x57, 0x4d, 0x6f, 0x1b, 0x37, - 0x13, 0xf6, 0xda, 0xb2, 0x2c, 0x8d, 0x3e, 0xbc, 0xa6, 0x15, 0x67, 0xe3, 0x17, 0x51, 0xfc, 0xaa, + 0x13, 0xf6, 0xda, 0xb2, 0x2c, 0x8d, 0x3e, 0xbc, 0xa6, 0x15, 0x67, 0xe3, 0x17, 0x51, 0xf4, 0xaa, 0x75, 0xa2, 0xb8, 0xb5, 0x84, 0x28, 0x40, 0x51, 0xf4, 0x66, 0x1b, 0x71, 0xe2, 0xb6, 0x89, 0x8d, - 0x8d, 0x63, 0x20, 0x39, 0x74, 0x4b, 0xed, 0xd2, 0x12, 0xe1, 0xd5, 0x52, 0x5d, 0x52, 0x86, 0x14, - 0xf4, 0xd6, 0x73, 0x81, 0xfe, 0x88, 0x1e, 0x7a, 0xec, 0xcf, 0xc8, 0x31, 0xc7, 0xa2, 0x87, 0x20, - 0x48, 0xfe, 0x41, 0xcf, 0x3d, 0x14, 0xfc, 0x92, 0xac, 0xc0, 0xb5, 0xd3, 0xb4, 0x27, 0x71, 0x66, - 0x38, 0xcf, 0xcc, 0x0e, 0xe7, 0x19, 0x52, 0xd0, 0x7a, 0x4e, 0x04, 0x0e, 0xbb, 0x98, 0x26, 0x4d, - 0xb5, 0x62, 0x29, 0x69, 0x86, 0x29, 0xe3, 0x5c, 0xeb, 0xd4, 0x32, 0x50, 0xeb, 0x40, 0x0c, 0x1b, - 0xfd, 0x94, 0x09, 0x86, 0xae, 0x8f, 0x7d, 0x1a, 0xd6, 0xa7, 0x31, 0xf1, 0x59, 0xad, 0x74, 0x58, - 0x87, 0xa9, 0x9d, 0x4d, 0xb9, 0xd2, 0x4e, 0xab, 0x37, 0xcf, 0x09, 0xd4, 0x3f, 0xe9, 0x34, 0x43, - 0x26, 0xc3, 0x30, 0x9a, 0xe8, 0x7d, 0xb5, 0x5f, 0x33, 0x50, 0xda, 0x4b, 0xda, 0x6c, 0x90, 0x44, - 0x07, 0x38, 0xc5, 0x3d, 0x8e, 0x56, 0x20, 0xcb, 0x49, 0x12, 0x91, 0xd4, 0x73, 0xd6, 0x9c, 0x7a, - 0xde, 0x37, 0x12, 0xba, 0x09, 0x8b, 0x7a, 0x65, 0xf2, 0xa3, 0x91, 0x37, 0xbb, 0xe6, 0xd4, 0xe7, - 0xfc, 0x92, 0x56, 0xef, 0x48, 0xed, 0x5e, 0x84, 0xfe, 0x07, 0x79, 0x31, 0x0c, 0x58, 0x4a, 0x3b, - 0x34, 0xf1, 0xe6, 0x14, 0x44, 0x4e, 0x0c, 0xf7, 0x95, 0x8c, 0xb6, 0x21, 0x2f, 0x83, 0x07, 0x62, - 0xd4, 0x27, 0x5e, 0x66, 0xcd, 0xa9, 0x97, 0x5b, 0xeb, 0x8d, 0x73, 0xbe, 0xaf, 0x7f, 0xd2, 0x69, - 0xa8, 0x2c, 0x77, 0x18, 0x4d, 0x0e, 0x47, 0x7d, 0xe2, 0xe7, 0x42, 0xb3, 0x42, 0x15, 0x98, 0xc7, - 0x9c, 0x13, 0xe1, 0xcd, 0x2b, 0x70, 0x2d, 0xa0, 0xfb, 0x90, 0xc5, 0x3d, 0x36, 0x48, 0x84, 0x97, - 0x95, 0xea, 0xed, 0xe6, 0x8b, 0x57, 0x37, 0x66, 0x7e, 0x7f, 0x75, 0xe3, 0x56, 0x87, 0x8a, 0xee, - 0xa0, 0xdd, 0x08, 0x59, 0xaf, 0x19, 0x32, 0xde, 0x63, 0xdc, 0xfc, 0x6c, 0xf2, 0xe8, 0xa4, 0x29, - 0xf3, 0xe0, 0x8d, 0x27, 0x34, 0x11, 0xbe, 0x71, 0x47, 0x1f, 0x41, 0x89, 0xb5, 0x39, 0x49, 0x4f, - 0x49, 0x14, 0x74, 0x31, 0xef, 0x7a, 0x0b, 0x2a, 0x4c, 0xd1, 0x2a, 0x1f, 0x60, 0xde, 0x45, 0x9f, - 0x83, 0x37, 0xde, 0x44, 0x86, 0x82, 0xa4, 0x09, 0x8e, 0x83, 0x2e, 0xa1, 0x9d, 0xae, 0xf0, 0x72, - 0x6b, 0x4e, 0x3d, 0xe3, 0xaf, 0x58, 0xfb, 0x3d, 0x63, 0x7e, 0xa0, 0xac, 0xe8, 0xff, 0x50, 0x6c, - 0xe3, 0x38, 0x66, 0x22, 0xa0, 0x49, 0x44, 0x86, 0x5e, 0x5e, 0xa1, 0x17, 0xb4, 0x6e, 0x4f, 0xaa, - 0x50, 0x0b, 0xae, 0x1c, 0xd3, 0x04, 0xc7, 0xf4, 0x39, 0x89, 0x02, 0x59, 0x12, 0x8b, 0x0c, 0x0a, - 0x79, 0x79, 0x6c, 0x7c, 0x46, 0x04, 0x36, 0xb0, 0x14, 0x56, 0xc4, 0x30, 0x30, 0x16, 0x2c, 0x28, - 0x4b, 0x02, 0x2e, 0xb0, 0x18, 0x70, 0xaf, 0xa0, 0xaa, 0x7c, 0xb7, 0x71, 0x61, 0x17, 0x35, 0x0e, - 0x87, 0xbb, 0x67, 0x7c, 0x1f, 0x2b, 0x57, 0xbf, 0x22, 0xce, 0xd1, 0xd6, 0xbe, 0x83, 0xb2, 0x0c, - 0xbc, 0x15, 0x86, 0xb2, 0x5e, 0x34, 0xe9, 0xa0, 0x00, 0x96, 0x71, 0x9b, 0xa5, 0xc2, 0xa6, 0x6b, - 0x0e, 0xc2, 0xf9, 0xb0, 0x83, 0x58, 0x32, 0x58, 0x2a, 0x88, 0x42, 0xaa, 0x1d, 0x41, 0x61, 0x07, - 0xc7, 0xf1, 0x7e, 0x5f, 0xa6, 0xc1, 0x65, 0x8b, 0x75, 0x30, 0x0f, 0x62, 0xda, 0xa3, 0x3a, 0x4a, - 0xc6, 0xcf, 0x75, 0x30, 0xff, 0x5a, 0xca, 0x68, 0x03, 0x96, 0x28, 0x0f, 0x70, 0xda, 0xa6, 0x22, - 0xc5, 0xe9, 0x28, 0x08, 0x71, 0x1c, 0xab, 0x4e, 0xcd, 0xf9, 0x8b, 0x94, 0x6f, 0x59, 0xbd, 0xc4, - 0xab, 0xbd, 0xce, 0x42, 0x79, 0x7f, 0x20, 0xce, 0xb6, 0xff, 0x2a, 0xe4, 0x52, 0x12, 0x12, 0x7a, - 0x3a, 0x26, 0xc0, 0x58, 0x46, 0xb7, 0xc1, 0xb5, 0x6b, 0x4d, 0x82, 0x3d, 0xcb, 0x81, 0x45, 0xab, - 0xb7, 0x2c, 0x98, 0x6a, 0xf4, 0xb9, 0x0f, 0x6b, 0xf4, 0x49, 0x4b, 0x67, 0xfe, 0x5d, 0x4b, 0x4b, - 0x4a, 0x72, 0x1e, 0x24, 0x2c, 0x09, 0x89, 0x62, 0x4d, 0xc6, 0xcf, 0x09, 0xce, 0x1f, 0x49, 0x79, - 0xba, 0x98, 0xd9, 0x77, 0x8a, 0x69, 0x8c, 0xfd, 0x94, 0x86, 0xc4, 0x10, 0x41, 0x1a, 0x0f, 0xa4, - 0x8c, 0xea, 0xe0, 0x1a, 0x23, 0x4b, 0xa9, 0x18, 0x05, 0xc7, 0x84, 0x78, 0x57, 0xd5, 0x9e, 0xb2, - 0xde, 0xa3, 0xd4, 0xbb, 0x84, 0x20, 0x04, 0x19, 0x45, 0xa5, 0x9c, 0xb2, 0xaa, 0xf5, 0xfb, 0x10, - 0xe1, 0x22, 0x96, 0xc1, 0x85, 0x2c, 0xbb, 0x06, 0x32, 0xcd, 0x60, 0xc0, 0x49, 0xe4, 0x55, 0xd4, - 0xce, 0x85, 0x0e, 0xe6, 0x4f, 0x38, 0x89, 0xd0, 0x37, 0xb0, 0x4c, 0x8e, 0x8f, 0x49, 0x28, 0xe8, - 0x29, 0x09, 0x26, 0x1f, 0x77, 0x45, 0x95, 0xb8, 0x61, 0x4a, 0x7c, 0xf3, 0x3d, 0x4a, 0xbc, 0x27, - 0x7b, 0x75, 0x0c, 0x75, 0xdf, 0x56, 0xa5, 0xf1, 0x2e, 0xbe, 0xae, 0xec, 0x8a, 0xca, 0x62, 0x6a, - 0xbf, 0x2e, 0xf1, 0x75, 0x00, 0x79, 0x38, 0xfd, 0x41, 0xfb, 0x84, 0x8c, 0x14, 0x5b, 0xf3, 0xbe, - 0x3c, 0xae, 0x03, 0xa5, 0xb8, 0x80, 0xd8, 0xc5, 0xff, 0x98, 0xd8, 0xe8, 0x21, 0x14, 0x25, 0x59, - 0x02, 0xa6, 0x69, 0xe6, 0x79, 0x6b, 0x4e, 0xbd, 0xd0, 0xda, 0xb8, 0x24, 0xc0, 0x19, 0x62, 0xfa, - 0x85, 0x70, 0x22, 0x7c, 0x99, 0xc9, 0x95, 0xdc, 0x4a, 0xed, 0x4f, 0x07, 0xb2, 0x06, 0x7f, 0x0b, - 0xb2, 0x26, 0x75, 0x47, 0xa5, 0x7e, 0xfb, 0x32, 0xe4, 0x50, 0x0c, 0x4d, 0xc2, 0xc6, 0x11, 0xad, - 0x43, 0x59, 0xaf, 0x82, 0x1e, 0xe1, 0x1c, 0x77, 0x88, 0xe2, 0x5f, 0xde, 0x2f, 0x69, 0xed, 0x43, - 0xad, 0x44, 0x77, 0xa0, 0x12, 0x63, 0x2e, 0x9e, 0xf4, 0x23, 0x2c, 0x48, 0x20, 0x68, 0x8f, 0x70, - 0x81, 0x7b, 0x7d, 0x45, 0xc4, 0x39, 0x7f, 0x79, 0x62, 0x3b, 0xb4, 0x26, 0x54, 0x07, 0x39, 0x1d, - 0xe4, 0xe4, 0xf1, 0xc9, 0xf1, 0x20, 0x89, 0x48, 0xa4, 0x58, 0xa7, 0x87, 0xc6, 0x59, 0x35, 0xfa, - 0x04, 0x96, 0xc2, 0x94, 0x60, 0x39, 0xed, 0x26, 0xc8, 0xf3, 0x0a, 0xd9, 0x35, 0x86, 0x31, 0x6c, - 0xed, 0x87, 0x59, 0x28, 0xf9, 0xe4, 0x94, 0xa4, 0xc2, 0x0e, 0xaf, 0x75, 0x28, 0xa7, 0x4a, 0x11, - 0xe0, 0x28, 0x4a, 0x09, 0xe7, 0x66, 0xcc, 0x94, 0xb4, 0x76, 0x4b, 0x2b, 0xd1, 0xc7, 0x50, 0xd6, - 0x87, 0x91, 0x04, 0xda, 0x60, 0x66, 0x98, 0x3a, 0xa2, 0xfd, 0x44, 0x63, 0xca, 0xcb, 0x4a, 0x4d, - 0xcb, 0x31, 0x96, 0xbe, 0x70, 0x8b, 0x4a, 0x69, 0xa1, 0x26, 0x11, 0x6d, 0xd1, 0xe4, 0x97, 0x15, - 0x6d, 0x44, 0x5b, 0xb4, 0xa7, 0x72, 0xba, 0xa9, 0x6d, 0x93, 0xae, 0x9d, 0xff, 0xb0, 0xc1, 0x63, - 0xe2, 0xd9, 0x1e, 0xaf, 0xfd, 0x38, 0x0f, 0xc5, 0x1d, 0x79, 0xb0, 0x6a, 0x3c, 0x1e, 0x0e, 0x91, - 0x07, 0x0b, 0xaa, 0x54, 0xcc, 0x0e, 0x59, 0x2b, 0xca, 0xdb, 0x5d, 0xcf, 0x03, 0x7d, 0xb0, 0x5a, - 0x40, 0xdf, 0x42, 0x5e, 0xdd, 0x2c, 0xc7, 0x84, 0x70, 0x93, 0xd4, 0xce, 0x3f, 0x4c, 0xea, 0x8f, - 0x57, 0x37, 0xdc, 0x11, 0xee, 0xc5, 0x5f, 0xd4, 0xc6, 0x48, 0x35, 0x3f, 0x27, 0xd7, 0xbb, 0x84, - 0x70, 0x74, 0x0b, 0x16, 0x53, 0x12, 0xe3, 0x11, 0x89, 0xc6, 0x55, 0xca, 0xea, 0x59, 0x66, 0xd4, - 0xb6, 0x4c, 0xbb, 0x50, 0x08, 0x43, 0x31, 0xb4, 0x2c, 0xcc, 0x29, 0x92, 0xac, 0x5f, 0xd2, 0xca, - 0xa6, 0x8d, 0x21, 0x1c, 0xb7, 0x34, 0x7a, 0x0c, 0x65, 0xaa, 0x1f, 0x5e, 0x41, 0x5f, 0x5d, 0x3d, - 0x6a, 0x02, 0x16, 0x5a, 0x9f, 0x5e, 0x02, 0x35, 0xf5, 0x5a, 0xf3, 0x4b, 0x74, 0xea, 0xf1, 0x76, - 0x04, 0x8b, 0xcc, 0xdc, 0x67, 0x16, 0x15, 0xd6, 0xe6, 0xea, 0x85, 0xd6, 0xe6, 0x25, 0xa8, 0xd3, - 0xb7, 0xa0, 0x5f, 0x66, 0xd3, 0xb7, 0x62, 0x0a, 0xd7, 0xd4, 0x7b, 0x31, 0x64, 0x71, 0x10, 0xb2, - 0x44, 0xa4, 0x38, 0x14, 0xc1, 0x29, 0x49, 0x39, 0x65, 0x89, 0x79, 0x61, 0x7c, 0x76, 0x49, 0x84, - 0x03, 0xe3, 0xbf, 0x63, 0xdc, 0x8f, 0xb4, 0xb7, 0x7f, 0xb5, 0x7f, 0xbe, 0x01, 0x3d, 0x1d, 0xb7, - 0xad, 0x1d, 0x48, 0xc5, 0xf7, 0x2a, 0xd0, 0x14, 0xdd, 0xb6, 0x33, 0xb2, 0x4d, 0x6c, 0xab, 0x1b, - 0xe5, 0xc6, 0xf7, 0x00, 0x93, 0xe1, 0x82, 0x10, 0x94, 0x0f, 0x48, 0x12, 0xd1, 0xa4, 0x63, 0x6a, - 0xeb, 0xce, 0xa0, 0x65, 0x58, 0x34, 0x3a, 0x5b, 0x19, 0xd7, 0x41, 0x4b, 0x50, 0xb2, 0xd2, 0x43, - 0x9a, 0x90, 0xc8, 0x9d, 0x93, 0x2a, 0xb3, 0x4f, 0x87, 0x75, 0x33, 0xa8, 0x08, 0x39, 0xbd, 0x26, - 0x91, 0x3b, 0x8f, 0x0a, 0xb0, 0xb0, 0xa5, 0xdf, 0x33, 0x6e, 0x76, 0x35, 0xf3, 0xcb, 0xcf, 0x55, - 0x67, 0xe3, 0x2b, 0xa8, 0x9c, 0x37, 0x95, 0x91, 0x0b, 0xc5, 0x47, 0x4c, 0xec, 0xda, 0xd7, 0x9d, - 0x3b, 0x83, 0x4a, 0x90, 0x9f, 0x88, 0x8e, 0x44, 0xbe, 0x37, 0x24, 0xe1, 0x40, 0x82, 0xcd, 0x1a, - 0xb0, 0x26, 0x5c, 0xfd, 0x9b, 0xca, 0xa2, 0x2c, 0xcc, 0x1e, 0xdd, 0x71, 0x67, 0xd4, 0x6f, 0xcb, - 0x75, 0xb4, 0xc3, 0xf6, 0xfd, 0x17, 0x6f, 0xaa, 0xce, 0xcb, 0x37, 0x55, 0xe7, 0xf5, 0x9b, 0xaa, - 0xf3, 0xd3, 0xdb, 0xea, 0xcc, 0xcb, 0xb7, 0xd5, 0x99, 0xdf, 0xde, 0x56, 0x67, 0x9e, 0x6d, 0x9e, - 0x61, 0x92, 0x2c, 0xec, 0xa6, 0xfe, 0xff, 0x90, 0xb0, 0x88, 0x34, 0x87, 0x67, 0xff, 0xa6, 0x28, - 0x52, 0xb5, 0xb3, 0xea, 0xe0, 0xee, 0xfe, 0x15, 0x00, 0x00, 0xff, 0xff, 0x94, 0x4b, 0x30, 0xb4, - 0xd4, 0x0c, 0x00, 0x00, + 0x8d, 0x63, 0x20, 0x39, 0x74, 0x4b, 0xed, 0xd2, 0x12, 0x61, 0x69, 0xa9, 0x2e, 0x29, 0x43, 0x0a, + 0x7a, 0xeb, 0xb9, 0x40, 0xff, 0x42, 0x81, 0x1e, 0xfa, 0x53, 0x72, 0xcc, 0xb1, 0xe8, 0x21, 0x0d, + 0x92, 0x7f, 0xd0, 0x5f, 0x50, 0xf0, 0x4b, 0x1f, 0x81, 0x6b, 0xa7, 0x69, 0x4f, 0x22, 0x9f, 0x21, + 0x9f, 0x99, 0x1d, 0xce, 0x33, 0xa4, 0xa0, 0xf9, 0x9c, 0x08, 0x1c, 0x76, 0x30, 0x8d, 0x1b, 0x6a, + 0xc4, 0x12, 0xd2, 0x08, 0x13, 0xc6, 0xb9, 0xc6, 0xd4, 0x30, 0x50, 0xe3, 0x40, 0x0c, 0xeb, 0xfd, + 0x84, 0x09, 0x86, 0xae, 0x8f, 0xf7, 0xd4, 0xed, 0x9e, 0xfa, 0x64, 0xcf, 0x7a, 0xa9, 0xcd, 0xda, + 0x4c, 0xad, 0x6c, 0xc8, 0x91, 0xde, 0xb4, 0x7e, 0xf3, 0x1c, 0x47, 0xfd, 0xd3, 0x76, 0x23, 0x64, + 0xd2, 0x0d, 0xa3, 0xb1, 0x5e, 0x57, 0xfd, 0x23, 0x05, 0x85, 0xfd, 0xb8, 0xc5, 0x06, 0x71, 0x74, + 0x88, 0x13, 0xdc, 0xe3, 0x68, 0x0d, 0xd2, 0x9c, 0xc4, 0x11, 0x49, 0x3c, 0xa7, 0xe2, 0xd4, 0xb2, + 0xbe, 0x99, 0xa1, 0x9b, 0xb0, 0xac, 0x47, 0x26, 0x3e, 0x1a, 0x79, 0xf3, 0x15, 0xa7, 0xb6, 0xe0, + 0x17, 0x34, 0xbc, 0x2b, 0xd1, 0xfd, 0x08, 0xfd, 0x0f, 0xb2, 0x62, 0x18, 0xb0, 0x84, 0xb6, 0x69, + 0xec, 0x2d, 0x28, 0x8a, 0x8c, 0x18, 0x1e, 0xa8, 0x39, 0xda, 0x81, 0xac, 0x74, 0x1e, 0x88, 0x51, + 0x9f, 0x78, 0xa9, 0x8a, 0x53, 0x2b, 0x36, 0x37, 0xea, 0xe7, 0x7c, 0x5f, 0xff, 0xb4, 0x5d, 0x57, + 0x51, 0xee, 0x32, 0x1a, 0x1f, 0x8d, 0xfa, 0xc4, 0xcf, 0x84, 0x66, 0x84, 0x4a, 0xb0, 0x88, 0x39, + 0x27, 0xc2, 0x5b, 0x54, 0xe4, 0x7a, 0x82, 0xee, 0x43, 0x1a, 0xf7, 0xd8, 0x20, 0x16, 0x5e, 0x5a, + 0xc2, 0x3b, 0x8d, 0x17, 0xaf, 0x6e, 0xcc, 0xfd, 0xfe, 0xea, 0xc6, 0xad, 0x36, 0x15, 0x9d, 0x41, + 0xab, 0x1e, 0xb2, 0x5e, 0x23, 0x64, 0xbc, 0xc7, 0xb8, 0xf9, 0xd9, 0xe2, 0xd1, 0x69, 0x43, 0xc6, + 0xc1, 0xeb, 0x4f, 0x68, 0x2c, 0x7c, 0xb3, 0x1d, 0x7d, 0x04, 0x05, 0xd6, 0xe2, 0x24, 0x39, 0x23, + 0x51, 0xd0, 0xc1, 0xbc, 0xe3, 0x2d, 0x29, 0x37, 0x79, 0x0b, 0x3e, 0xc0, 0xbc, 0x83, 0x3e, 0x07, + 0x6f, 0xbc, 0x88, 0x0c, 0x05, 0x49, 0x62, 0xdc, 0x0d, 0x3a, 0x84, 0xb6, 0x3b, 0xc2, 0xcb, 0x54, + 0x9c, 0x5a, 0xca, 0x5f, 0xb3, 0xf6, 0x7b, 0xc6, 0xfc, 0x40, 0x59, 0xd1, 0xff, 0x21, 0xdf, 0xc2, + 0xdd, 0x2e, 0x13, 0x01, 0x8d, 0x23, 0x32, 0xf4, 0xb2, 0x8a, 0x3d, 0xa7, 0xb1, 0x7d, 0x09, 0xa1, + 0x26, 0x5c, 0x39, 0xa1, 0x31, 0xee, 0xd2, 0xe7, 0x24, 0x0a, 0x64, 0x4a, 0x2c, 0x33, 0x28, 0xe6, + 0xd5, 0xb1, 0xf1, 0x19, 0x11, 0xd8, 0xd0, 0x52, 0x58, 0x13, 0xc3, 0xc0, 0x58, 0xb0, 0xa0, 0x2c, + 0x0e, 0xb8, 0xc0, 0x62, 0xc0, 0xbd, 0x9c, 0xca, 0xf2, 0xdd, 0xfa, 0x85, 0x55, 0x54, 0x3f, 0x1a, + 0xee, 0x4d, 0xed, 0x7d, 0xac, 0xb6, 0xfa, 0x25, 0x71, 0x0e, 0x8a, 0xb6, 0x60, 0x95, 0xf2, 0x60, + 0xba, 0x54, 0x43, 0xdc, 0xed, 0x7a, 0xf9, 0x8a, 0x53, 0xcb, 0xf8, 0x2e, 0xe5, 0xbb, 0xd2, 0xa2, + 0xaa, 0x61, 0x17, 0x77, 0xbb, 0xd5, 0xef, 0xa0, 0x28, 0xe3, 0xdc, 0x0e, 0x43, 0x99, 0x5e, 0x1a, + 0xb7, 0x51, 0x00, 0xab, 0xb8, 0xc5, 0x12, 0x61, 0xbf, 0xce, 0x9c, 0x9b, 0xf3, 0x61, 0xe7, 0xb6, + 0x62, 0xb8, 0x94, 0x13, 0xc5, 0x54, 0x3d, 0x86, 0x9c, 0x74, 0x7d, 0xd0, 0x97, 0x51, 0x73, 0x59, + 0x91, 0x6d, 0xcc, 0x83, 0x2e, 0xed, 0x51, 0xed, 0x25, 0xe5, 0x67, 0xda, 0x98, 0x7f, 0x2d, 0xe7, + 0x68, 0x13, 0x56, 0x28, 0x0f, 0x70, 0xd2, 0xa2, 0x22, 0xc1, 0xc9, 0x48, 0x7f, 0xcb, 0xbc, 0xfa, + 0x96, 0x65, 0xca, 0xb7, 0x2d, 0xae, 0x3e, 0xe5, 0x75, 0x1a, 0x8a, 0x07, 0x03, 0x31, 0xad, 0x96, + 0x75, 0xc8, 0x24, 0x24, 0x24, 0xf4, 0x6c, 0xac, 0x97, 0xf1, 0x1c, 0xdd, 0x06, 0xd7, 0x8e, 0x75, + 0xa2, 0xf6, 0xad, 0x64, 0x96, 0x2d, 0x6e, 0x45, 0x33, 0xa3, 0x8b, 0x85, 0x0f, 0xd3, 0xc5, 0x44, + 0x01, 0xa9, 0x7f, 0xa7, 0x00, 0xa9, 0x60, 0xce, 0x83, 0x98, 0xc5, 0x21, 0x51, 0x22, 0x4b, 0xf9, + 0x19, 0xc1, 0xf9, 0x23, 0x39, 0x9f, 0x4d, 0x66, 0xfa, 0x9d, 0x64, 0x1a, 0x63, 0x3f, 0xa1, 0x21, + 0x31, 0xba, 0x91, 0xc6, 0x43, 0x39, 0x47, 0x35, 0x70, 0x8d, 0x91, 0x25, 0x54, 0x8c, 0x82, 0x13, + 0x42, 0xbc, 0xab, 0x6a, 0x4d, 0x51, 0xaf, 0x51, 0xf0, 0x1e, 0x21, 0x08, 0x41, 0x4a, 0x29, 0x2f, + 0xa3, 0xac, 0x6a, 0xfc, 0x3e, 0xba, 0xb9, 0x48, 0x94, 0x70, 0xa1, 0x28, 0xaf, 0x81, 0x0c, 0x33, + 0x18, 0x70, 0x12, 0x79, 0x25, 0xb5, 0x72, 0xa9, 0x8d, 0xf9, 0x13, 0x4e, 0x22, 0xf4, 0x0d, 0xac, + 0x92, 0x93, 0x13, 0x12, 0x0a, 0x7a, 0x46, 0x82, 0xc9, 0xc7, 0x5d, 0x51, 0x29, 0xae, 0x9b, 0x14, + 0xdf, 0x7c, 0x8f, 0x14, 0xef, 0xcb, 0x5a, 0x1d, 0x53, 0xdd, 0xb7, 0x59, 0xa9, 0xbf, 0xcb, 0xaf, + 0x33, 0xbb, 0xa6, 0xa2, 0x98, 0x59, 0xaf, 0x53, 0x7c, 0x1d, 0x40, 0x1e, 0x4e, 0x7f, 0xd0, 0x3a, + 0x25, 0x23, 0x25, 0xee, 0xac, 0x2f, 0x8f, 0xeb, 0x50, 0x01, 0x17, 0xf4, 0x81, 0xfc, 0x7f, 0xdd, + 0x07, 0x1e, 0x42, 0x5e, 0x8a, 0x25, 0x60, 0x5a, 0x66, 0x9e, 0x57, 0x71, 0x6a, 0xb9, 0xe6, 0xe6, + 0x25, 0x0e, 0xa6, 0x84, 0xe9, 0xe7, 0xc2, 0xc9, 0xe4, 0xcb, 0x54, 0xa6, 0xe0, 0x96, 0xaa, 0x3f, + 0xcf, 0x43, 0xda, 0xf0, 0x6f, 0x43, 0xda, 0x84, 0xee, 0xa8, 0xd0, 0x6f, 0x5f, 0xc6, 0x1c, 0x8a, + 0xa1, 0x09, 0xd8, 0x6c, 0x44, 0x1b, 0x50, 0xd4, 0xa3, 0xa0, 0x47, 0x38, 0xc7, 0x6d, 0xa2, 0xf4, + 0x97, 0xf5, 0x0b, 0x1a, 0x7d, 0xa8, 0x41, 0xd9, 0xf2, 0x49, 0x92, 0xb0, 0x64, 0xbc, 0x2a, 0xad, + 0x5b, 0xbe, 0x02, 0xed, 0xa2, 0x3b, 0x50, 0xea, 0x62, 0x2e, 0x9e, 0xf4, 0x23, 0x2c, 0x48, 0x20, + 0x68, 0x8f, 0x70, 0x81, 0x7b, 0x7d, 0xa5, 0xd6, 0x05, 0x7f, 0x75, 0x62, 0x3b, 0xb2, 0x26, 0x54, + 0x03, 0xd9, 0x42, 0x64, 0x7b, 0xf2, 0xc9, 0xc9, 0x20, 0x8e, 0x48, 0xa4, 0xa4, 0xa9, 0x3b, 0xcb, + 0x34, 0x8c, 0x3e, 0x81, 0x95, 0x30, 0x21, 0x58, 0xb6, 0xc4, 0x09, 0xf3, 0xa2, 0x62, 0x76, 0x8d, + 0x61, 0x4c, 0x5b, 0xfd, 0x61, 0x1e, 0x0a, 0x3e, 0x39, 0x23, 0x89, 0xb0, 0x1d, 0x6e, 0x03, 0x8a, + 0x89, 0x02, 0x02, 0x1c, 0x45, 0x09, 0xe1, 0xdc, 0xf4, 0xa2, 0x82, 0x46, 0xb7, 0x35, 0x88, 0x3e, + 0x86, 0xa2, 0x3e, 0xb1, 0x38, 0xd0, 0x06, 0xd3, 0xe8, 0xd4, 0x39, 0x1e, 0xc4, 0x9a, 0x53, 0x66, + 0x43, 0xb5, 0xd4, 0x31, 0x97, 0xbe, 0xc4, 0xf3, 0x0a, 0xb4, 0x54, 0x13, 0x8f, 0x36, 0x67, 0xf2, + 0xcb, 0xf2, 0xd6, 0xa3, 0x4d, 0xda, 0x53, 0xd9, 0x02, 0xd5, 0xb2, 0x49, 0x69, 0x2f, 0x7e, 0x58, + 0x77, 0x32, 0xfe, 0xac, 0x10, 0xaa, 0x3f, 0x2e, 0x42, 0x7e, 0x72, 0xd5, 0x1c, 0x0d, 0x91, 0x07, + 0x4b, 0x2a, 0x55, 0xcc, 0x76, 0x62, 0x3b, 0x95, 0x2f, 0x06, 0xdd, 0x34, 0xf4, 0xe9, 0xeb, 0x09, + 0xfa, 0x16, 0xb2, 0xea, 0xfa, 0x39, 0x21, 0x84, 0x9b, 0xa0, 0x76, 0xff, 0x61, 0x50, 0x7f, 0xbe, + 0xba, 0xe1, 0x8e, 0x70, 0xaf, 0xfb, 0x45, 0x75, 0xcc, 0x54, 0xf5, 0x33, 0x72, 0xbc, 0x47, 0x08, + 0x47, 0xb7, 0x60, 0x39, 0x21, 0x5d, 0x3c, 0x22, 0xd1, 0x3b, 0x95, 0x55, 0x34, 0xb0, 0x4d, 0xd3, + 0x1e, 0xe4, 0xc2, 0x50, 0x0c, 0xad, 0x54, 0x33, 0x4a, 0x49, 0x1b, 0x97, 0xd4, 0xbb, 0xa9, 0x75, + 0x08, 0xc7, 0x75, 0x8f, 0x1e, 0x43, 0x91, 0xea, 0xc7, 0x5c, 0xd0, 0x57, 0xf7, 0x93, 0x6a, 0x93, + 0xb9, 0xe6, 0xa7, 0x97, 0x50, 0xcd, 0xbc, 0x00, 0xfd, 0x02, 0x9d, 0x79, 0x10, 0x1e, 0xc3, 0x32, + 0x33, 0x97, 0x9e, 0x65, 0x85, 0xca, 0x42, 0x2d, 0xd7, 0xdc, 0xba, 0x84, 0x75, 0xf6, 0xaa, 0xf4, + 0x8b, 0x6c, 0xf6, 0xea, 0x4c, 0xe0, 0x9a, 0x7a, 0x83, 0x86, 0xac, 0x1b, 0x84, 0x2c, 0x16, 0x09, + 0x0e, 0x45, 0x70, 0x46, 0x12, 0x4e, 0x59, 0x6c, 0x5e, 0x2d, 0x9f, 0x5d, 0xe2, 0xe1, 0xd0, 0xec, + 0xdf, 0x35, 0xdb, 0x8f, 0xf5, 0x6e, 0xff, 0x6a, 0xff, 0x7c, 0x03, 0x7a, 0x3a, 0x2e, 0x5b, 0xdb, + 0xb5, 0xf2, 0xef, 0x95, 0xa0, 0x19, 0xb9, 0xed, 0xa4, 0x64, 0x99, 0xd8, 0x52, 0x37, 0xe0, 0xe6, + 0xf7, 0x00, 0x93, 0x0e, 0x84, 0x10, 0x14, 0x0f, 0x49, 0x1c, 0xd1, 0xb8, 0x6d, 0x72, 0xeb, 0xce, + 0xa1, 0x55, 0x58, 0x36, 0x98, 0xcd, 0x8c, 0xeb, 0xa0, 0x15, 0x28, 0xd8, 0xd9, 0x43, 0x1a, 0x93, + 0xc8, 0x5d, 0x90, 0x90, 0x59, 0xa7, 0xdd, 0xba, 0x29, 0x94, 0x87, 0x8c, 0x1e, 0x93, 0xc8, 0x5d, + 0x44, 0x39, 0x58, 0xda, 0xd6, 0x8f, 0x1e, 0x37, 0xbd, 0x9e, 0xfa, 0xf5, 0x97, 0xb2, 0xb3, 0xf9, + 0x15, 0x94, 0xce, 0x6b, 0xdd, 0xc8, 0x85, 0xfc, 0x23, 0x26, 0xf6, 0xec, 0x8b, 0xd1, 0x9d, 0x43, + 0x05, 0xc8, 0x4e, 0xa6, 0x8e, 0x64, 0xbe, 0x37, 0x24, 0xe1, 0x40, 0x92, 0xcd, 0x1b, 0xb2, 0x06, + 0x5c, 0xfd, 0x9b, 0xcc, 0xa2, 0x34, 0xcc, 0x1f, 0xdf, 0x71, 0xe7, 0xd4, 0x6f, 0xd3, 0x75, 0xf4, + 0x86, 0x9d, 0xfb, 0x2f, 0xde, 0x94, 0x9d, 0x97, 0x6f, 0xca, 0xce, 0xeb, 0x37, 0x65, 0xe7, 0xa7, + 0xb7, 0xe5, 0xb9, 0x97, 0x6f, 0xcb, 0x73, 0xbf, 0xbd, 0x2d, 0xcf, 0x3d, 0xdb, 0x9a, 0x52, 0x92, + 0x4c, 0xec, 0x96, 0xfe, 0x4f, 0x12, 0xb3, 0x88, 0x34, 0x86, 0xd3, 0x7f, 0x7d, 0x94, 0xa8, 0x5a, + 0x69, 0x75, 0x70, 0x77, 0xff, 0x0a, 0x00, 0x00, 0xff, 0xff, 0xa1, 0xdc, 0x72, 0x48, 0x28, 0x0d, + 0x00, 0x00, } func (m *InboundParams) Marshal() (dAtA []byte, err error) { @@ -861,6 +885,16 @@ func (m *InboundParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.IsCrossChainCall { + i-- + if m.IsCrossChainCall { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x60 + } if m.TxFinalizationStatus != 0 { i = encodeVarintCrossChainTx(dAtA, i, uint64(m.TxFinalizationStatus)) i-- @@ -1172,6 +1206,13 @@ func (m *Status) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.ErrorMessage) > 0 { + i -= len(m.ErrorMessage) + copy(dAtA[i:], m.ErrorMessage) + i = encodeVarintCrossChainTx(dAtA, i, uint64(len(m.ErrorMessage))) + i-- + dAtA[i] = 0x32 + } if m.CreatedTimestamp != 0 { i = encodeVarintCrossChainTx(dAtA, i, uint64(m.CreatedTimestamp)) i-- @@ -1432,6 +1473,9 @@ func (m *InboundParams) Size() (n int) { if m.TxFinalizationStatus != 0 { n += 1 + sovCrossChainTx(uint64(m.TxFinalizationStatus)) } + if m.IsCrossChainCall { + n += 2 + } return n } @@ -1548,6 +1592,10 @@ func (m *Status) Size() (n int) { if m.CreatedTimestamp != 0 { n += 1 + sovCrossChainTx(uint64(m.CreatedTimestamp)) } + l = len(m.ErrorMessage) + if l > 0 { + n += 1 + l + sovCrossChainTx(uint64(l)) + } return n } @@ -1943,6 +1991,26 @@ func (m *InboundParams) Unmarshal(dAtA []byte) error { break } } + case 12: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsCrossChainCall", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCrossChainTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IsCrossChainCall = bool(v != 0) default: iNdEx = preIndex skippy, err := skipCrossChainTx(dAtA[iNdEx:]) @@ -2773,6 +2841,38 @@ func (m *Status) Unmarshal(dAtA []byte) error { break } } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ErrorMessage", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCrossChainTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCrossChainTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCrossChainTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ErrorMessage = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipCrossChainTx(dAtA[iNdEx:]) diff --git a/x/crosschain/types/expected_keepers.go b/x/crosschain/types/expected_keepers.go index 140c9b2db2..0f496c13d3 100644 --- a/x/crosschain/types/expected_keepers.go +++ b/x/crosschain/types/expected_keepers.go @@ -146,6 +146,7 @@ type FungibleKeeper interface { coinType coin.CoinType, asset string, protocolContractVersion ProtocolContractVersion, + isCrossChainCall bool, ) (*evmtypes.MsgEthereumTxResponse, bool, error) ProcessV2RevertDeposit( ctx sdk.Context, diff --git a/x/crosschain/types/inbound_parsing.go b/x/crosschain/types/inbound_parsing.go new file mode 100644 index 0000000000..e057183f2c --- /dev/null +++ b/x/crosschain/types/inbound_parsing.go @@ -0,0 +1,246 @@ +package types + +import ( + "encoding/hex" + "math/big" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + ethcommon "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/pkg/errors" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" + + "github.com/zeta-chain/node/pkg/chains" + "github.com/zeta-chain/node/pkg/coin" +) + +// ParseGatewayEvent parses the event from the gateway contract +func ParseGatewayEvent( + log ethtypes.Log, + gatewayAddr ethcommon.Address, +) (*gatewayzevm.GatewayZEVMWithdrawn, *gatewayzevm.GatewayZEVMCalled, *gatewayzevm.GatewayZEVMWithdrawnAndCalled, error) { + if len(log.Topics) == 0 { + return nil, nil, nil, errors.New("ParseGatewayCallEvent: invalid log - no topics") + } + filterer, err := gatewayzevm.NewGatewayZEVMFilterer(log.Address, bind.ContractFilterer(nil)) + if err != nil { + return nil, nil, nil, err + } + withdrawalEvent, err := ParseGatewayWithdrawalEvent(log, gatewayAddr, filterer) + if err == nil { + return withdrawalEvent, nil, nil, nil + } + callEvent, err := ParseGatewayCallEvent(log, gatewayAddr, filterer) + if err == nil { + return nil, callEvent, nil, nil + } + withdrawAndCallEvent, err := ParseGatewayWithdrawAndCallEvent(log, gatewayAddr, filterer) + if err == nil { + return nil, nil, withdrawAndCallEvent, nil + } + return nil, nil, nil, errors.New("ParseGatewayEvent: invalid log - no event found") +} + +// ParseGatewayWithdrawalEvent parses the GatewayZEVMWithdrawal event from the log +func ParseGatewayWithdrawalEvent( + log ethtypes.Log, + gatewayAddr ethcommon.Address, + filterer *gatewayzevm.GatewayZEVMFilterer, +) (*gatewayzevm.GatewayZEVMWithdrawn, error) { + event, err := filterer.ParseWithdrawn(log) + if err != nil { + return nil, err + } + if event.Raw.Address != gatewayAddr { + return nil, errors.New("ParseGatewayWithdrawalEvent: invalid log - wrong contract address") + } + + return event, nil +} + +// ParseGatewayCallEvent parses the GatewayZEVMCall event from the log +func ParseGatewayCallEvent( + log ethtypes.Log, + gatewayAddr ethcommon.Address, + filterer *gatewayzevm.GatewayZEVMFilterer, +) (*gatewayzevm.GatewayZEVMCalled, error) { + event, err := filterer.ParseCalled(log) + if err != nil { + return nil, err + } + if event.Raw.Address != gatewayAddr { + return nil, errors.New("ParseGatewayCallEvent: invalid log - wrong contract address") + } + return event, nil +} + +// ParseGatewayWithdrawAndCallEvent parses the GatewayZEVMWithdrawAndCall event from the log +func ParseGatewayWithdrawAndCallEvent( + log ethtypes.Log, + gatewayAddr ethcommon.Address, + filterer *gatewayzevm.GatewayZEVMFilterer, +) (*gatewayzevm.GatewayZEVMWithdrawnAndCalled, error) { + event, err := filterer.ParseWithdrawnAndCalled(log) + if err != nil { + return nil, err + } + if event.Raw.Address != gatewayAddr { + return nil, errors.New("ParseGatewayWithdrawAndCallEvent: invalid log - wrong contract address") + } + return event, nil +} + +// NewWithdrawalInbound creates a new inbound object for a withdrawal +// currently inbound data is represented with a MsgVoteInbound message +// TODO: replace with a more appropriate object +// https://github.com/zeta-chain/node/issues/2658 +func NewWithdrawalInbound( + ctx sdk.Context, + txOrigin string, + coinType coin.CoinType, + asset string, + event *gatewayzevm.GatewayZEVMWithdrawn, + receiverChain chains.Chain, + gasLimitQueried *big.Int, +) (*MsgVoteInbound, error) { + senderChain, err := chains.ZetaChainFromCosmosChainID(ctx.ChainID()) + if err != nil { + return nil, errors.Wrapf(err, "ProcessZEVMInboundV2: failed to convert chainID %s", ctx.ChainID()) + } + + toAddr, err := receiverChain.EncodeAddress(event.Receiver) + if err != nil { + return nil, errors.Wrapf(err, "cannot encode address %v", event.Receiver) + } + + gasLimit := event.CallOptions.GasLimit.Uint64() + if gasLimit == 0 { + gasLimit = gasLimitQueried.Uint64() + } + + // if the message is not empty, specify cross-chain call for backward compatibility with the Withdraw event + isCrossChainCall := false + if len(event.Message) > 0 { + isCrossChainCall = true + } + + return NewMsgVoteInbound( + "", + event.Sender.Hex(), + senderChain.ChainId, + txOrigin, + toAddr, + receiverChain.ChainId, + math.NewUintFromBigInt(event.Value), + hex.EncodeToString(event.Message), + event.Raw.TxHash.String(), + event.Raw.BlockNumber, + gasLimit, + coinType, + asset, + event.Raw.Index, + ProtocolContractVersion_V2, + event.CallOptions.IsArbitraryCall, + WithZEVMRevertOptions(event.RevertOptions), + WithCrossChainCall(isCrossChainCall), + ), nil +} + +// NewCallInbound creates a new inbound object for a call +// currently inbound data is represented with a MsgVoteInbound message +// TODO: replace with a more appropriate object +// https://github.com/zeta-chain/node/issues/2658 +func NewCallInbound( + ctx sdk.Context, + txOrigin string, + event *gatewayzevm.GatewayZEVMCalled, + receiverChain chains.Chain, + gasLimitQueried *big.Int, +) (*MsgVoteInbound, error) { + senderChain, err := chains.ZetaChainFromCosmosChainID(ctx.ChainID()) + if err != nil { + return nil, errors.Wrapf(err, "ProcessZEVMInboundV2: failed to convert chainID %s", ctx.ChainID()) + } + + toAddr, err := receiverChain.EncodeAddress(event.Receiver) + if err != nil { + return nil, errors.Wrapf(err, "cannot encode address %v", event.Receiver) + } + + gasLimit := event.CallOptions.GasLimit.Uint64() + if gasLimit == 0 { + gasLimit = gasLimitQueried.Uint64() + } + + return NewMsgVoteInbound( + "", + event.Sender.Hex(), + senderChain.ChainId, + txOrigin, + toAddr, + receiverChain.ChainId, + math.ZeroUint(), + hex.EncodeToString(event.Message), + event.Raw.TxHash.String(), + event.Raw.BlockNumber, + gasLimit, + coin.CoinType_NoAssetCall, + "", + event.Raw.Index, + ProtocolContractVersion_V2, + event.CallOptions.IsArbitraryCall, + WithZEVMRevertOptions(event.RevertOptions), + ), nil +} + +// NewWithdrawAndCallInbound creates a new inbound object for a withdraw and call +// currently inbound data is represented with a MsgVoteInbound message +// TODO: replace with a more appropriate object +// https://github.com/zeta-chain/node/issues/2658 +func NewWithdrawAndCallInbound( + ctx sdk.Context, + txOrigin string, + coinType coin.CoinType, + asset string, + event *gatewayzevm.GatewayZEVMWithdrawnAndCalled, + receiverChain chains.Chain, + gasLimitQueried *big.Int, +) (*MsgVoteInbound, error) { + senderChain, err := chains.ZetaChainFromCosmosChainID(ctx.ChainID()) + if err != nil { + return nil, errors.Wrapf(err, "ProcessZEVMInboundV2: failed to convert chainID %s", ctx.ChainID()) + } + + toAddr, err := receiverChain.EncodeAddress(event.Receiver) + if err != nil { + return nil, errors.Wrapf(err, "cannot encode address %v", event.Receiver) + } + + gasLimit := event.CallOptions.GasLimit.Uint64() + if gasLimit == 0 { + gasLimit = gasLimitQueried.Uint64() + } + + return NewMsgVoteInbound( + "", + event.Sender.Hex(), + senderChain.ChainId, + txOrigin, + toAddr, + receiverChain.ChainId, + math.NewUintFromBigInt(event.Value), + hex.EncodeToString(event.Message), + event.Raw.TxHash.String(), + event.Raw.BlockNumber, + gasLimit, + coinType, + asset, + event.Raw.Index, + ProtocolContractVersion_V2, + event.CallOptions.IsArbitraryCall, + WithZEVMRevertOptions(event.RevertOptions), + WithCrossChainCall(true), + ), nil +} diff --git a/x/crosschain/types/inbound_parsing_test.go b/x/crosschain/types/inbound_parsing_test.go new file mode 100644 index 0000000000..0d030fdd3d --- /dev/null +++ b/x/crosschain/types/inbound_parsing_test.go @@ -0,0 +1,89 @@ +package types_test + +import ( + "testing" +) + +// TODO: Complete tests for this file +// https://github.com/zeta-chain/node/issues/2669 + +func TestParseGatewayEvent(t *testing.T) { + +} + +func TestParseGatewayWithdrawalEvent(t *testing.T) { + +} + +func TestParseGatewayCallEvent(t *testing.T) { + +} + +func TestParseGatewayWithdrawAndCallEvent(t *testing.T) { + +} + +func TestNewWithdrawalInbound(t *testing.T) { + //t.Run("fail if sender chain ID is not valid", func(t *testing.T) { + // _, ctx, _, _ := keepertest.CrosschainKeeper(t) + // ctx = ctx.WithChainID("invalidChainID") + // + // fc := sample.ForeignCoins(t, sample.EthAddress().Hex()) + // + // _, err := types.NewWithdrawalInbound( + // ctx, + // sample.EthAddress().Hex(), + // fc.CoinType, + // fc.Asset, + // nil, + // chains.GoerliLocalnet, + // big.NewInt(1000), + // ) + // + // require.ErrorContains(t, err, " failed to convert chainID") + //}) + +} + +func TestNewCallInbound(t *testing.T) { + //t.Run("fail if sender chain ID is not valid", func(t *testing.T) { + // _, ctx, _, _ := keepertest.CrosschainKeeper(t) + // ctx = ctx.WithChainID("invalidChainID") + // + // _, err := types.NewCallInbound( + // ctx, + // sample.EthAddress().Hex(), + // nil, + // chains.GoerliLocalnet, + // big.NewInt(1000), + // ) + // + // require.ErrorContains(t, err, " failed to convert chainID") + //}) + +} + +func TestNewWithdrawAndCallInbound(t *testing.T) { + //t.Run("fail if sender chain ID is not valid", func(t *testing.T) { + // _, ctx, _, _ := keepertest.CrosschainKeeper(t) + // ctx = ctx.WithChainID("invalidChainID") + // + // fc := sample.ForeignCoins(t, sample.EthAddress().Hex()) + // + // _, err := types.NewWithdrawAndCallInbound( + // ctx, + // sample.EthAddress().Hex(), + // fc.CoinType, + // fc.Asset, + // nil, + // chains.GoerliLocalnet, + // big.NewInt(1000), + // ) + // + // require.ErrorContains(t, err, " failed to convert chainID") + //}) + // + //t.Run("fail if receiver address can't be decoded", func(t *testing.T) { + // + //}) +} diff --git a/x/crosschain/types/keys.go b/x/crosschain/types/keys.go index d9fea4a77d..76d343dd3e 100644 --- a/x/crosschain/types/keys.go +++ b/x/crosschain/types/keys.go @@ -3,7 +3,7 @@ package types import ( "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" + "cosmossdk.io/math" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/ethereum/go-ethereum/common" ) @@ -29,8 +29,8 @@ const ( CCTXIndexLength = 66 ) -func GetProtocolFee() sdk.Uint { - return sdk.NewUint(ProtocolFee) +func GetProtocolFee() math.Uint { + return math.NewUint(ProtocolFee) } func KeyPrefix(p string) []byte { diff --git a/x/crosschain/types/legacy_msgs.go b/x/crosschain/types/legacy_msgs.go new file mode 100644 index 0000000000..8b29ae9507 --- /dev/null +++ b/x/crosschain/types/legacy_msgs.go @@ -0,0 +1,181 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/zeta-chain/node/pkg/authz" +) + +// MsgVoteOnObservedInboundTx + +var _ sdk.Msg = &MsgVoteOnObservedInboundTx{} + +func (msg *MsgVoteOnObservedInboundTx) Route() string { + return RouterKey +} + +func (msg *MsgVoteOnObservedInboundTx) Type() string { + return authz.InboundVoter.String() +} + +func (msg *MsgVoteOnObservedInboundTx) GetSigners() []sdk.AccAddress { + creator, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + panic(err) + } + return []sdk.AccAddress{creator} +} + +func (msg *MsgVoteOnObservedInboundTx) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +func (msg *MsgVoteOnObservedInboundTx) ValidateBasic() error { + return nil +} + +// MsgVoteOnObservedOutboundTx + +var _ sdk.Msg = &MsgVoteOnObservedOutboundTx{} + +func (msg *MsgVoteOnObservedOutboundTx) Route() string { + return RouterKey +} + +func (msg *MsgVoteOnObservedOutboundTx) Type() string { + return authz.OutboundVoter.String() +} + +func (msg *MsgVoteOnObservedOutboundTx) GetSigners() []sdk.AccAddress { + creator, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + panic(err) + } + return []sdk.AccAddress{creator} +} + +func (msg *MsgVoteOnObservedOutboundTx) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +func (msg *MsgVoteOnObservedOutboundTx) ValidateBasic() error { + return nil +} + +// MsgAddToInTxTracker + +var _ sdk.Msg = &MsgAddToInTxTracker{} + +func (msg *MsgAddToInTxTracker) Route() string { + return RouterKey +} + +func (msg *MsgAddToInTxTracker) Type() string { + return "AddToInTxTracker" +} + +func (msg *MsgAddToInTxTracker) GetSigners() []sdk.AccAddress { + creator, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + panic(err) + } + return []sdk.AccAddress{creator} +} + +func (msg *MsgAddToInTxTracker) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +func (msg *MsgAddToInTxTracker) ValidateBasic() error { + return nil +} + +// MsgAddToOutTxTracker + +var _ sdk.Msg = &MsgAddToOutTxTracker{} + +func (msg *MsgAddToOutTxTracker) Route() string { + return RouterKey +} + +func (msg *MsgAddToOutTxTracker) Type() string { + return "AddToOutTxTracker" +} + +func (msg *MsgAddToOutTxTracker) GetSigners() []sdk.AccAddress { + creator, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + panic(err) + } + return []sdk.AccAddress{creator} +} + +func (msg *MsgAddToOutTxTracker) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +func (msg *MsgAddToOutTxTracker) ValidateBasic() error { + return nil +} + +// MsgRemoveFromOutTxTracker + +var _ sdk.Msg = &MsgRemoveFromOutTxTracker{} + +func (msg *MsgRemoveFromOutTxTracker) Route() string { + return RouterKey +} + +func (msg *MsgRemoveFromOutTxTracker) Type() string { + return "RemoveFromOutTxTracker" +} + +func (msg *MsgRemoveFromOutTxTracker) GetSigners() []sdk.AccAddress { + creator, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + panic(err) + } + return []sdk.AccAddress{creator} +} + +func (msg *MsgRemoveFromOutTxTracker) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +func (msg *MsgRemoveFromOutTxTracker) ValidateBasic() error { + return nil +} + +// MsgGasPriceVoter + +var _ sdk.Msg = &MsgGasPriceVoter{} + +func (msg *MsgGasPriceVoter) Route() string { + return RouterKey +} + +func (msg *MsgGasPriceVoter) Type() string { + return "GasPriceVoter" +} + +func (msg *MsgGasPriceVoter) GetSigners() []sdk.AccAddress { + creator, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + panic(err) + } + return []sdk.AccAddress{creator} +} + +func (msg *MsgGasPriceVoter) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +func (msg *MsgGasPriceVoter) ValidateBasic() error { + return nil +} diff --git a/x/crosschain/types/legacy_msgs.pb.go b/x/crosschain/types/legacy_msgs.pb.go new file mode 100644 index 0000000000..e5a6e67355 --- /dev/null +++ b/x/crosschain/types/legacy_msgs.pb.go @@ -0,0 +1,3002 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: zetachain/zetacore/crosschain/legacy_msgs.proto + +package types + +import ( + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + chains "github.com/zeta-chain/node/pkg/chains" + coin "github.com/zeta-chain/node/pkg/coin" + proofs "github.com/zeta-chain/node/pkg/proofs" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// legacy MsgAddOutboundTracker +// defined to keep codec compatibility +type MsgAddToOutTxTracker struct { + Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` + ChainId int64 `protobuf:"varint,2,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + Nonce uint64 `protobuf:"varint,3,opt,name=nonce,proto3" json:"nonce,omitempty"` + TxHash string `protobuf:"bytes,4,opt,name=tx_hash,json=txHash,proto3" json:"tx_hash,omitempty"` + Proof *proofs.Proof `protobuf:"bytes,5,opt,name=proof,proto3" json:"proof,omitempty"` + BlockHash string `protobuf:"bytes,6,opt,name=block_hash,json=blockHash,proto3" json:"block_hash,omitempty"` + TxIndex int64 `protobuf:"varint,7,opt,name=tx_index,json=txIndex,proto3" json:"tx_index,omitempty"` +} + +func (m *MsgAddToOutTxTracker) Reset() { *m = MsgAddToOutTxTracker{} } +func (m *MsgAddToOutTxTracker) String() string { return proto.CompactTextString(m) } +func (*MsgAddToOutTxTracker) ProtoMessage() {} +func (*MsgAddToOutTxTracker) Descriptor() ([]byte, []int) { + return fileDescriptor_246a7cc819884e07, []int{0} +} +func (m *MsgAddToOutTxTracker) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgAddToOutTxTracker) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgAddToOutTxTracker.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgAddToOutTxTracker) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgAddToOutTxTracker.Merge(m, src) +} +func (m *MsgAddToOutTxTracker) XXX_Size() int { + return m.Size() +} +func (m *MsgAddToOutTxTracker) XXX_DiscardUnknown() { + xxx_messageInfo_MsgAddToOutTxTracker.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgAddToOutTxTracker proto.InternalMessageInfo + +func (m *MsgAddToOutTxTracker) GetCreator() string { + if m != nil { + return m.Creator + } + return "" +} + +func (m *MsgAddToOutTxTracker) GetChainId() int64 { + if m != nil { + return m.ChainId + } + return 0 +} + +func (m *MsgAddToOutTxTracker) GetNonce() uint64 { + if m != nil { + return m.Nonce + } + return 0 +} + +func (m *MsgAddToOutTxTracker) GetTxHash() string { + if m != nil { + return m.TxHash + } + return "" +} + +func (m *MsgAddToOutTxTracker) GetProof() *proofs.Proof { + if m != nil { + return m.Proof + } + return nil +} + +func (m *MsgAddToOutTxTracker) GetBlockHash() string { + if m != nil { + return m.BlockHash + } + return "" +} + +func (m *MsgAddToOutTxTracker) GetTxIndex() int64 { + if m != nil { + return m.TxIndex + } + return 0 +} + +// legacy MsgAddInboundTracker +// defined to keep codec compatibility +type MsgAddToInTxTracker struct { + Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` + ChainId int64 `protobuf:"varint,2,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + TxHash string `protobuf:"bytes,3,opt,name=tx_hash,json=txHash,proto3" json:"tx_hash,omitempty"` + CoinType coin.CoinType `protobuf:"varint,4,opt,name=coin_type,json=coinType,proto3,enum=zetachain.zetacore.pkg.coin.CoinType" json:"coin_type,omitempty"` + Proof *proofs.Proof `protobuf:"bytes,5,opt,name=proof,proto3" json:"proof,omitempty"` + BlockHash string `protobuf:"bytes,6,opt,name=block_hash,json=blockHash,proto3" json:"block_hash,omitempty"` + TxIndex int64 `protobuf:"varint,7,opt,name=tx_index,json=txIndex,proto3" json:"tx_index,omitempty"` +} + +func (m *MsgAddToInTxTracker) Reset() { *m = MsgAddToInTxTracker{} } +func (m *MsgAddToInTxTracker) String() string { return proto.CompactTextString(m) } +func (*MsgAddToInTxTracker) ProtoMessage() {} +func (*MsgAddToInTxTracker) Descriptor() ([]byte, []int) { + return fileDescriptor_246a7cc819884e07, []int{1} +} +func (m *MsgAddToInTxTracker) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgAddToInTxTracker) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgAddToInTxTracker.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgAddToInTxTracker) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgAddToInTxTracker.Merge(m, src) +} +func (m *MsgAddToInTxTracker) XXX_Size() int { + return m.Size() +} +func (m *MsgAddToInTxTracker) XXX_DiscardUnknown() { + xxx_messageInfo_MsgAddToInTxTracker.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgAddToInTxTracker proto.InternalMessageInfo + +func (m *MsgAddToInTxTracker) GetCreator() string { + if m != nil { + return m.Creator + } + return "" +} + +func (m *MsgAddToInTxTracker) GetChainId() int64 { + if m != nil { + return m.ChainId + } + return 0 +} + +func (m *MsgAddToInTxTracker) GetTxHash() string { + if m != nil { + return m.TxHash + } + return "" +} + +func (m *MsgAddToInTxTracker) GetCoinType() coin.CoinType { + if m != nil { + return m.CoinType + } + return coin.CoinType_Zeta +} + +func (m *MsgAddToInTxTracker) GetProof() *proofs.Proof { + if m != nil { + return m.Proof + } + return nil +} + +func (m *MsgAddToInTxTracker) GetBlockHash() string { + if m != nil { + return m.BlockHash + } + return "" +} + +func (m *MsgAddToInTxTracker) GetTxIndex() int64 { + if m != nil { + return m.TxIndex + } + return 0 +} + +// legacy MsgRemoveOutboundTracker +// defined to keep codec compatibility +type MsgRemoveFromOutTxTracker struct { + Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` + ChainId int64 `protobuf:"varint,2,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + Nonce uint64 `protobuf:"varint,3,opt,name=nonce,proto3" json:"nonce,omitempty"` +} + +func (m *MsgRemoveFromOutTxTracker) Reset() { *m = MsgRemoveFromOutTxTracker{} } +func (m *MsgRemoveFromOutTxTracker) String() string { return proto.CompactTextString(m) } +func (*MsgRemoveFromOutTxTracker) ProtoMessage() {} +func (*MsgRemoveFromOutTxTracker) Descriptor() ([]byte, []int) { + return fileDescriptor_246a7cc819884e07, []int{2} +} +func (m *MsgRemoveFromOutTxTracker) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgRemoveFromOutTxTracker) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgRemoveFromOutTxTracker.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgRemoveFromOutTxTracker) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgRemoveFromOutTxTracker.Merge(m, src) +} +func (m *MsgRemoveFromOutTxTracker) XXX_Size() int { + return m.Size() +} +func (m *MsgRemoveFromOutTxTracker) XXX_DiscardUnknown() { + xxx_messageInfo_MsgRemoveFromOutTxTracker.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgRemoveFromOutTxTracker proto.InternalMessageInfo + +func (m *MsgRemoveFromOutTxTracker) GetCreator() string { + if m != nil { + return m.Creator + } + return "" +} + +func (m *MsgRemoveFromOutTxTracker) GetChainId() int64 { + if m != nil { + return m.ChainId + } + return 0 +} + +func (m *MsgRemoveFromOutTxTracker) GetNonce() uint64 { + if m != nil { + return m.Nonce + } + return 0 +} + +// legacy MsgVoteOutbound +// defined to keep codec compatibility +type MsgVoteOnObservedOutboundTx struct { + Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` + CctxHash string `protobuf:"bytes,2,opt,name=cctx_hash,json=cctxHash,proto3" json:"cctx_hash,omitempty"` + ObservedOutTxHash string `protobuf:"bytes,3,opt,name=observed_outTx_hash,json=observedOutTxHash,proto3" json:"observed_outTx_hash,omitempty"` + ObservedOutTxBlockHeight uint64 `protobuf:"varint,4,opt,name=observed_outTx_blockHeight,json=observedOutTxBlockHeight,proto3" json:"observed_outTx_blockHeight,omitempty"` + ObservedOutTxGasUsed uint64 `protobuf:"varint,10,opt,name=observed_outTx_gas_used,json=observedOutTxGasUsed,proto3" json:"observed_outTx_gas_used,omitempty"` + ObservedOutTxEffectiveGasPrice github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,11,opt,name=observed_outTx_effective_gas_price,json=observedOutTxEffectiveGasPrice,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"observed_outTx_effective_gas_price"` + ObservedOutTxEffectiveGasLimit uint64 `protobuf:"varint,12,opt,name=observed_outTx_effective_gas_limit,json=observedOutTxEffectiveGasLimit,proto3" json:"observed_outTx_effective_gas_limit,omitempty"` + ValueReceived github_com_cosmos_cosmos_sdk_types.Uint `protobuf:"bytes,5,opt,name=value_received,json=valueReceived,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Uint" json:"value_received" yaml:"value_received"` + Status chains.ReceiveStatus `protobuf:"varint,6,opt,name=status,proto3,enum=zetachain.zetacore.pkg.chains.ReceiveStatus" json:"status,omitempty"` + OutTxChain int64 `protobuf:"varint,7,opt,name=outTx_chain,json=outTxChain,proto3" json:"outTx_chain,omitempty"` + OutTxTssNonce uint64 `protobuf:"varint,8,opt,name=outTx_tss_nonce,json=outTxTssNonce,proto3" json:"outTx_tss_nonce,omitempty"` + CoinType coin.CoinType `protobuf:"varint,9,opt,name=coin_type,json=coinType,proto3,enum=zetachain.zetacore.pkg.coin.CoinType" json:"coin_type,omitempty"` +} + +func (m *MsgVoteOnObservedOutboundTx) Reset() { *m = MsgVoteOnObservedOutboundTx{} } +func (m *MsgVoteOnObservedOutboundTx) String() string { return proto.CompactTextString(m) } +func (*MsgVoteOnObservedOutboundTx) ProtoMessage() {} +func (*MsgVoteOnObservedOutboundTx) Descriptor() ([]byte, []int) { + return fileDescriptor_246a7cc819884e07, []int{3} +} +func (m *MsgVoteOnObservedOutboundTx) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgVoteOnObservedOutboundTx) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgVoteOnObservedOutboundTx.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgVoteOnObservedOutboundTx) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgVoteOnObservedOutboundTx.Merge(m, src) +} +func (m *MsgVoteOnObservedOutboundTx) XXX_Size() int { + return m.Size() +} +func (m *MsgVoteOnObservedOutboundTx) XXX_DiscardUnknown() { + xxx_messageInfo_MsgVoteOnObservedOutboundTx.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgVoteOnObservedOutboundTx proto.InternalMessageInfo + +func (m *MsgVoteOnObservedOutboundTx) GetCreator() string { + if m != nil { + return m.Creator + } + return "" +} + +func (m *MsgVoteOnObservedOutboundTx) GetCctxHash() string { + if m != nil { + return m.CctxHash + } + return "" +} + +func (m *MsgVoteOnObservedOutboundTx) GetObservedOutTxHash() string { + if m != nil { + return m.ObservedOutTxHash + } + return "" +} + +func (m *MsgVoteOnObservedOutboundTx) GetObservedOutTxBlockHeight() uint64 { + if m != nil { + return m.ObservedOutTxBlockHeight + } + return 0 +} + +func (m *MsgVoteOnObservedOutboundTx) GetObservedOutTxGasUsed() uint64 { + if m != nil { + return m.ObservedOutTxGasUsed + } + return 0 +} + +func (m *MsgVoteOnObservedOutboundTx) GetObservedOutTxEffectiveGasLimit() uint64 { + if m != nil { + return m.ObservedOutTxEffectiveGasLimit + } + return 0 +} + +func (m *MsgVoteOnObservedOutboundTx) GetStatus() chains.ReceiveStatus { + if m != nil { + return m.Status + } + return chains.ReceiveStatus_created +} + +func (m *MsgVoteOnObservedOutboundTx) GetOutTxChain() int64 { + if m != nil { + return m.OutTxChain + } + return 0 +} + +func (m *MsgVoteOnObservedOutboundTx) GetOutTxTssNonce() uint64 { + if m != nil { + return m.OutTxTssNonce + } + return 0 +} + +func (m *MsgVoteOnObservedOutboundTx) GetCoinType() coin.CoinType { + if m != nil { + return m.CoinType + } + return coin.CoinType_Zeta +} + +// legacy MsgVoteInbound +// defined to keep codec compatibility +type MsgVoteOnObservedInboundTx struct { + Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` + Sender string `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty"` + SenderChainId int64 `protobuf:"varint,3,opt,name=sender_chain_id,json=senderChainId,proto3" json:"sender_chain_id,omitempty"` + Receiver string `protobuf:"bytes,4,opt,name=receiver,proto3" json:"receiver,omitempty"` + ReceiverChain int64 `protobuf:"varint,5,opt,name=receiver_chain,json=receiverChain,proto3" json:"receiver_chain,omitempty"` + // string zeta_burnt = 6; + Amount github_com_cosmos_cosmos_sdk_types.Uint `protobuf:"bytes,6,opt,name=amount,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Uint" json:"amount"` + // string mMint = 7; + Message string `protobuf:"bytes,8,opt,name=message,proto3" json:"message,omitempty"` + InTxHash string `protobuf:"bytes,9,opt,name=in_tx_hash,json=inTxHash,proto3" json:"in_tx_hash,omitempty"` + InBlockHeight uint64 `protobuf:"varint,10,opt,name=in_block_height,json=inBlockHeight,proto3" json:"in_block_height,omitempty"` + GasLimit uint64 `protobuf:"varint,11,opt,name=gas_limit,json=gasLimit,proto3" json:"gas_limit,omitempty"` + CoinType coin.CoinType `protobuf:"varint,12,opt,name=coin_type,json=coinType,proto3,enum=zetachain.zetacore.pkg.coin.CoinType" json:"coin_type,omitempty"` + TxOrigin string `protobuf:"bytes,13,opt,name=tx_origin,json=txOrigin,proto3" json:"tx_origin,omitempty"` + Asset string `protobuf:"bytes,14,opt,name=asset,proto3" json:"asset,omitempty"` + // event index of the sent asset in the observed tx + EventIndex uint64 `protobuf:"varint,15,opt,name=event_index,json=eventIndex,proto3" json:"event_index,omitempty"` +} + +func (m *MsgVoteOnObservedInboundTx) Reset() { *m = MsgVoteOnObservedInboundTx{} } +func (m *MsgVoteOnObservedInboundTx) String() string { return proto.CompactTextString(m) } +func (*MsgVoteOnObservedInboundTx) ProtoMessage() {} +func (*MsgVoteOnObservedInboundTx) Descriptor() ([]byte, []int) { + return fileDescriptor_246a7cc819884e07, []int{4} +} +func (m *MsgVoteOnObservedInboundTx) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgVoteOnObservedInboundTx) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgVoteOnObservedInboundTx.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgVoteOnObservedInboundTx) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgVoteOnObservedInboundTx.Merge(m, src) +} +func (m *MsgVoteOnObservedInboundTx) XXX_Size() int { + return m.Size() +} +func (m *MsgVoteOnObservedInboundTx) XXX_DiscardUnknown() { + xxx_messageInfo_MsgVoteOnObservedInboundTx.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgVoteOnObservedInboundTx proto.InternalMessageInfo + +func (m *MsgVoteOnObservedInboundTx) GetCreator() string { + if m != nil { + return m.Creator + } + return "" +} + +func (m *MsgVoteOnObservedInboundTx) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgVoteOnObservedInboundTx) GetSenderChainId() int64 { + if m != nil { + return m.SenderChainId + } + return 0 +} + +func (m *MsgVoteOnObservedInboundTx) GetReceiver() string { + if m != nil { + return m.Receiver + } + return "" +} + +func (m *MsgVoteOnObservedInboundTx) GetReceiverChain() int64 { + if m != nil { + return m.ReceiverChain + } + return 0 +} + +func (m *MsgVoteOnObservedInboundTx) GetMessage() string { + if m != nil { + return m.Message + } + return "" +} + +func (m *MsgVoteOnObservedInboundTx) GetInTxHash() string { + if m != nil { + return m.InTxHash + } + return "" +} + +func (m *MsgVoteOnObservedInboundTx) GetInBlockHeight() uint64 { + if m != nil { + return m.InBlockHeight + } + return 0 +} + +func (m *MsgVoteOnObservedInboundTx) GetGasLimit() uint64 { + if m != nil { + return m.GasLimit + } + return 0 +} + +func (m *MsgVoteOnObservedInboundTx) GetCoinType() coin.CoinType { + if m != nil { + return m.CoinType + } + return coin.CoinType_Zeta +} + +func (m *MsgVoteOnObservedInboundTx) GetTxOrigin() string { + if m != nil { + return m.TxOrigin + } + return "" +} + +func (m *MsgVoteOnObservedInboundTx) GetAsset() string { + if m != nil { + return m.Asset + } + return "" +} + +func (m *MsgVoteOnObservedInboundTx) GetEventIndex() uint64 { + if m != nil { + return m.EventIndex + } + return 0 +} + +// legacy MsgVoteGasPrice +// defined to keep codec compatibility +type MsgGasPriceVoter struct { + Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` + ChainId int64 `protobuf:"varint,2,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + Price uint64 `protobuf:"varint,3,opt,name=price,proto3" json:"price,omitempty"` + BlockNumber uint64 `protobuf:"varint,4,opt,name=block_number,json=blockNumber,proto3" json:"block_number,omitempty"` + Supply string `protobuf:"bytes,5,opt,name=supply,proto3" json:"supply,omitempty"` +} + +func (m *MsgGasPriceVoter) Reset() { *m = MsgGasPriceVoter{} } +func (m *MsgGasPriceVoter) String() string { return proto.CompactTextString(m) } +func (*MsgGasPriceVoter) ProtoMessage() {} +func (*MsgGasPriceVoter) Descriptor() ([]byte, []int) { + return fileDescriptor_246a7cc819884e07, []int{5} +} +func (m *MsgGasPriceVoter) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgGasPriceVoter) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgGasPriceVoter.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgGasPriceVoter) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgGasPriceVoter.Merge(m, src) +} +func (m *MsgGasPriceVoter) XXX_Size() int { + return m.Size() +} +func (m *MsgGasPriceVoter) XXX_DiscardUnknown() { + xxx_messageInfo_MsgGasPriceVoter.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgGasPriceVoter proto.InternalMessageInfo + +func (m *MsgGasPriceVoter) GetCreator() string { + if m != nil { + return m.Creator + } + return "" +} + +func (m *MsgGasPriceVoter) GetChainId() int64 { + if m != nil { + return m.ChainId + } + return 0 +} + +func (m *MsgGasPriceVoter) GetPrice() uint64 { + if m != nil { + return m.Price + } + return 0 +} + +func (m *MsgGasPriceVoter) GetBlockNumber() uint64 { + if m != nil { + return m.BlockNumber + } + return 0 +} + +func (m *MsgGasPriceVoter) GetSupply() string { + if m != nil { + return m.Supply + } + return "" +} + +func init() { + proto.RegisterType((*MsgAddToOutTxTracker)(nil), "zetachain.zetacore.crosschain.MsgAddToOutTxTracker") + proto.RegisterType((*MsgAddToInTxTracker)(nil), "zetachain.zetacore.crosschain.MsgAddToInTxTracker") + proto.RegisterType((*MsgRemoveFromOutTxTracker)(nil), "zetachain.zetacore.crosschain.MsgRemoveFromOutTxTracker") + proto.RegisterType((*MsgVoteOnObservedOutboundTx)(nil), "zetachain.zetacore.crosschain.MsgVoteOnObservedOutboundTx") + proto.RegisterType((*MsgVoteOnObservedInboundTx)(nil), "zetachain.zetacore.crosschain.MsgVoteOnObservedInboundTx") + proto.RegisterType((*MsgGasPriceVoter)(nil), "zetachain.zetacore.crosschain.MsgGasPriceVoter") +} + +func init() { + proto.RegisterFile("zetachain/zetacore/crosschain/legacy_msgs.proto", fileDescriptor_246a7cc819884e07) +} + +var fileDescriptor_246a7cc819884e07 = []byte{ + // 979 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x56, 0x4f, 0x6f, 0xe3, 0x44, + 0x14, 0xaf, 0xdb, 0x34, 0x8d, 0x5f, 0x9a, 0x14, 0xbc, 0x85, 0x75, 0x53, 0x36, 0x2d, 0x11, 0x5b, + 0x2a, 0x44, 0x13, 0xa9, 0x08, 0x0e, 0x2b, 0x2e, 0xa4, 0x40, 0x37, 0x88, 0x6e, 0x56, 0x26, 0xcb, + 0x81, 0x8b, 0xe5, 0xd8, 0x13, 0x67, 0x94, 0x78, 0x26, 0xf2, 0x8c, 0x23, 0x67, 0xef, 0xdc, 0xb9, + 0xc1, 0x85, 0xef, 0xb3, 0xc7, 0x3d, 0x22, 0x0e, 0x15, 0x6a, 0xbf, 0x00, 0xe2, 0x13, 0x20, 0xbf, + 0x19, 0xb7, 0x49, 0xe9, 0x1f, 0x28, 0x42, 0x5c, 0xe2, 0x79, 0xcf, 0xef, 0xf7, 0xe6, 0xfd, 0xfb, + 0xbd, 0x18, 0x5a, 0x2f, 0x89, 0xf4, 0xfc, 0xa1, 0x47, 0x99, 0x3a, 0xf1, 0x98, 0xb4, 0xfc, 0x98, + 0x0b, 0xa1, 0x74, 0x63, 0x12, 0x7a, 0xfe, 0xcc, 0x8d, 0x44, 0x28, 0x9a, 0x93, 0x98, 0x4b, 0x6e, + 0x3d, 0xba, 0x00, 0x34, 0x73, 0x40, 0xf3, 0x12, 0x50, 0xdb, 0x0c, 0x79, 0xc8, 0xd1, 0xb2, 0x95, + 0x9d, 0x14, 0xa8, 0xf6, 0xc1, 0x35, 0xb7, 0x4c, 0x46, 0x61, 0x0b, 0x55, 0x42, 0x3f, 0xb4, 0xed, + 0xde, 0x4d, 0xb6, 0x9c, 0x32, 0xfc, 0xb9, 0xc3, 0xe7, 0x24, 0xe6, 0x7c, 0x20, 0xf4, 0x43, 0xdb, + 0x7e, 0x72, 0x7b, 0x96, 0xb1, 0x27, 0x89, 0x3b, 0xa6, 0x11, 0x95, 0x24, 0x76, 0x07, 0x63, 0x2f, + 0x4f, 0xb6, 0x76, 0x78, 0x3b, 0x0e, 0x8f, 0x2e, 0x9e, 0x5d, 0x99, 0x2a, 0x4c, 0xe3, 0x77, 0x03, + 0x36, 0x4f, 0x44, 0xf8, 0x59, 0x10, 0xf4, 0x78, 0x37, 0x91, 0xbd, 0xb4, 0x17, 0x7b, 0xfe, 0x88, + 0xc4, 0x96, 0x0d, 0x6b, 0x7e, 0x4c, 0x3c, 0xc9, 0x63, 0xdb, 0xd8, 0x35, 0xf6, 0x4d, 0x27, 0x17, + 0xad, 0x2d, 0x28, 0x29, 0x27, 0x34, 0xb0, 0x97, 0x77, 0x8d, 0xfd, 0x15, 0x67, 0x0d, 0xe5, 0x4e, + 0x60, 0x6d, 0xc2, 0x2a, 0xe3, 0xcc, 0x27, 0xf6, 0xca, 0xae, 0xb1, 0x5f, 0x70, 0x94, 0x60, 0x3d, + 0x84, 0x35, 0x99, 0xba, 0x43, 0x4f, 0x0c, 0xed, 0x02, 0xba, 0x2a, 0xca, 0xf4, 0xa9, 0x27, 0x86, + 0xd6, 0x13, 0x58, 0xc5, 0xc4, 0xed, 0xd5, 0x5d, 0x63, 0xbf, 0x7c, 0xf8, 0x5e, 0xf3, 0x9a, 0x6e, + 0x4d, 0x46, 0x61, 0x53, 0x57, 0xe7, 0x79, 0xf6, 0x70, 0x14, 0xc4, 0x7a, 0x04, 0xd0, 0x1f, 0x73, + 0x7f, 0xa4, 0xfc, 0x16, 0xd1, 0xaf, 0x89, 0x1a, 0x74, 0xbd, 0x05, 0x25, 0x99, 0xba, 0x94, 0x05, + 0x24, 0xb5, 0xd7, 0x54, 0x90, 0x32, 0xed, 0x64, 0x62, 0xe3, 0xe7, 0x65, 0x78, 0x90, 0xa7, 0xdc, + 0x61, 0xff, 0x32, 0xe3, 0xb9, 0xdc, 0x56, 0x16, 0x72, 0x6b, 0x83, 0x99, 0xb5, 0xdf, 0x95, 0xb3, + 0x09, 0xc1, 0xb4, 0xab, 0x87, 0x8f, 0x6f, 0xca, 0x0f, 0xe7, 0xe4, 0x88, 0x53, 0xd6, 0x9b, 0x4d, + 0x88, 0x53, 0xf2, 0xf5, 0xe9, 0x7f, 0xaa, 0xcf, 0x00, 0xb6, 0x4e, 0x44, 0xe8, 0x90, 0x88, 0x4f, + 0xc9, 0x97, 0x31, 0x8f, 0xfe, 0xa3, 0xb1, 0x68, 0x7c, 0x5f, 0x84, 0xed, 0x13, 0x11, 0x7e, 0xcb, + 0x25, 0xe9, 0xb2, 0x6e, 0x5f, 0x90, 0x78, 0x4a, 0x82, 0x6e, 0x22, 0xfb, 0x3c, 0x61, 0x41, 0x2f, + 0xbd, 0xe5, 0xaa, 0x6d, 0x30, 0x7d, 0x3f, 0x2f, 0xfb, 0x32, 0xbe, 0x2b, 0x65, 0x0a, 0xcc, 0xac, + 0x09, 0x0f, 0xb8, 0x76, 0xe6, 0xf2, 0x2c, 0xf4, 0xf9, 0xee, 0xbc, 0xc9, 0x2f, 0xef, 0xe9, 0x29, + 0xfb, 0x4f, 0xa1, 0x76, 0xc5, 0x5e, 0x55, 0x89, 0xd0, 0x70, 0x28, 0xb1, 0x73, 0x05, 0xc7, 0x5e, + 0x80, 0xb5, 0x2f, 0xdf, 0x5b, 0x1f, 0xc3, 0xc3, 0x2b, 0xe8, 0xd0, 0x13, 0x6e, 0x22, 0x48, 0x60, + 0x03, 0x42, 0x37, 0x17, 0xa0, 0xc7, 0x9e, 0x78, 0x21, 0x48, 0x60, 0xbd, 0x84, 0xc6, 0x15, 0x18, + 0x19, 0x0c, 0x88, 0x2f, 0xe9, 0x94, 0xa0, 0x83, 0x49, 0x4c, 0x7d, 0x62, 0x97, 0xb3, 0x98, 0xdb, + 0xcd, 0x57, 0xa7, 0x3b, 0x4b, 0xbf, 0x9e, 0xee, 0xec, 0x85, 0x54, 0x0e, 0x93, 0x7e, 0xd3, 0xe7, + 0x51, 0xcb, 0xe7, 0x22, 0xe2, 0x42, 0x3f, 0x0e, 0x44, 0x30, 0x6a, 0x65, 0x73, 0x26, 0x9a, 0x1d, + 0x26, 0x9d, 0xfa, 0xc2, 0x8d, 0x5f, 0xe4, 0x7e, 0x8f, 0x3d, 0xf1, 0x3c, 0xf3, 0x6a, 0x7d, 0x75, + 0xc7, 0xdd, 0xb8, 0x5a, 0xec, 0x75, 0x8c, 0xfe, 0x66, 0x5f, 0x5f, 0x67, 0x56, 0x16, 0x87, 0xea, + 0xd4, 0x1b, 0x27, 0xc4, 0x8d, 0x89, 0x4f, 0xe8, 0x94, 0x04, 0x38, 0xaa, 0x66, 0xfb, 0xa9, 0x8e, + 0xf9, 0xfd, 0xbf, 0x11, 0xf3, 0x0b, 0xca, 0xe4, 0x1f, 0xa7, 0x3b, 0x6f, 0xcd, 0xbc, 0x68, 0xfc, + 0xa4, 0xb1, 0xe8, 0xae, 0xe1, 0x54, 0x50, 0xe1, 0x68, 0xd9, 0xfa, 0x1c, 0x8a, 0x42, 0x7a, 0x32, + 0x11, 0x38, 0xd2, 0xd5, 0xc3, 0x0f, 0x6f, 0xe4, 0x94, 0xda, 0xd2, 0x1a, 0xf8, 0x0d, 0x62, 0x1c, + 0x8d, 0xb5, 0x76, 0xa0, 0xac, 0x32, 0x47, 0x2b, 0x4d, 0x00, 0x40, 0xd5, 0x51, 0xa6, 0xb1, 0xf6, + 0x60, 0x43, 0x19, 0x48, 0x21, 0x5c, 0x35, 0xbb, 0x25, 0x2c, 0x48, 0x05, 0xd5, 0x3d, 0x21, 0x9e, + 0xe1, 0x6a, 0x5b, 0x60, 0xb9, 0x79, 0x2f, 0x96, 0x37, 0x7e, 0x2c, 0x40, 0xed, 0x2f, 0x3c, 0xe8, + 0xb0, 0xbb, 0x69, 0xf0, 0x36, 0x14, 0x05, 0x61, 0x01, 0x89, 0x35, 0x07, 0xb4, 0x94, 0x05, 0xaf, + 0x4e, 0xee, 0x05, 0x21, 0x57, 0x30, 0xc3, 0x8a, 0x52, 0x1f, 0x69, 0x5a, 0xd6, 0xa0, 0xa4, 0xeb, + 0x1c, 0xeb, 0xc5, 0x7c, 0x21, 0x5b, 0x8f, 0xa1, 0x9a, 0x9f, 0x75, 0x91, 0x56, 0x95, 0x8b, 0x5c, + 0xab, 0xea, 0x74, 0x0c, 0x45, 0x2f, 0xe2, 0x09, 0x93, 0x6a, 0xc3, 0xb4, 0x5b, 0xff, 0xb0, 0xef, + 0x8e, 0x86, 0x67, 0x59, 0x46, 0x44, 0x08, 0x2f, 0x54, 0x85, 0x36, 0x9d, 0x5c, 0xb4, 0xde, 0x01, + 0xc0, 0x3f, 0x2c, 0x45, 0x63, 0x53, 0xc5, 0x49, 0x99, 0x66, 0xef, 0x1e, 0x6c, 0x50, 0xe6, 0xea, + 0x4d, 0xa7, 0x28, 0xab, 0x78, 0x57, 0xa1, 0x6c, 0x9e, 0xa7, 0xdb, 0x60, 0x5e, 0xce, 0x76, 0x19, + 0x2d, 0x4a, 0x61, 0x3e, 0xc5, 0x0b, 0x5d, 0x5c, 0xbf, 0xdf, 0xae, 0xde, 0x06, 0x53, 0xa6, 0x2e, + 0x8f, 0x69, 0x48, 0x99, 0x5d, 0x51, 0x51, 0xca, 0xb4, 0x8b, 0x72, 0xb6, 0x00, 0x3d, 0x21, 0x88, + 0xb4, 0xab, 0xf8, 0x42, 0x09, 0xd9, 0x14, 0x92, 0x29, 0x61, 0x52, 0xaf, 0xe1, 0x0d, 0x8c, 0x0a, + 0x50, 0xa5, 0x36, 0xf1, 0x4f, 0x06, 0xbc, 0x71, 0x22, 0xc2, 0x9c, 0xb9, 0xd9, 0x84, 0xdc, 0x7f, + 0x03, 0xab, 0x95, 0xa2, 0x37, 0x30, 0x0a, 0xd6, 0xbb, 0xb0, 0xae, 0x2a, 0xc7, 0x92, 0xa8, 0xaf, + 0x87, 0xa0, 0xe0, 0x94, 0x51, 0xf7, 0x0c, 0x55, 0x38, 0x63, 0xc9, 0x64, 0x32, 0x9e, 0x29, 0x62, + 0x3b, 0x5a, 0x6a, 0x1f, 0xbf, 0x3a, 0xab, 0x1b, 0xaf, 0xcf, 0xea, 0xc6, 0x6f, 0x67, 0x75, 0xe3, + 0x87, 0xf3, 0xfa, 0xd2, 0xeb, 0xf3, 0xfa, 0xd2, 0x2f, 0xe7, 0xf5, 0xa5, 0xef, 0x0e, 0xe6, 0x5a, + 0x9f, 0x55, 0xee, 0x40, 0x7d, 0x7d, 0x30, 0x1e, 0x90, 0x56, 0x3a, 0xff, 0x3d, 0x82, 0x53, 0xd0, + 0x2f, 0xe2, 0x77, 0xc8, 0x47, 0x7f, 0x06, 0x00, 0x00, 0xff, 0xff, 0x59, 0xa5, 0x4e, 0x23, 0xdb, + 0x09, 0x00, 0x00, +} + +func (m *MsgAddToOutTxTracker) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgAddToOutTxTracker) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgAddToOutTxTracker) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.TxIndex != 0 { + i = encodeVarintLegacyMsgs(dAtA, i, uint64(m.TxIndex)) + i-- + dAtA[i] = 0x38 + } + if len(m.BlockHash) > 0 { + i -= len(m.BlockHash) + copy(dAtA[i:], m.BlockHash) + i = encodeVarintLegacyMsgs(dAtA, i, uint64(len(m.BlockHash))) + i-- + dAtA[i] = 0x32 + } + if m.Proof != nil { + { + size, err := m.Proof.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintLegacyMsgs(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if len(m.TxHash) > 0 { + i -= len(m.TxHash) + copy(dAtA[i:], m.TxHash) + i = encodeVarintLegacyMsgs(dAtA, i, uint64(len(m.TxHash))) + i-- + dAtA[i] = 0x22 + } + if m.Nonce != 0 { + i = encodeVarintLegacyMsgs(dAtA, i, uint64(m.Nonce)) + i-- + dAtA[i] = 0x18 + } + if m.ChainId != 0 { + i = encodeVarintLegacyMsgs(dAtA, i, uint64(m.ChainId)) + i-- + dAtA[i] = 0x10 + } + if len(m.Creator) > 0 { + i -= len(m.Creator) + copy(dAtA[i:], m.Creator) + i = encodeVarintLegacyMsgs(dAtA, i, uint64(len(m.Creator))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgAddToInTxTracker) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgAddToInTxTracker) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgAddToInTxTracker) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.TxIndex != 0 { + i = encodeVarintLegacyMsgs(dAtA, i, uint64(m.TxIndex)) + i-- + dAtA[i] = 0x38 + } + if len(m.BlockHash) > 0 { + i -= len(m.BlockHash) + copy(dAtA[i:], m.BlockHash) + i = encodeVarintLegacyMsgs(dAtA, i, uint64(len(m.BlockHash))) + i-- + dAtA[i] = 0x32 + } + if m.Proof != nil { + { + size, err := m.Proof.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintLegacyMsgs(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if m.CoinType != 0 { + i = encodeVarintLegacyMsgs(dAtA, i, uint64(m.CoinType)) + i-- + dAtA[i] = 0x20 + } + if len(m.TxHash) > 0 { + i -= len(m.TxHash) + copy(dAtA[i:], m.TxHash) + i = encodeVarintLegacyMsgs(dAtA, i, uint64(len(m.TxHash))) + i-- + dAtA[i] = 0x1a + } + if m.ChainId != 0 { + i = encodeVarintLegacyMsgs(dAtA, i, uint64(m.ChainId)) + i-- + dAtA[i] = 0x10 + } + if len(m.Creator) > 0 { + i -= len(m.Creator) + copy(dAtA[i:], m.Creator) + i = encodeVarintLegacyMsgs(dAtA, i, uint64(len(m.Creator))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgRemoveFromOutTxTracker) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgRemoveFromOutTxTracker) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgRemoveFromOutTxTracker) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Nonce != 0 { + i = encodeVarintLegacyMsgs(dAtA, i, uint64(m.Nonce)) + i-- + dAtA[i] = 0x18 + } + if m.ChainId != 0 { + i = encodeVarintLegacyMsgs(dAtA, i, uint64(m.ChainId)) + i-- + dAtA[i] = 0x10 + } + if len(m.Creator) > 0 { + i -= len(m.Creator) + copy(dAtA[i:], m.Creator) + i = encodeVarintLegacyMsgs(dAtA, i, uint64(len(m.Creator))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgVoteOnObservedOutboundTx) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgVoteOnObservedOutboundTx) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgVoteOnObservedOutboundTx) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ObservedOutTxEffectiveGasLimit != 0 { + i = encodeVarintLegacyMsgs(dAtA, i, uint64(m.ObservedOutTxEffectiveGasLimit)) + i-- + dAtA[i] = 0x60 + } + { + size := m.ObservedOutTxEffectiveGasPrice.Size() + i -= size + if _, err := m.ObservedOutTxEffectiveGasPrice.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintLegacyMsgs(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x5a + if m.ObservedOutTxGasUsed != 0 { + i = encodeVarintLegacyMsgs(dAtA, i, uint64(m.ObservedOutTxGasUsed)) + i-- + dAtA[i] = 0x50 + } + if m.CoinType != 0 { + i = encodeVarintLegacyMsgs(dAtA, i, uint64(m.CoinType)) + i-- + dAtA[i] = 0x48 + } + if m.OutTxTssNonce != 0 { + i = encodeVarintLegacyMsgs(dAtA, i, uint64(m.OutTxTssNonce)) + i-- + dAtA[i] = 0x40 + } + if m.OutTxChain != 0 { + i = encodeVarintLegacyMsgs(dAtA, i, uint64(m.OutTxChain)) + i-- + dAtA[i] = 0x38 + } + if m.Status != 0 { + i = encodeVarintLegacyMsgs(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x30 + } + { + size := m.ValueReceived.Size() + i -= size + if _, err := m.ValueReceived.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintLegacyMsgs(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + if m.ObservedOutTxBlockHeight != 0 { + i = encodeVarintLegacyMsgs(dAtA, i, uint64(m.ObservedOutTxBlockHeight)) + i-- + dAtA[i] = 0x20 + } + if len(m.ObservedOutTxHash) > 0 { + i -= len(m.ObservedOutTxHash) + copy(dAtA[i:], m.ObservedOutTxHash) + i = encodeVarintLegacyMsgs(dAtA, i, uint64(len(m.ObservedOutTxHash))) + i-- + dAtA[i] = 0x1a + } + if len(m.CctxHash) > 0 { + i -= len(m.CctxHash) + copy(dAtA[i:], m.CctxHash) + i = encodeVarintLegacyMsgs(dAtA, i, uint64(len(m.CctxHash))) + i-- + dAtA[i] = 0x12 + } + if len(m.Creator) > 0 { + i -= len(m.Creator) + copy(dAtA[i:], m.Creator) + i = encodeVarintLegacyMsgs(dAtA, i, uint64(len(m.Creator))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgVoteOnObservedInboundTx) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgVoteOnObservedInboundTx) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgVoteOnObservedInboundTx) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.EventIndex != 0 { + i = encodeVarintLegacyMsgs(dAtA, i, uint64(m.EventIndex)) + i-- + dAtA[i] = 0x78 + } + if len(m.Asset) > 0 { + i -= len(m.Asset) + copy(dAtA[i:], m.Asset) + i = encodeVarintLegacyMsgs(dAtA, i, uint64(len(m.Asset))) + i-- + dAtA[i] = 0x72 + } + if len(m.TxOrigin) > 0 { + i -= len(m.TxOrigin) + copy(dAtA[i:], m.TxOrigin) + i = encodeVarintLegacyMsgs(dAtA, i, uint64(len(m.TxOrigin))) + i-- + dAtA[i] = 0x6a + } + if m.CoinType != 0 { + i = encodeVarintLegacyMsgs(dAtA, i, uint64(m.CoinType)) + i-- + dAtA[i] = 0x60 + } + if m.GasLimit != 0 { + i = encodeVarintLegacyMsgs(dAtA, i, uint64(m.GasLimit)) + i-- + dAtA[i] = 0x58 + } + if m.InBlockHeight != 0 { + i = encodeVarintLegacyMsgs(dAtA, i, uint64(m.InBlockHeight)) + i-- + dAtA[i] = 0x50 + } + if len(m.InTxHash) > 0 { + i -= len(m.InTxHash) + copy(dAtA[i:], m.InTxHash) + i = encodeVarintLegacyMsgs(dAtA, i, uint64(len(m.InTxHash))) + i-- + dAtA[i] = 0x4a + } + if len(m.Message) > 0 { + i -= len(m.Message) + copy(dAtA[i:], m.Message) + i = encodeVarintLegacyMsgs(dAtA, i, uint64(len(m.Message))) + i-- + dAtA[i] = 0x42 + } + { + size := m.Amount.Size() + i -= size + if _, err := m.Amount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintLegacyMsgs(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + if m.ReceiverChain != 0 { + i = encodeVarintLegacyMsgs(dAtA, i, uint64(m.ReceiverChain)) + i-- + dAtA[i] = 0x28 + } + if len(m.Receiver) > 0 { + i -= len(m.Receiver) + copy(dAtA[i:], m.Receiver) + i = encodeVarintLegacyMsgs(dAtA, i, uint64(len(m.Receiver))) + i-- + dAtA[i] = 0x22 + } + if m.SenderChainId != 0 { + i = encodeVarintLegacyMsgs(dAtA, i, uint64(m.SenderChainId)) + i-- + dAtA[i] = 0x18 + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintLegacyMsgs(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0x12 + } + if len(m.Creator) > 0 { + i -= len(m.Creator) + copy(dAtA[i:], m.Creator) + i = encodeVarintLegacyMsgs(dAtA, i, uint64(len(m.Creator))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgGasPriceVoter) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgGasPriceVoter) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgGasPriceVoter) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Supply) > 0 { + i -= len(m.Supply) + copy(dAtA[i:], m.Supply) + i = encodeVarintLegacyMsgs(dAtA, i, uint64(len(m.Supply))) + i-- + dAtA[i] = 0x2a + } + if m.BlockNumber != 0 { + i = encodeVarintLegacyMsgs(dAtA, i, uint64(m.BlockNumber)) + i-- + dAtA[i] = 0x20 + } + if m.Price != 0 { + i = encodeVarintLegacyMsgs(dAtA, i, uint64(m.Price)) + i-- + dAtA[i] = 0x18 + } + if m.ChainId != 0 { + i = encodeVarintLegacyMsgs(dAtA, i, uint64(m.ChainId)) + i-- + dAtA[i] = 0x10 + } + if len(m.Creator) > 0 { + i -= len(m.Creator) + copy(dAtA[i:], m.Creator) + i = encodeVarintLegacyMsgs(dAtA, i, uint64(len(m.Creator))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintLegacyMsgs(dAtA []byte, offset int, v uint64) int { + offset -= sovLegacyMsgs(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgAddToOutTxTracker) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Creator) + if l > 0 { + n += 1 + l + sovLegacyMsgs(uint64(l)) + } + if m.ChainId != 0 { + n += 1 + sovLegacyMsgs(uint64(m.ChainId)) + } + if m.Nonce != 0 { + n += 1 + sovLegacyMsgs(uint64(m.Nonce)) + } + l = len(m.TxHash) + if l > 0 { + n += 1 + l + sovLegacyMsgs(uint64(l)) + } + if m.Proof != nil { + l = m.Proof.Size() + n += 1 + l + sovLegacyMsgs(uint64(l)) + } + l = len(m.BlockHash) + if l > 0 { + n += 1 + l + sovLegacyMsgs(uint64(l)) + } + if m.TxIndex != 0 { + n += 1 + sovLegacyMsgs(uint64(m.TxIndex)) + } + return n +} + +func (m *MsgAddToInTxTracker) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Creator) + if l > 0 { + n += 1 + l + sovLegacyMsgs(uint64(l)) + } + if m.ChainId != 0 { + n += 1 + sovLegacyMsgs(uint64(m.ChainId)) + } + l = len(m.TxHash) + if l > 0 { + n += 1 + l + sovLegacyMsgs(uint64(l)) + } + if m.CoinType != 0 { + n += 1 + sovLegacyMsgs(uint64(m.CoinType)) + } + if m.Proof != nil { + l = m.Proof.Size() + n += 1 + l + sovLegacyMsgs(uint64(l)) + } + l = len(m.BlockHash) + if l > 0 { + n += 1 + l + sovLegacyMsgs(uint64(l)) + } + if m.TxIndex != 0 { + n += 1 + sovLegacyMsgs(uint64(m.TxIndex)) + } + return n +} + +func (m *MsgRemoveFromOutTxTracker) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Creator) + if l > 0 { + n += 1 + l + sovLegacyMsgs(uint64(l)) + } + if m.ChainId != 0 { + n += 1 + sovLegacyMsgs(uint64(m.ChainId)) + } + if m.Nonce != 0 { + n += 1 + sovLegacyMsgs(uint64(m.Nonce)) + } + return n +} + +func (m *MsgVoteOnObservedOutboundTx) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Creator) + if l > 0 { + n += 1 + l + sovLegacyMsgs(uint64(l)) + } + l = len(m.CctxHash) + if l > 0 { + n += 1 + l + sovLegacyMsgs(uint64(l)) + } + l = len(m.ObservedOutTxHash) + if l > 0 { + n += 1 + l + sovLegacyMsgs(uint64(l)) + } + if m.ObservedOutTxBlockHeight != 0 { + n += 1 + sovLegacyMsgs(uint64(m.ObservedOutTxBlockHeight)) + } + l = m.ValueReceived.Size() + n += 1 + l + sovLegacyMsgs(uint64(l)) + if m.Status != 0 { + n += 1 + sovLegacyMsgs(uint64(m.Status)) + } + if m.OutTxChain != 0 { + n += 1 + sovLegacyMsgs(uint64(m.OutTxChain)) + } + if m.OutTxTssNonce != 0 { + n += 1 + sovLegacyMsgs(uint64(m.OutTxTssNonce)) + } + if m.CoinType != 0 { + n += 1 + sovLegacyMsgs(uint64(m.CoinType)) + } + if m.ObservedOutTxGasUsed != 0 { + n += 1 + sovLegacyMsgs(uint64(m.ObservedOutTxGasUsed)) + } + l = m.ObservedOutTxEffectiveGasPrice.Size() + n += 1 + l + sovLegacyMsgs(uint64(l)) + if m.ObservedOutTxEffectiveGasLimit != 0 { + n += 1 + sovLegacyMsgs(uint64(m.ObservedOutTxEffectiveGasLimit)) + } + return n +} + +func (m *MsgVoteOnObservedInboundTx) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Creator) + if l > 0 { + n += 1 + l + sovLegacyMsgs(uint64(l)) + } + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovLegacyMsgs(uint64(l)) + } + if m.SenderChainId != 0 { + n += 1 + sovLegacyMsgs(uint64(m.SenderChainId)) + } + l = len(m.Receiver) + if l > 0 { + n += 1 + l + sovLegacyMsgs(uint64(l)) + } + if m.ReceiverChain != 0 { + n += 1 + sovLegacyMsgs(uint64(m.ReceiverChain)) + } + l = m.Amount.Size() + n += 1 + l + sovLegacyMsgs(uint64(l)) + l = len(m.Message) + if l > 0 { + n += 1 + l + sovLegacyMsgs(uint64(l)) + } + l = len(m.InTxHash) + if l > 0 { + n += 1 + l + sovLegacyMsgs(uint64(l)) + } + if m.InBlockHeight != 0 { + n += 1 + sovLegacyMsgs(uint64(m.InBlockHeight)) + } + if m.GasLimit != 0 { + n += 1 + sovLegacyMsgs(uint64(m.GasLimit)) + } + if m.CoinType != 0 { + n += 1 + sovLegacyMsgs(uint64(m.CoinType)) + } + l = len(m.TxOrigin) + if l > 0 { + n += 1 + l + sovLegacyMsgs(uint64(l)) + } + l = len(m.Asset) + if l > 0 { + n += 1 + l + sovLegacyMsgs(uint64(l)) + } + if m.EventIndex != 0 { + n += 1 + sovLegacyMsgs(uint64(m.EventIndex)) + } + return n +} + +func (m *MsgGasPriceVoter) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Creator) + if l > 0 { + n += 1 + l + sovLegacyMsgs(uint64(l)) + } + if m.ChainId != 0 { + n += 1 + sovLegacyMsgs(uint64(m.ChainId)) + } + if m.Price != 0 { + n += 1 + sovLegacyMsgs(uint64(m.Price)) + } + if m.BlockNumber != 0 { + n += 1 + sovLegacyMsgs(uint64(m.BlockNumber)) + } + l = len(m.Supply) + if l > 0 { + n += 1 + l + sovLegacyMsgs(uint64(l)) + } + return n +} + +func sovLegacyMsgs(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozLegacyMsgs(x uint64) (n int) { + return sovLegacyMsgs(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgAddToOutTxTracker) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgAddToOutTxTracker: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgAddToOutTxTracker: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Creator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLegacyMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLegacyMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Creator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + m.ChainId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ChainId |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Nonce", wireType) + } + m.Nonce = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Nonce |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TxHash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLegacyMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLegacyMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TxHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthLegacyMsgs + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLegacyMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Proof == nil { + m.Proof = &proofs.Proof{} + } + if err := m.Proof.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockHash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLegacyMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLegacyMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BlockHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TxIndex", wireType) + } + m.TxIndex = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TxIndex |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipLegacyMsgs(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthLegacyMsgs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgAddToInTxTracker) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgAddToInTxTracker: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgAddToInTxTracker: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Creator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLegacyMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLegacyMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Creator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + m.ChainId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ChainId |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TxHash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLegacyMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLegacyMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TxHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CoinType", wireType) + } + m.CoinType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CoinType |= coin.CoinType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthLegacyMsgs + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLegacyMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Proof == nil { + m.Proof = &proofs.Proof{} + } + if err := m.Proof.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockHash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLegacyMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLegacyMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BlockHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TxIndex", wireType) + } + m.TxIndex = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TxIndex |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipLegacyMsgs(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthLegacyMsgs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgRemoveFromOutTxTracker) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgRemoveFromOutTxTracker: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgRemoveFromOutTxTracker: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Creator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLegacyMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLegacyMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Creator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + m.ChainId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ChainId |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Nonce", wireType) + } + m.Nonce = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Nonce |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipLegacyMsgs(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthLegacyMsgs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgVoteOnObservedOutboundTx) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgVoteOnObservedOutboundTx: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgVoteOnObservedOutboundTx: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Creator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLegacyMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLegacyMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Creator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CctxHash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLegacyMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLegacyMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CctxHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ObservedOutTxHash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLegacyMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLegacyMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ObservedOutTxHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ObservedOutTxBlockHeight", wireType) + } + m.ObservedOutTxBlockHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ObservedOutTxBlockHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValueReceived", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLegacyMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLegacyMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ValueReceived.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + m.Status = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Status |= chains.ReceiveStatus(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OutTxChain", wireType) + } + m.OutTxChain = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OutTxChain |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OutTxTssNonce", wireType) + } + m.OutTxTssNonce = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OutTxTssNonce |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CoinType", wireType) + } + m.CoinType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CoinType |= coin.CoinType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ObservedOutTxGasUsed", wireType) + } + m.ObservedOutTxGasUsed = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ObservedOutTxGasUsed |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ObservedOutTxEffectiveGasPrice", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLegacyMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLegacyMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ObservedOutTxEffectiveGasPrice.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ObservedOutTxEffectiveGasLimit", wireType) + } + m.ObservedOutTxEffectiveGasLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ObservedOutTxEffectiveGasLimit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipLegacyMsgs(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthLegacyMsgs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgVoteOnObservedInboundTx) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgVoteOnObservedInboundTx: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgVoteOnObservedInboundTx: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Creator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLegacyMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLegacyMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Creator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLegacyMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLegacyMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SenderChainId", wireType) + } + m.SenderChainId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SenderChainId |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Receiver", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLegacyMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLegacyMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Receiver = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ReceiverChain", wireType) + } + m.ReceiverChain = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ReceiverChain |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLegacyMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLegacyMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLegacyMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLegacyMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Message = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InTxHash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLegacyMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLegacyMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.InTxHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field InBlockHeight", wireType) + } + m.InBlockHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.InBlockHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field GasLimit", wireType) + } + m.GasLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.GasLimit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 12: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CoinType", wireType) + } + m.CoinType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CoinType |= coin.CoinType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TxOrigin", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLegacyMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLegacyMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TxOrigin = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 14: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Asset", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLegacyMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLegacyMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Asset = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 15: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field EventIndex", wireType) + } + m.EventIndex = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.EventIndex |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipLegacyMsgs(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthLegacyMsgs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgGasPriceVoter) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgGasPriceVoter: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgGasPriceVoter: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Creator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLegacyMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLegacyMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Creator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + m.ChainId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ChainId |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Price", wireType) + } + m.Price = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Price |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockNumber", wireType) + } + m.BlockNumber = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BlockNumber |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Supply", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLegacyMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLegacyMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Supply = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipLegacyMsgs(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthLegacyMsgs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipLegacyMsgs(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowLegacyMsgs + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthLegacyMsgs + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupLegacyMsgs + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthLegacyMsgs + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthLegacyMsgs = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowLegacyMsgs = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupLegacyMsgs = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/crosschain/types/message_vote_inbound.go b/x/crosschain/types/message_vote_inbound.go index c73c9a24d4..34afb68be2 100644 --- a/x/crosschain/types/message_vote_inbound.go +++ b/x/crosschain/types/message_vote_inbound.go @@ -38,6 +38,13 @@ func WithEVMRevertOptions(revertOptions gatewayevm.RevertOptions) InboundVoteOpt } } +// WithCrossChainCall sets the cross chain call to true for the inbound vote message +func WithCrossChainCall(isCrossChainCall bool) InboundVoteOption { + return func(msg *MsgVoteInbound) { + msg.IsCrossChainCall = isCrossChainCall + } +} + var _ sdk.Msg = &MsgVoteInbound{} func NewMsgVoteInbound( @@ -79,6 +86,7 @@ func NewMsgVoteInbound( EventIndex: uint64(eventIndex), ProtocolContractVersion: protocolContractVersion, RevertOptions: NewEmptyRevertOptions(), + IsCrossChainCall: false, } for _, option := range options { diff --git a/x/crosschain/types/message_vote_inbound_test.go b/x/crosschain/types/message_vote_inbound_test.go index 77631f20fd..e3e9bc7fce 100644 --- a/x/crosschain/types/message_vote_inbound_test.go +++ b/x/crosschain/types/message_vote_inbound_test.go @@ -184,6 +184,71 @@ func TestNewMsgVoteInbound(t *testing.T) { RevertGasLimit: math.ZeroUint(), }, msg.RevertOptions) }) + + t.Run("can set is cross chain call options", func(t *testing.T) { + // false by default + msg := types.NewMsgVoteInbound( + sample.AccAddress(), + sample.AccAddress(), + 42, + sample.String(), + sample.String(), + 42, + math.NewUint(42), + sample.String(), + sample.String(), + 42, + 42, + coin.CoinType_Zeta, + sample.String(), + 42, + types.ProtocolContractVersion_V1, + true, + ) + require.False(t, msg.IsCrossChainCall) + + msg = types.NewMsgVoteInbound( + sample.AccAddress(), + sample.AccAddress(), + 42, + sample.String(), + sample.String(), + 42, + math.NewUint(42), + sample.String(), + sample.String(), + 42, + 42, + coin.CoinType_Zeta, + sample.String(), + 42, + types.ProtocolContractVersion_V1, + true, + types.WithCrossChainCall(true), + ) + require.True(t, msg.IsCrossChainCall) + + msg = types.NewMsgVoteInbound( + sample.AccAddress(), + sample.AccAddress(), + 42, + sample.String(), + sample.String(), + 42, + math.NewUint(42), + sample.String(), + sample.String(), + 42, + 42, + coin.CoinType_Zeta, + sample.String(), + 42, + types.ProtocolContractVersion_V1, + true, + types.WithCrossChainCall(false), + ) + require.False(t, msg.IsCrossChainCall) + }) } func TestMsgVoteInbound_ValidateBasic(t *testing.T) { diff --git a/x/crosschain/types/rate_limiter_flags_test.go b/x/crosschain/types/rate_limiter_flags_test.go index 8531a34105..545ef708a7 100644 --- a/x/crosschain/types/rate_limiter_flags_test.go +++ b/x/crosschain/types/rate_limiter_flags_test.go @@ -6,7 +6,6 @@ import ( "testing" "cosmossdk.io/math" - sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" @@ -263,7 +262,7 @@ func TestConvertCctxValue(t *testing.T) { erc20AssetRates map[int64]map[string]types.AssetRate // output - expectedValue sdkmath.Int + expectedValue math.Int }{ { name: "should convert cctx ZETA value correctly", diff --git a/x/crosschain/types/status.go b/x/crosschain/types/status.go index cdaf5b0aae..00c08bf6f7 100644 --- a/x/crosschain/types/status.go +++ b/x/crosschain/types/status.go @@ -9,28 +9,45 @@ func (m *Status) AbortRefunded() { m.StatusMessage = "CCTX aborted and Refunded" } -// ChangeStatus changes the status of the cross chain transaction -// empty msg does not overwrite old status message -func (m *Status) ChangeStatus(newStatus CctxStatus, msg string) { - if len(msg) > 0 { - if m.StatusMessage != "" { - m.StatusMessage = fmt.Sprintf("%s : %s", m.StatusMessage, msg) - } else { - m.StatusMessage = msg - } +// UpdateStatusAndErrorMessages transitions the Status and Error messages. +func (m *Status) UpdateStatusAndErrorMessages(newStatus CctxStatus, statusMsg, errorMsg string) { + m.UpdateStatus(newStatus, statusMsg) + + if newStatus == CctxStatus_Aborted || newStatus == CctxStatus_Reverted || newStatus == CctxStatus_PendingRevert { + m.UpdateErrorMessage(errorMsg) } - if !m.ValidateTransition(newStatus) { +} + +// UpdateStatus updates the cctx status and cctx.status.status_message. +func (m *Status) UpdateStatus(newStatus CctxStatus, statusMsg string) { + if m.ValidateTransition(newStatus) { + m.StatusMessage = fmt.Sprintf("Status changed from %s to %s", m.Status.String(), newStatus.String()) + m.Status = newStatus + } else { m.StatusMessage = fmt.Sprintf( - "Failed to transition : OldStatus %s , NewStatus %s , MSG : %s :", + "Failed to transition status from %s to %s", m.Status.String(), newStatus.String(), - msg, ) + m.Status = CctxStatus_Aborted - return } - m.Status = newStatus -} //nolint:typecheck + + if statusMsg != "" { + m.StatusMessage += fmt.Sprintf(": %s", statusMsg) + } +} + +// UpdateErrorMessage updates cctx.status.error_message. +func (m *Status) UpdateErrorMessage(errorMsg string) { + errMsg := errorMsg + + if errMsg == "" { + errMsg = "unknown error" + } + + m.ErrorMessage = errMsg +} func (m *Status) ValidateTransition(newStatus CctxStatus) bool { stateTransitionMap := stateTransitionMap() diff --git a/x/crosschain/types/status_test.go b/x/crosschain/types/status_test.go index 88013a41a9..5bb272d522 100644 --- a/x/crosschain/types/status_test.go +++ b/x/crosschain/types/status_test.go @@ -140,31 +140,35 @@ func TestStatus_ChangeStatus(t *testing.T) { t.Run("should change status and msg if transition is valid", func(t *testing.T) { s := types.Status{Status: types.CctxStatus_PendingInbound} - s.ChangeStatus(types.CctxStatus_PendingOutbound, "msg") + s.UpdateStatus(types.CctxStatus_PendingOutbound, "msg") assert.Equal(t, s.Status, types.CctxStatus_PendingOutbound) - assert.Equal(t, s.StatusMessage, "msg") + assert.Equal(t, s.StatusMessage, "Status changed from PendingInbound to PendingOutbound: msg") }) t.Run("should change status if transition is valid", func(t *testing.T) { s := types.Status{Status: types.CctxStatus_PendingInbound} - s.ChangeStatus(types.CctxStatus_PendingOutbound, "") + s.UpdateStatus(types.CctxStatus_PendingOutbound, "") + fmt.Printf("%+v\n", s) assert.Equal(t, s.Status, types.CctxStatus_PendingOutbound) - assert.Equal(t, s.StatusMessage, "") + assert.Equal(t, s.StatusMessage, fmt.Sprintf( + "Status changed from %s to %s", + types.CctxStatus_PendingInbound.String(), + types.CctxStatus_PendingOutbound.String()), + ) }) t.Run("should change status to aborted and msg if transition is invalid", func(t *testing.T) { s := types.Status{Status: types.CctxStatus_PendingOutbound} - s.ChangeStatus(types.CctxStatus_PendingInbound, "msg") + s.UpdateStatus(types.CctxStatus_PendingInbound, "msg") assert.Equal(t, s.Status, types.CctxStatus_Aborted) assert.Equal( t, fmt.Sprintf( - "Failed to transition : OldStatus %s , NewStatus %s , MSG : %s :", + "Failed to transition status from %s to %s: msg", types.CctxStatus_PendingOutbound.String(), types.CctxStatus_PendingInbound.String(), - "msg", ), s.StatusMessage, ) diff --git a/x/crosschain/types/tx.pb.go b/x/crosschain/types/tx.pb.go index 7496f74404..ffe5c8cec7 100644 --- a/x/crosschain/types/tx.pb.go +++ b/x/crosschain/types/tx.pb.go @@ -719,6 +719,7 @@ type MsgVoteGasPrice struct { Price uint64 `protobuf:"varint,3,opt,name=price,proto3" json:"price,omitempty"` PriorityFee uint64 `protobuf:"varint,6,opt,name=priority_fee,json=priorityFee,proto3" json:"priority_fee,omitempty"` BlockNumber uint64 `protobuf:"varint,4,opt,name=block_number,json=blockNumber,proto3" json:"block_number,omitempty"` + Supply string `protobuf:"bytes,5,opt,name=supply,proto3" json:"supply,omitempty"` // Deprecated: Do not use. } func (m *MsgVoteGasPrice) Reset() { *m = MsgVoteGasPrice{} } @@ -789,6 +790,14 @@ func (m *MsgVoteGasPrice) GetBlockNumber() uint64 { return 0 } +// Deprecated: Do not use. +func (m *MsgVoteGasPrice) GetSupply() string { + if m != nil { + return m.Supply + } + return "" +} + type MsgVoteGasPriceResponse struct { } @@ -1003,6 +1012,8 @@ type MsgVoteInbound struct { // revert options provided by the sender RevertOptions RevertOptions `protobuf:"bytes,17,opt,name=revert_options,json=revertOptions,proto3" json:"revert_options"` CallOptions *CallOptions `protobuf:"bytes,18,opt,name=call_options,json=callOptions,proto3" json:"call_options,omitempty"` + // define if a smart contract call should be made with asset + IsCrossChainCall bool `protobuf:"varint,19,opt,name=is_cross_chain_call,json=isCrossChainCall,proto3" json:"is_cross_chain_call,omitempty"` } func (m *MsgVoteInbound) Reset() { *m = MsgVoteInbound{} } @@ -1150,6 +1161,13 @@ func (m *MsgVoteInbound) GetCallOptions() *CallOptions { return nil } +func (m *MsgVoteInbound) GetIsCrossChainCall() bool { + if m != nil { + return m.IsCrossChainCall + } + return false +} + type MsgVoteInboundResponse struct { } @@ -1715,120 +1733,122 @@ func init() { } var fileDescriptor_15f0860550897740 = []byte{ - // 1807 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x59, 0x4f, 0x6f, 0x23, 0x49, - 0x15, 0x4f, 0x6f, 0x1c, 0xc7, 0x7e, 0x4e, 0x9c, 0xa4, 0x37, 0x93, 0x38, 0x9d, 0x8d, 0x93, 0xf1, - 0x30, 0x21, 0x5a, 0x4d, 0xec, 0xe0, 0x59, 0x86, 0x25, 0x8b, 0x58, 0x26, 0xde, 0x9d, 0x6c, 0xd0, - 0x78, 0x26, 0xea, 0xcd, 0x2c, 0x7f, 0x2e, 0xad, 0x76, 0x77, 0xa5, 0xd3, 0x8a, 0xdd, 0x65, 0x75, - 0x95, 0xbd, 0xce, 0x08, 0x09, 0x84, 0x84, 0xc4, 0x11, 0x10, 0xa7, 0x3d, 0x70, 0x43, 0x82, 0x6f, - 0xc0, 0x47, 0xd8, 0xe3, 0x88, 0x13, 0xe2, 0x30, 0x42, 0x33, 0x5f, 0x00, 0xb8, 0x72, 0x41, 0xfd, - 0xaa, 0xba, 0x63, 0xb7, 0xff, 0xc6, 0x11, 0xe2, 0x92, 0xee, 0x7a, 0xfd, 0x7e, 0xef, 0x5f, 0xbd, - 0xaa, 0xf7, 0x9e, 0x03, 0xbb, 0x2f, 0x09, 0x37, 0xad, 0x0b, 0xd3, 0xf5, 0x4a, 0xf8, 0x46, 0x7d, - 0x52, 0xb2, 0x7c, 0xca, 0x98, 0xa0, 0xf1, 0x4e, 0xb1, 0xe9, 0x53, 0x4e, 0xd5, 0xad, 0x88, 0xaf, - 0x18, 0xf2, 0x15, 0xaf, 0xf9, 0xb4, 0x55, 0x87, 0x3a, 0x14, 0x39, 0x4b, 0xc1, 0x9b, 0x00, 0x69, - 0xef, 0x0f, 0x10, 0xde, 0xbc, 0x74, 0x4a, 0x48, 0x62, 0xf2, 0x21, 0x79, 0x77, 0x87, 0xf1, 0x52, - 0xd7, 0xc3, 0x3f, 0x63, 0x64, 0x36, 0x7d, 0x4a, 0xcf, 0x99, 0x7c, 0x48, 0xde, 0x47, 0xa3, 0x9d, - 0xf3, 0x4d, 0x4e, 0x8c, 0xba, 0xdb, 0x70, 0x39, 0xf1, 0x8d, 0xf3, 0xba, 0xe9, 0x84, 0xb8, 0xf2, - 0x68, 0x1c, 0xbe, 0x1a, 0xf8, 0x6e, 0x84, 0x01, 0x2a, 0xfc, 0x4e, 0x01, 0xb5, 0xca, 0x9c, 0xaa, - 0xeb, 0x04, 0x62, 0xcf, 0x18, 0x7b, 0xd2, 0xf2, 0x6c, 0xa6, 0xe6, 0x60, 0xde, 0xf2, 0x89, 0xc9, - 0xa9, 0x9f, 0x53, 0x76, 0x94, 0xbd, 0xb4, 0x1e, 0x2e, 0xd5, 0x0d, 0x48, 0x09, 0x11, 0xae, 0x9d, - 0x7b, 0x67, 0x47, 0xd9, 0x9b, 0xd5, 0xe7, 0x71, 0x7d, 0x62, 0xab, 0xc7, 0x90, 0x34, 0x1b, 0xb4, - 0xe5, 0xf1, 0xdc, 0x6c, 0x80, 0x39, 0x2a, 0x7d, 0xfd, 0x7a, 0x7b, 0xe6, 0xef, 0xaf, 0xb7, 0xbf, - 0xe9, 0xb8, 0xfc, 0xa2, 0x55, 0x2b, 0x5a, 0xb4, 0x51, 0xb2, 0x28, 0x6b, 0x50, 0x26, 0x1f, 0xfb, - 0xcc, 0xbe, 0x2c, 0xf1, 0xab, 0x26, 0x61, 0xc5, 0x17, 0xae, 0xc7, 0x75, 0x09, 0x2f, 0xbc, 0x07, - 0x5a, 0xbf, 0x4d, 0x3a, 0x61, 0x4d, 0xea, 0x31, 0x52, 0x78, 0x06, 0xef, 0x56, 0x99, 0xf3, 0xa2, - 0x69, 0x8b, 0x8f, 0x8f, 0x6d, 0xdb, 0x27, 0x6c, 0x94, 0xc9, 0x5b, 0x00, 0x9c, 0x31, 0xa3, 0xd9, - 0xaa, 0x5d, 0x92, 0x2b, 0x34, 0x3a, 0xad, 0xa7, 0x39, 0x63, 0xa7, 0x48, 0x28, 0x6c, 0xc1, 0xe6, - 0x00, 0x79, 0x91, 0xba, 0x3f, 0xbc, 0x03, 0xab, 0x55, 0xe6, 0x3c, 0xb6, 0xed, 0x13, 0xaf, 0x46, - 0x5b, 0x9e, 0x7d, 0xe6, 0x9b, 0xd6, 0x25, 0xf1, 0xa7, 0x8b, 0xd1, 0x3a, 0xcc, 0xf3, 0x8e, 0x71, - 0x61, 0xb2, 0x0b, 0x11, 0x24, 0x3d, 0xc9, 0x3b, 0x9f, 0x99, 0xec, 0x42, 0x3d, 0x82, 0x74, 0x90, - 0x2e, 0x46, 0x10, 0x8e, 0x5c, 0x62, 0x47, 0xd9, 0xcb, 0x96, 0xef, 0x17, 0x07, 0x64, 0x6f, 0xf3, - 0xd2, 0x29, 0x62, 0x5e, 0x55, 0xa8, 0xeb, 0x9d, 0x5d, 0x35, 0x89, 0x9e, 0xb2, 0xe4, 0x9b, 0x7a, - 0x08, 0x73, 0x98, 0x48, 0xb9, 0xb9, 0x1d, 0x65, 0x2f, 0x53, 0xfe, 0xc6, 0x30, 0xbc, 0xcc, 0xb6, - 0xd3, 0xe0, 0xa1, 0x0b, 0x48, 0x10, 0xa4, 0x5a, 0x9d, 0x5a, 0x97, 0xc2, 0xb6, 0xa4, 0x08, 0x12, - 0x52, 0xd0, 0xbc, 0x0d, 0x48, 0xf1, 0x8e, 0xe1, 0x7a, 0x36, 0xe9, 0xe4, 0xe6, 0x85, 0x4b, 0xbc, - 0x73, 0x12, 0x2c, 0x0b, 0x79, 0x78, 0x6f, 0x50, 0x7c, 0xa2, 0x00, 0xfe, 0x55, 0x81, 0x95, 0x2a, - 0x73, 0x7e, 0x74, 0xe1, 0x72, 0x52, 0x77, 0x19, 0xff, 0x54, 0xaf, 0x94, 0x0f, 0x46, 0x44, 0xef, - 0x1e, 0x2c, 0x12, 0xdf, 0x2a, 0x1f, 0x18, 0xa6, 0xd8, 0x09, 0xb9, 0x63, 0x0b, 0x48, 0x0c, 0x77, - 0xbb, 0x3b, 0xc4, 0xb3, 0xbd, 0x21, 0x56, 0x21, 0xe1, 0x99, 0x0d, 0x11, 0xc4, 0xb4, 0x8e, 0xef, - 0xea, 0x1a, 0x24, 0xd9, 0x55, 0xa3, 0x46, 0xeb, 0x18, 0x9a, 0xb4, 0x2e, 0x57, 0xaa, 0x06, 0x29, - 0x9b, 0x58, 0x6e, 0xc3, 0xac, 0x33, 0xf4, 0x79, 0x51, 0x8f, 0xd6, 0xea, 0x26, 0xa4, 0x1d, 0x93, - 0x89, 0x93, 0x26, 0x7d, 0x4e, 0x39, 0x26, 0x7b, 0x1a, 0xac, 0x0b, 0x06, 0x6c, 0xf4, 0xf9, 0x14, - 0x7a, 0x1c, 0x78, 0xf0, 0xb2, 0xc7, 0x03, 0xe1, 0xe1, 0xc2, 0xcb, 0x6e, 0x0f, 0xb6, 0x00, 0x2c, - 0x2b, 0x8a, 0xa9, 0xcc, 0xca, 0x80, 0x22, 0xa2, 0xfa, 0x2f, 0x05, 0xee, 0x88, 0xb0, 0x3e, 0x6f, - 0xf1, 0xdb, 0xe7, 0xdd, 0x2a, 0xcc, 0x79, 0xd4, 0xb3, 0x08, 0x06, 0x2b, 0xa1, 0x8b, 0x45, 0x77, - 0x36, 0x26, 0x7a, 0xb2, 0xf1, 0xff, 0x93, 0x49, 0xdf, 0x87, 0xad, 0x81, 0x2e, 0x47, 0x81, 0xdd, - 0x02, 0x70, 0x99, 0xe1, 0x93, 0x06, 0x6d, 0x13, 0x1b, 0xbd, 0x4f, 0xe9, 0x69, 0x97, 0xe9, 0x82, - 0x50, 0x20, 0x90, 0xab, 0x32, 0x47, 0xac, 0xfe, 0x77, 0x51, 0x2b, 0x14, 0x60, 0x67, 0x98, 0x9a, - 0x28, 0xe9, 0xff, 0xa4, 0xc0, 0x52, 0x95, 0x39, 0x5f, 0x50, 0x4e, 0x8e, 0x4d, 0x76, 0xea, 0xbb, - 0x16, 0x99, 0xda, 0x84, 0x66, 0x80, 0x0e, 0x4d, 0xc0, 0x85, 0x7a, 0x17, 0x16, 0x9a, 0xbe, 0x4b, - 0x7d, 0x97, 0x5f, 0x19, 0xe7, 0x84, 0x60, 0x94, 0x13, 0x7a, 0x26, 0xa4, 0x3d, 0x21, 0xc8, 0x22, - 0xb6, 0xc1, 0x6b, 0x35, 0x6a, 0xc4, 0xc7, 0x0d, 0x4e, 0xe8, 0x19, 0xa4, 0x3d, 0x43, 0xd2, 0x0f, - 0x13, 0xa9, 0xb9, 0xe5, 0x64, 0x61, 0x03, 0xd6, 0x63, 0x96, 0x46, 0x5e, 0xfc, 0x31, 0x19, 0x79, - 0x11, 0x3a, 0x3a, 0xc2, 0x8b, 0x4d, 0xc0, 0xfc, 0x15, 0xfb, 0x2e, 0x12, 0x3a, 0x15, 0x10, 0x70, - 0xdb, 0x3f, 0x80, 0x35, 0x5a, 0x63, 0xc4, 0x6f, 0x13, 0xdb, 0xa0, 0x52, 0x56, 0xf7, 0x3d, 0xb8, - 0x1a, 0x7e, 0x0d, 0x15, 0x21, 0xaa, 0x02, 0xf9, 0x7e, 0x94, 0xcc, 0x2e, 0xe2, 0x3a, 0x17, 0x5c, - 0xba, 0xb5, 0x19, 0x47, 0x1f, 0x61, 0xbe, 0x21, 0x8b, 0xfa, 0x11, 0x68, 0xfd, 0x42, 0x82, 0xa3, - 0xdd, 0x62, 0xc4, 0xce, 0x01, 0x0a, 0x58, 0x8f, 0x0b, 0x38, 0x36, 0xd9, 0x0b, 0x46, 0x6c, 0xf5, - 0x17, 0x0a, 0xdc, 0xef, 0x47, 0x93, 0xf3, 0x73, 0x62, 0x71, 0xb7, 0x4d, 0x50, 0x8e, 0xd8, 0xa0, - 0x0c, 0x16, 0xbd, 0xa2, 0x2c, 0x7a, 0xbb, 0x13, 0x14, 0xbd, 0x13, 0x8f, 0xeb, 0x77, 0xe3, 0x8a, - 0x3f, 0x0d, 0x45, 0x47, 0x79, 0x73, 0x3a, 0xde, 0x02, 0x71, 0x49, 0x2d, 0xa0, 0x2b, 0x23, 0x25, - 0xe2, 0xed, 0xa5, 0x52, 0xc8, 0xb6, 0xcd, 0x7a, 0x8b, 0x18, 0x3e, 0xb1, 0x88, 0x1b, 0x9c, 0x25, - 0xbc, 0x16, 0x8f, 0x3e, 0xbb, 0x61, 0xc5, 0xfe, 0xf7, 0xeb, 0xed, 0x3b, 0x57, 0x66, 0xa3, 0x7e, - 0x58, 0xe8, 0x15, 0x57, 0xd0, 0x17, 0x91, 0xa0, 0xcb, 0xb5, 0xfa, 0x09, 0x24, 0x19, 0x37, 0x79, - 0x4b, 0xdc, 0xb2, 0xd9, 0xf2, 0x83, 0xa1, 0xa5, 0x4d, 0x34, 0x57, 0x12, 0xf8, 0x39, 0x62, 0x74, - 0x89, 0x55, 0xef, 0x43, 0x36, 0xf2, 0x1f, 0x19, 0xe5, 0x05, 0xb2, 0x18, 0x52, 0x2b, 0x01, 0x51, - 0x7d, 0x00, 0x6a, 0xc4, 0x16, 0x14, 0x7e, 0x71, 0x84, 0x53, 0x18, 0x9c, 0xe5, 0xf0, 0xcb, 0x19, - 0x63, 0xcf, 0xf0, 0x0e, 0xec, 0x29, 0xbc, 0xe9, 0xa9, 0x0a, 0x6f, 0xd7, 0x11, 0x0a, 0x63, 0x1e, - 0x1d, 0xa1, 0xbf, 0x24, 0x21, 0x2b, 0xbf, 0xc9, 0xfa, 0x38, 0xe2, 0x04, 0x05, 0x65, 0x8a, 0x78, - 0x36, 0xf1, 0xe5, 0xf1, 0x91, 0x2b, 0x75, 0x17, 0x96, 0xc4, 0x9b, 0x11, 0x2b, 0x7a, 0x8b, 0x82, - 0x5c, 0x91, 0x97, 0x85, 0x06, 0x29, 0xb9, 0x05, 0xbe, 0xbc, 0xd0, 0xa3, 0x75, 0x10, 0xbc, 0xf0, - 0x5d, 0x06, 0x6f, 0x4e, 0x88, 0x08, 0xa9, 0x22, 0x78, 0xd7, 0x4d, 0x5c, 0xf2, 0x56, 0x4d, 0x5c, - 0xe0, 0x65, 0x83, 0x30, 0x66, 0x3a, 0x22, 0xf4, 0x69, 0x3d, 0x5c, 0x06, 0x37, 0x93, 0xeb, 0x75, - 0x5d, 0x00, 0x69, 0xfc, 0x9c, 0x91, 0x34, 0x3c, 0xf7, 0x07, 0xb0, 0x1a, 0xb2, 0xf4, 0x9c, 0x76, - 0x71, 0x58, 0x55, 0xf9, 0xad, 0xfb, 0x90, 0xf7, 0x54, 0xeb, 0x0c, 0xb2, 0x45, 0xd5, 0xba, 0x77, - 0x8f, 0x17, 0xa6, 0x6b, 0xae, 0x36, 0x21, 0xcd, 0x3b, 0x06, 0xf5, 0x5d, 0xc7, 0xf5, 0x72, 0x8b, - 0x22, 0xb8, 0xbc, 0xf3, 0x1c, 0xd7, 0xc1, 0x2d, 0x6d, 0x32, 0x46, 0x78, 0x2e, 0x8b, 0x1f, 0xc4, - 0x42, 0xdd, 0x86, 0x0c, 0x69, 0x13, 0x8f, 0xcb, 0x6a, 0xb7, 0x84, 0x56, 0x01, 0x92, 0xb0, 0xe0, - 0xa9, 0x3e, 0x6c, 0x60, 0x1b, 0x6e, 0xd1, 0xba, 0x61, 0x51, 0x8f, 0xfb, 0xa6, 0xc5, 0x8d, 0x36, - 0xf1, 0x99, 0x4b, 0xbd, 0xdc, 0x32, 0xda, 0xf9, 0xa8, 0x38, 0x72, 0x84, 0x09, 0x4a, 0x2f, 0xe2, - 0x2b, 0x12, 0xfe, 0x85, 0x40, 0xeb, 0xeb, 0xcd, 0xc1, 0x1f, 0xd4, 0x9f, 0x04, 0x79, 0xd0, 0x26, - 0x3e, 0x37, 0x68, 0x93, 0xbb, 0xd4, 0x63, 0xb9, 0x15, 0xac, 0xf1, 0x0f, 0xc6, 0x28, 0xd2, 0x11, - 0xf4, 0x5c, 0x60, 0x8e, 0x12, 0x41, 0x5a, 0x04, 0xb9, 0xd3, 0x45, 0x54, 0xab, 0xb0, 0x60, 0x99, - 0xf5, 0x7a, 0x24, 0x58, 0x45, 0xc1, 0xef, 0x8f, 0x11, 0x5c, 0x31, 0xeb, 0x75, 0x29, 0x41, 0xcf, - 0x58, 0xd7, 0x8b, 0x42, 0x0e, 0xd6, 0x7a, 0x4f, 0x4e, 0x74, 0xa8, 0x9e, 0x62, 0x47, 0xf9, 0xb8, - 0x46, 0x7d, 0xfe, 0x39, 0x6f, 0x59, 0x97, 0x95, 0xca, 0xd9, 0x8f, 0x47, 0x0f, 0x00, 0xa3, 0x5a, - 0xad, 0x4d, 0xec, 0xe5, 0x7a, 0xa5, 0x45, 0xaa, 0xda, 0xd8, 0xfd, 0xeb, 0xe4, 0xbc, 0xe5, 0xd9, - 0xc8, 0x42, 0xec, 0x5b, 0x69, 0x13, 0xe7, 0x30, 0x90, 0x16, 0x75, 0x87, 0xa2, 0x00, 0x2e, 0x0a, - 0xaa, 0x6c, 0x0f, 0x65, 0x57, 0xdd, 0xa7, 0x37, 0xb2, 0xeb, 0x2b, 0x05, 0xad, 0x16, 0x63, 0x8b, - 0x6e, 0x72, 0xf2, 0x54, 0x4c, 0x84, 0x4f, 0x82, 0x81, 0x70, 0x84, 0x75, 0x16, 0xa8, 0xfd, 0x03, - 0x24, 0x5a, 0x99, 0x29, 0x97, 0xc6, 0xa5, 0x40, 0x4c, 0x8d, 0xcc, 0x82, 0x65, 0x3f, 0x46, 0x2f, - 0xdc, 0x83, 0xbb, 0x43, 0x6d, 0x8b, 0x3c, 0xf8, 0xa7, 0x82, 0x83, 0x97, 0x1c, 0xf3, 0xb0, 0x83, - 0xae, 0xb4, 0x18, 0xa7, 0xf6, 0xd5, 0x2d, 0x66, 0xd0, 0x22, 0xbc, 0xeb, 0x91, 0x2f, 0x0d, 0x4b, - 0x08, 0x8a, 0x85, 0x78, 0xc5, 0x23, 0x5f, 0x4a, 0x15, 0x61, 0x17, 0xde, 0x37, 0x6c, 0x24, 0x06, - 0x0c, 0x1b, 0xd7, 0x77, 0xe2, 0xdc, 0xed, 0x06, 0xdb, 0x4f, 0xe0, 0xde, 0x08, 0x8f, 0xbb, 0xdb, - 0xdc, 0xae, 0x0c, 0x52, 0xe2, 0xf9, 0xda, 0xc0, 0xfe, 0x53, 0x44, 0xb7, 0x5b, 0xc8, 0xa9, 0xd9, - 0x62, 0xb2, 0x64, 0x4e, 0xdf, 0x6b, 0x06, 0x32, 0x30, 0x5c, 0x29, 0x5d, 0x2c, 0x0a, 0x27, 0xb0, - 0x37, 0x4e, 0xdd, 0x84, 0x96, 0x97, 0xff, 0x93, 0x85, 0xd9, 0x2a, 0x73, 0xd4, 0x5f, 0x2b, 0xa0, - 0x0e, 0x98, 0x6c, 0x3e, 0x18, 0x93, 0x7f, 0x03, 0x87, 0x03, 0xed, 0x7b, 0xd3, 0xa0, 0x22, 0x8b, - 0x7f, 0xa5, 0xc0, 0x4a, 0xff, 0x6c, 0xff, 0x70, 0x22, 0x99, 0xbd, 0x20, 0xed, 0xa3, 0x29, 0x40, - 0x91, 0x1d, 0xbf, 0x55, 0xe0, 0xce, 0xe0, 0xc9, 0xe5, 0x3b, 0xe3, 0xc5, 0x0e, 0x04, 0x6a, 0x1f, - 0x4f, 0x09, 0x8c, 0x6c, 0x6a, 0xc3, 0x42, 0xcf, 0x00, 0x53, 0x1c, 0x2f, 0xb0, 0x9b, 0x5f, 0x7b, - 0x74, 0x33, 0xfe, 0xb8, 0xde, 0x68, 0xe4, 0x98, 0x50, 0x6f, 0xc8, 0x3f, 0xa9, 0xde, 0x78, 0xaf, - 0xa6, 0x32, 0xc8, 0x74, 0xf7, 0x69, 0xfb, 0x93, 0x89, 0x91, 0xec, 0xda, 0xb7, 0x6f, 0xc4, 0x1e, - 0x29, 0xfd, 0x19, 0x64, 0x63, 0x3f, 0x8d, 0x1c, 0x8c, 0x17, 0xd4, 0x8b, 0xd0, 0x3e, 0xbc, 0x29, - 0x22, 0xd2, 0xfe, 0x4b, 0x05, 0x96, 0xfb, 0x7e, 0x4a, 0x2b, 0x8f, 0x17, 0x17, 0xc7, 0x68, 0x87, - 0x37, 0xc7, 0x44, 0x46, 0xfc, 0x1c, 0x96, 0xe2, 0x3f, 0x40, 0x7e, 0x6b, 0xbc, 0xb8, 0x18, 0x44, - 0xfb, 0xee, 0x8d, 0x21, 0xdd, 0x7b, 0x10, 0x6b, 0x26, 0x26, 0xd8, 0x83, 0x5e, 0xc4, 0x24, 0x7b, - 0x30, 0xb8, 0xc5, 0xc0, 0x2b, 0xa8, 0xbf, 0xc1, 0x78, 0x38, 0xc9, 0xe9, 0x8d, 0x81, 0x26, 0xb9, - 0x82, 0x86, 0xb6, 0x14, 0xea, 0xef, 0x15, 0x58, 0x1b, 0xd2, 0x4f, 0x7c, 0x38, 0xe9, 0xee, 0xc6, - 0x91, 0xda, 0x0f, 0xa6, 0x45, 0x46, 0x66, 0x7d, 0xa5, 0x40, 0x6e, 0x68, 0x93, 0x70, 0x38, 0xf1, - 0xa6, 0xf7, 0x61, 0xb5, 0xa3, 0xe9, 0xb1, 0x91, 0x71, 0x7f, 0x56, 0x60, 0x6b, 0x74, 0x25, 0xfe, - 0x78, 0xd2, 0x00, 0x0c, 0x11, 0xa0, 0x1d, 0xdf, 0x52, 0x40, 0x68, 0xeb, 0xd1, 0xf1, 0xd7, 0x6f, - 0xf2, 0xca, 0xab, 0x37, 0x79, 0xe5, 0x1f, 0x6f, 0xf2, 0xca, 0x6f, 0xde, 0xe6, 0x67, 0x5e, 0xbd, - 0xcd, 0xcf, 0xfc, 0xed, 0x6d, 0x7e, 0xe6, 0xa7, 0xfb, 0x5d, 0x8d, 0x4c, 0xa0, 0x62, 0x5f, 0xfc, - 0xc7, 0xc0, 0xa3, 0x36, 0x29, 0x75, 0x7a, 0xfe, 0xb1, 0x12, 0xf4, 0x34, 0xb5, 0x24, 0xce, 0x16, - 0x0f, 0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x3f, 0x68, 0xfd, 0xc9, 0x86, 0x19, 0x00, 0x00, + // 1840 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x59, 0x4b, 0x6f, 0x1b, 0xc9, + 0x11, 0xf6, 0xd8, 0x12, 0x4d, 0x16, 0x25, 0x59, 0x6e, 0xcb, 0x36, 0x35, 0x5a, 0xc9, 0x32, 0x1d, + 0x3b, 0xc2, 0xc2, 0x22, 0x1d, 0x7a, 0xe3, 0x6c, 0xbc, 0x41, 0x36, 0x16, 0x77, 0xad, 0x15, 0x60, + 0xda, 0xc2, 0xac, 0xbc, 0x79, 0x5c, 0x06, 0xc3, 0x99, 0xd6, 0x68, 0x20, 0x72, 0x9a, 0x98, 0x6e, + 0x72, 0x29, 0x23, 0x40, 0x82, 0x00, 0x01, 0x72, 0x4c, 0x82, 0x9c, 0xf6, 0x90, 0x5b, 0x0e, 0xf9, + 0x13, 0x39, 0xef, 0xd1, 0xc8, 0x29, 0xc8, 0xc1, 0x08, 0xec, 0x43, 0xae, 0x49, 0xae, 0xb9, 0x04, + 0x5d, 0xdd, 0x33, 0x22, 0x87, 0x4f, 0x51, 0x08, 0xf6, 0x22, 0x76, 0x57, 0xd7, 0x57, 0x5d, 0x55, + 0x5d, 0xdd, 0x55, 0x35, 0x82, 0x7b, 0xaf, 0xa8, 0x70, 0xdc, 0x23, 0x27, 0x08, 0xcb, 0x38, 0x62, + 0x11, 0x2d, 0xbb, 0x11, 0xe3, 0x5c, 0xd1, 0x44, 0xb7, 0xd4, 0x8a, 0x98, 0x60, 0x64, 0x3d, 0xe1, + 0x2b, 0xc5, 0x7c, 0xa5, 0x53, 0x3e, 0x73, 0xc5, 0x67, 0x3e, 0x43, 0xce, 0xb2, 0x1c, 0x29, 0x90, + 0xf9, 0xfe, 0x10, 0xe1, 0xad, 0x63, 0xbf, 0x8c, 0x24, 0xae, 0x7f, 0x34, 0xef, 0xbd, 0x51, 0xbc, + 0x2c, 0x08, 0xf1, 0xcf, 0x04, 0x99, 0xad, 0x88, 0xb1, 0x43, 0xae, 0x7f, 0x34, 0xef, 0xa3, 0xf1, + 0xc6, 0x45, 0x8e, 0xa0, 0x76, 0x23, 0x68, 0x06, 0x82, 0x46, 0xf6, 0x61, 0xc3, 0xf1, 0x63, 0x5c, + 0x65, 0x3c, 0x0e, 0x87, 0x36, 0x8e, 0xed, 0xd8, 0x41, 0xc5, 0xdf, 0x1b, 0x40, 0x6a, 0xdc, 0xaf, + 0x05, 0xbe, 0x14, 0x7b, 0xc0, 0xf9, 0xd3, 0x76, 0xe8, 0x71, 0x52, 0x80, 0xcb, 0x6e, 0x44, 0x1d, + 0xc1, 0xa2, 0x82, 0xb1, 0x69, 0x6c, 0xe5, 0xac, 0x78, 0x4a, 0x56, 0x21, 0xab, 0x44, 0x04, 0x5e, + 0xe1, 0xe2, 0xa6, 0xb1, 0x75, 0xc9, 0xba, 0x8c, 0xf3, 0x3d, 0x8f, 0xec, 0x42, 0xc6, 0x69, 0xb2, + 0x76, 0x28, 0x0a, 0x97, 0x24, 0x66, 0xa7, 0xfc, 0xf5, 0x9b, 0x5b, 0x17, 0xfe, 0xfe, 0xe6, 0xd6, + 0xb7, 0xfd, 0x40, 0x1c, 0xb5, 0xeb, 0x25, 0x97, 0x35, 0xcb, 0x2e, 0xe3, 0x4d, 0xc6, 0xf5, 0xcf, + 0x36, 0xf7, 0x8e, 0xcb, 0xe2, 0xa4, 0x45, 0x79, 0xe9, 0x65, 0x10, 0x0a, 0x4b, 0xc3, 0x8b, 0xef, + 0x81, 0x39, 0xa8, 0x93, 0x45, 0x79, 0x8b, 0x85, 0x9c, 0x16, 0x9f, 0xc3, 0xb5, 0x1a, 0xf7, 0x5f, + 0xb6, 0x3c, 0xb5, 0xf8, 0xc4, 0xf3, 0x22, 0xca, 0xc7, 0xa9, 0xbc, 0x0e, 0x20, 0x38, 0xb7, 0x5b, + 0xed, 0xfa, 0x31, 0x3d, 0x41, 0xa5, 0x73, 0x56, 0x4e, 0x70, 0xbe, 0x8f, 0x84, 0xe2, 0x3a, 0xac, + 0x0d, 0x91, 0x97, 0x6c, 0xf7, 0xc7, 0x8b, 0xb0, 0x52, 0xe3, 0xfe, 0x13, 0xcf, 0xdb, 0x0b, 0xeb, + 0xac, 0x1d, 0x7a, 0x07, 0x91, 0xe3, 0x1e, 0xd3, 0x68, 0x36, 0x1f, 0xdd, 0x84, 0xcb, 0xa2, 0x6b, + 0x1f, 0x39, 0xfc, 0x48, 0x39, 0xc9, 0xca, 0x88, 0xee, 0x67, 0x0e, 0x3f, 0x22, 0x3b, 0x90, 0x93, + 0xe1, 0x62, 0x4b, 0x77, 0x14, 0xe6, 0x36, 0x8d, 0xad, 0xa5, 0xca, 0xdd, 0xd2, 0x90, 0xe8, 0x6d, + 0x1d, 0xfb, 0x25, 0x8c, 0xab, 0x2a, 0x0b, 0xc2, 0x83, 0x93, 0x16, 0xb5, 0xb2, 0xae, 0x1e, 0x91, + 0xc7, 0x30, 0x8f, 0x81, 0x54, 0x98, 0xdf, 0x34, 0xb6, 0xf2, 0x95, 0x6f, 0x8d, 0xc2, 0xeb, 0x68, + 0xdb, 0x97, 0x3f, 0x96, 0x82, 0x48, 0x27, 0xd5, 0x1b, 0xcc, 0x3d, 0x56, 0xba, 0x65, 0x94, 0x93, + 0x90, 0x82, 0xea, 0xad, 0x42, 0x56, 0x74, 0xed, 0x20, 0xf4, 0x68, 0xb7, 0x70, 0x59, 0x99, 0x24, + 0xba, 0x7b, 0x72, 0x5a, 0xdc, 0x80, 0xf7, 0x86, 0xf9, 0x27, 0x71, 0xe0, 0x5f, 0x0d, 0xb8, 0x5a, + 0xe3, 0xfe, 0x8f, 0x8f, 0x02, 0x41, 0x1b, 0x01, 0x17, 0x9f, 0x5a, 0xd5, 0xca, 0x83, 0x31, 0xde, + 0xbb, 0x03, 0x8b, 0x34, 0x72, 0x2b, 0x0f, 0x6c, 0x47, 0x9d, 0x84, 0x3e, 0xb1, 0x05, 0x24, 0xc6, + 0xa7, 0xdd, 0xeb, 0xe2, 0x4b, 0xfd, 0x2e, 0x26, 0x30, 0x17, 0x3a, 0x4d, 0xe5, 0xc4, 0x9c, 0x85, + 0x63, 0x72, 0x03, 0x32, 0xfc, 0xa4, 0x59, 0x67, 0x0d, 0x74, 0x4d, 0xce, 0xd2, 0x33, 0x62, 0x42, + 0xd6, 0xa3, 0x6e, 0xd0, 0x74, 0x1a, 0x1c, 0x6d, 0x5e, 0xb4, 0x92, 0x39, 0x59, 0x83, 0x9c, 0xef, + 0x70, 0x75, 0xd3, 0xb4, 0xcd, 0x59, 0xdf, 0xe1, 0xcf, 0xe4, 0xbc, 0x68, 0xc3, 0xea, 0x80, 0x4d, + 0xb1, 0xc5, 0xd2, 0x82, 0x57, 0x7d, 0x16, 0x28, 0x0b, 0x17, 0x5e, 0xf5, 0x5a, 0xb0, 0x0e, 0xe0, + 0xba, 0x89, 0x4f, 0x75, 0x54, 0x4a, 0x8a, 0xf2, 0xea, 0xbf, 0x0d, 0xb8, 0xae, 0xdc, 0xfa, 0xa2, + 0x2d, 0xce, 0x1f, 0x77, 0x2b, 0x30, 0x1f, 0xb2, 0xd0, 0xa5, 0xe8, 0xac, 0x39, 0x4b, 0x4d, 0x7a, + 0xa3, 0x71, 0xae, 0x2f, 0x1a, 0xbf, 0x99, 0x48, 0xfa, 0x21, 0xac, 0x0f, 0x35, 0x39, 0x71, 0xec, + 0x3a, 0x40, 0xc0, 0xed, 0x88, 0x36, 0x59, 0x87, 0x7a, 0x68, 0x7d, 0xd6, 0xca, 0x05, 0xdc, 0x52, + 0x84, 0x22, 0x85, 0x42, 0x8d, 0xfb, 0x6a, 0xf6, 0xff, 0xf3, 0x5a, 0xb1, 0x08, 0x9b, 0xa3, 0xb6, + 0x49, 0x82, 0xfe, 0x2f, 0x06, 0x5c, 0xa9, 0x71, 0xff, 0x0b, 0x26, 0xe8, 0xae, 0xc3, 0xf7, 0xa3, + 0xc0, 0xa5, 0x33, 0xab, 0xd0, 0x92, 0xe8, 0x58, 0x05, 0x9c, 0x90, 0xdb, 0xb0, 0xd0, 0x8a, 0x02, + 0x16, 0x05, 0xe2, 0xc4, 0x3e, 0xa4, 0x14, 0xbd, 0x3c, 0x67, 0xe5, 0x63, 0xda, 0x53, 0x8a, 0x2c, + 0xea, 0x18, 0xc2, 0x76, 0xb3, 0x4e, 0x23, 0x3c, 0xe0, 0x39, 0x2b, 0x8f, 0xb4, 0xe7, 0x48, 0x22, + 0x26, 0x64, 0x78, 0xbb, 0xd5, 0x6a, 0x9c, 0xa8, 0x5b, 0xb1, 0x73, 0xb1, 0x60, 0x58, 0x9a, 0x52, + 0x5c, 0x85, 0x9b, 0x29, 0xfd, 0x13, 0xdb, 0xfe, 0x94, 0x49, 0x6c, 0x8b, 0xcd, 0x1f, 0x63, 0xdb, + 0x1a, 0x60, 0x54, 0xab, 0x68, 0x50, 0x61, 0x9e, 0x95, 0x04, 0x0c, 0x86, 0x0f, 0xe0, 0x06, 0xab, + 0x73, 0x1a, 0x75, 0xa8, 0x67, 0x33, 0x2d, 0xab, 0xf7, 0x75, 0x5c, 0x89, 0x57, 0xe3, 0x8d, 0x10, + 0x55, 0x85, 0x8d, 0x41, 0x94, 0x8e, 0x39, 0x1a, 0xf8, 0x47, 0x42, 0x1b, 0xbb, 0x96, 0x46, 0xef, + 0x60, 0x14, 0x22, 0x0b, 0xf9, 0x08, 0xcc, 0x41, 0x21, 0xf2, 0xc2, 0xb7, 0x39, 0xf5, 0x0a, 0x80, + 0x02, 0x6e, 0xa6, 0x05, 0xec, 0x3a, 0xfc, 0x25, 0xa7, 0x1e, 0xf9, 0xa5, 0x01, 0x77, 0x07, 0xd1, + 0xf4, 0xf0, 0x90, 0xba, 0x22, 0xe8, 0x50, 0x94, 0xa3, 0x8e, 0x2d, 0x8f, 0x9e, 0x2d, 0xe9, 0x54, + 0x78, 0x6f, 0x8a, 0x54, 0xb8, 0x17, 0x0a, 0xeb, 0x76, 0x7a, 0xe3, 0x4f, 0x63, 0xd1, 0x49, 0x34, + 0xed, 0x4f, 0xd6, 0x40, 0x3d, 0x5d, 0x0b, 0x68, 0xca, 0x58, 0x89, 0xf8, 0xa6, 0x11, 0x06, 0x4b, + 0x1d, 0xa7, 0xd1, 0xa6, 0x76, 0x44, 0x5d, 0x1a, 0xc8, 0x1b, 0xa6, 0xc2, 0xe2, 0xb3, 0x33, 0xe6, + 0xf1, 0xff, 0xbc, 0xb9, 0x75, 0xfd, 0xc4, 0x69, 0x36, 0x1e, 0x17, 0xfb, 0xc5, 0x15, 0xad, 0x45, + 0x24, 0x58, 0x7a, 0x4e, 0x3e, 0x81, 0x0c, 0x17, 0x8e, 0x68, 0xab, 0xb7, 0x77, 0xa9, 0x72, 0x7f, + 0x64, 0xc2, 0x53, 0x25, 0x97, 0x06, 0x7e, 0x8e, 0x18, 0x4b, 0x63, 0xc9, 0x5d, 0x58, 0x4a, 0xec, + 0x47, 0x46, 0xfd, 0xac, 0x2c, 0xc6, 0xd4, 0xaa, 0x24, 0x92, 0xfb, 0x40, 0x12, 0x36, 0x59, 0x0e, + 0xa8, 0x8b, 0x9d, 0x45, 0xe7, 0x2c, 0xc7, 0x2b, 0x07, 0x9c, 0x3f, 0xc7, 0x97, 0xb1, 0x2f, 0x1d, + 0xe7, 0x66, 0x4a, 0xc7, 0x3d, 0x57, 0x28, 0xf6, 0x79, 0x72, 0x85, 0xfe, 0x99, 0x81, 0x25, 0xbd, + 0xa6, 0xb3, 0xe6, 0x98, 0x1b, 0x24, 0x93, 0x17, 0x0d, 0x3d, 0x1a, 0xe9, 0xeb, 0xa3, 0x67, 0xe4, + 0x1e, 0x5c, 0x51, 0x23, 0x3b, 0x95, 0x0a, 0x17, 0x15, 0xb9, 0xaa, 0x9f, 0x10, 0x13, 0xb2, 0xfa, + 0x08, 0x22, 0xfd, 0xcc, 0x27, 0x73, 0xe9, 0xbc, 0x78, 0xac, 0x9d, 0x37, 0xaf, 0x44, 0xc4, 0x54, + 0xe5, 0xbc, 0xd3, 0xd2, 0x2e, 0x73, 0xae, 0xd2, 0x4e, 0x5a, 0xd9, 0xa4, 0x9c, 0x3b, 0xbe, 0x72, + 0x7d, 0xce, 0x8a, 0xa7, 0xf2, 0xbd, 0x0a, 0xc2, 0x9e, 0x07, 0x20, 0x87, 0xcb, 0x79, 0x4d, 0xc3, + 0x7b, 0xff, 0x00, 0x56, 0x62, 0x96, 0xbe, 0xdb, 0xae, 0x2e, 0x2b, 0xd1, 0x6b, 0xbd, 0x97, 0xbc, + 0x2f, 0x87, 0xe7, 0x91, 0x2d, 0xc9, 0xe1, 0xfd, 0x67, 0xbc, 0x30, 0x5b, 0xc9, 0xb5, 0x06, 0x39, + 0xd1, 0xb5, 0x59, 0x14, 0xf8, 0x41, 0x58, 0x58, 0x54, 0xce, 0x15, 0xdd, 0x17, 0x38, 0x97, 0x6f, + 0xb7, 0xc3, 0x39, 0x15, 0x85, 0x25, 0x5c, 0x50, 0x13, 0x72, 0x0b, 0xf2, 0xb4, 0x43, 0x43, 0xa1, + 0x73, 0xe0, 0x15, 0xd4, 0x0a, 0x90, 0x84, 0x69, 0x90, 0x44, 0xb0, 0x8a, 0xc5, 0xb9, 0xcb, 0x1a, + 0xb6, 0xcb, 0x42, 0x11, 0x39, 0xae, 0xb0, 0x3b, 0x34, 0xe2, 0x01, 0x0b, 0x0b, 0xcb, 0xa8, 0xe7, + 0xa3, 0xd2, 0xd8, 0xc6, 0x46, 0x26, 0x64, 0xc4, 0x57, 0x35, 0xfc, 0x0b, 0x85, 0xb6, 0x6e, 0xb6, + 0x86, 0x2f, 0x90, 0x9f, 0xca, 0x38, 0xe8, 0xd0, 0x48, 0xd8, 0xac, 0x25, 0x02, 0x16, 0xf2, 0xc2, + 0x55, 0xcc, 0xfc, 0xf7, 0x27, 0x6c, 0x64, 0x21, 0xe8, 0x85, 0xc2, 0xec, 0xcc, 0xc9, 0xb0, 0x90, + 0xb1, 0xd3, 0x43, 0x24, 0x35, 0x58, 0x70, 0x9d, 0x46, 0x23, 0x11, 0x4c, 0x50, 0xf0, 0xfb, 0x13, + 0x04, 0x57, 0x9d, 0x46, 0x43, 0x4b, 0xb0, 0xf2, 0xee, 0xe9, 0x84, 0x6c, 0xc3, 0xb5, 0x80, 0xdb, + 0xbd, 0xcd, 0x8c, 0x5c, 0x2d, 0x5c, 0xc3, 0x62, 0x60, 0x39, 0xe0, 0x55, 0xb9, 0x82, 0x51, 0x2b, + 0x45, 0x14, 0x0b, 0x70, 0xa3, 0xff, 0xa2, 0x25, 0x77, 0xf0, 0x19, 0x96, 0xa5, 0x4f, 0xea, 0x2c, + 0x12, 0x9f, 0x8b, 0xb6, 0x7b, 0x5c, 0xad, 0x1e, 0xfc, 0x64, 0x7c, 0x17, 0x31, 0xae, 0x5e, 0x5b, + 0xc3, 0x82, 0xb0, 0x5f, 0x5a, 0xb2, 0x55, 0x07, 0x5b, 0x08, 0x8b, 0x1e, 0xb6, 0x43, 0x0f, 0x59, + 0xa8, 0x77, 0xae, 0xdd, 0xd4, 0xb5, 0x95, 0xd2, 0x92, 0x12, 0x53, 0xe5, 0xcb, 0x45, 0x45, 0xd5, + 0x35, 0xa6, 0x2e, 0xcd, 0x07, 0xf6, 0x4d, 0xf4, 0xfa, 0xca, 0x40, 0xad, 0x55, 0xef, 0x63, 0x39, + 0x82, 0x3e, 0x53, 0x6d, 0xe5, 0x53, 0xd9, 0x55, 0x8e, 0xd1, 0xce, 0x05, 0x32, 0xd8, 0x85, 0xa2, + 0x96, 0xf9, 0x4a, 0x79, 0x52, 0xc4, 0xa4, 0xb6, 0xd1, 0x41, 0xb3, 0x1c, 0xa5, 0xe8, 0xc5, 0x3b, + 0x70, 0x7b, 0xa4, 0x6e, 0x89, 0x05, 0xff, 0x32, 0xb0, 0x7b, 0xd3, 0xbd, 0x22, 0x96, 0xe1, 0xd5, + 0x36, 0x17, 0xcc, 0x3b, 0x39, 0x47, 0x23, 0x5b, 0x82, 0x6b, 0x21, 0xfd, 0xd2, 0x76, 0x95, 0xa0, + 0x94, 0x8b, 0xaf, 0x86, 0xf4, 0x4b, 0xbd, 0x45, 0x5c, 0xca, 0x0f, 0x74, 0x2c, 0x73, 0x43, 0x3a, + 0x96, 0xd3, 0x27, 0x74, 0xfe, 0x7c, 0xdd, 0xf1, 0x27, 0x70, 0x67, 0x8c, 0xc5, 0xbd, 0xb5, 0x72, + 0x4f, 0x04, 0x19, 0xe9, 0x78, 0x6d, 0x62, 0x11, 0xab, 0xbc, 0xdb, 0x2b, 0x64, 0xdf, 0x69, 0x73, + 0x9d, 0x61, 0x67, 0x2f, 0x58, 0xa5, 0x0c, 0x74, 0x57, 0xd6, 0x52, 0x93, 0xe2, 0x1e, 0x6c, 0x4d, + 0xda, 0x6e, 0x4a, 0xcd, 0x2b, 0xff, 0x5d, 0x82, 0x4b, 0x35, 0xee, 0x93, 0xdf, 0x18, 0x40, 0x86, + 0xb4, 0x47, 0x1f, 0x4c, 0x88, 0xbf, 0xa1, 0x1d, 0x86, 0xf9, 0x83, 0x59, 0x50, 0x89, 0xc6, 0xbf, + 0x36, 0xe0, 0xea, 0xe0, 0x07, 0x82, 0x87, 0x53, 0xc9, 0xec, 0x07, 0x99, 0x1f, 0xcd, 0x00, 0x4a, + 0xf4, 0xf8, 0x9d, 0x01, 0xd7, 0x87, 0xb7, 0x3f, 0xdf, 0x9b, 0x2c, 0x76, 0x28, 0xd0, 0xfc, 0x78, + 0x46, 0x60, 0xa2, 0x53, 0x07, 0x16, 0xfa, 0xba, 0xa0, 0xd2, 0x64, 0x81, 0xbd, 0xfc, 0xe6, 0xa3, + 0xb3, 0xf1, 0xa7, 0xf7, 0x4d, 0x3a, 0x94, 0x29, 0xf7, 0x8d, 0xf9, 0xa7, 0xdd, 0x37, 0x5d, 0xda, + 0x11, 0x0e, 0xf9, 0xde, 0xb2, 0x6e, 0x7b, 0x3a, 0x31, 0x9a, 0xdd, 0xfc, 0xee, 0x99, 0xd8, 0x93, + 0x4d, 0x7f, 0x0e, 0x4b, 0xa9, 0xef, 0x2b, 0x0f, 0x26, 0x0b, 0xea, 0x47, 0x98, 0x1f, 0x9e, 0x15, + 0x91, 0xec, 0xfe, 0x2b, 0x03, 0x96, 0x07, 0xbe, 0xc7, 0x55, 0x26, 0x8b, 0x4b, 0x63, 0xcc, 0xc7, + 0x67, 0xc7, 0x24, 0x4a, 0xfc, 0x02, 0xae, 0xa4, 0xbf, 0x62, 0x7e, 0x67, 0xb2, 0xb8, 0x14, 0xc4, + 0xfc, 0xfe, 0x99, 0x21, 0xbd, 0x67, 0x90, 0x2a, 0x26, 0xa6, 0x38, 0x83, 0x7e, 0xc4, 0x34, 0x67, + 0x30, 0xbc, 0xc4, 0xc0, 0x27, 0x68, 0xb0, 0xc0, 0x78, 0x38, 0xcd, 0xed, 0x4d, 0x81, 0xa6, 0x79, + 0x82, 0x46, 0x96, 0x14, 0xe4, 0x0f, 0x06, 0xdc, 0x18, 0x51, 0x4f, 0x7c, 0x38, 0xed, 0xe9, 0xa6, + 0x91, 0xe6, 0x8f, 0x66, 0x45, 0x26, 0x6a, 0x7d, 0x65, 0x40, 0x61, 0x64, 0x91, 0xf0, 0x78, 0xea, + 0x43, 0x1f, 0xc0, 0x9a, 0x3b, 0xb3, 0x63, 0x13, 0xe5, 0xfe, 0x6c, 0xc0, 0xfa, 0xf8, 0x4c, 0xfc, + 0xf1, 0xb4, 0x0e, 0x18, 0x21, 0xc0, 0xdc, 0x3d, 0xa7, 0x80, 0x58, 0xd7, 0x9d, 0xdd, 0xaf, 0xdf, + 0x6e, 0x18, 0xaf, 0xdf, 0x6e, 0x18, 0xff, 0x78, 0xbb, 0x61, 0xfc, 0xf6, 0xdd, 0xc6, 0x85, 0xd7, + 0xef, 0x36, 0x2e, 0xfc, 0xed, 0xdd, 0xc6, 0x85, 0x9f, 0x6d, 0xf7, 0x14, 0x32, 0x72, 0x8b, 0x6d, + 0xf5, 0x6f, 0x87, 0x90, 0x79, 0xb4, 0xdc, 0xed, 0xfb, 0xef, 0x8c, 0xac, 0x69, 0xea, 0x19, 0x6c, + 0x45, 0x1e, 0xfe, 0x2f, 0x00, 0x00, 0xff, 0xff, 0x5c, 0xd9, 0x42, 0xd4, 0xcb, 0x19, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2896,6 +2916,13 @@ func (m *MsgVoteGasPrice) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x30 } + if len(m.Supply) > 0 { + i -= len(m.Supply) + copy(dAtA[i:], m.Supply) + i = encodeVarintTx(dAtA, i, uint64(len(m.Supply))) + i-- + dAtA[i] = 0x2a + } if m.BlockNumber != 0 { i = encodeVarintTx(dAtA, i, uint64(m.BlockNumber)) i-- @@ -3086,6 +3113,18 @@ func (m *MsgVoteInbound) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.IsCrossChainCall { + i-- + if m.IsCrossChainCall { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x98 + } if m.CallOptions != nil { { size, err := m.CallOptions.MarshalToSizedBuffer(dAtA[:i]) @@ -3839,6 +3878,10 @@ func (m *MsgVoteGasPrice) Size() (n int) { if m.BlockNumber != 0 { n += 1 + sovTx(uint64(m.BlockNumber)) } + l = len(m.Supply) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } if m.PriorityFee != 0 { n += 1 + sovTx(uint64(m.PriorityFee)) } @@ -3972,6 +4015,9 @@ func (m *MsgVoteInbound) Size() (n int) { l = m.CallOptions.Size() n += 2 + l + sovTx(uint64(l)) } + if m.IsCrossChainCall { + n += 3 + } return n } @@ -5725,6 +5771,38 @@ func (m *MsgVoteGasPrice) Unmarshal(dAtA []byte) error { break } } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Supply", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Supply = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field PriorityFee", wireType) @@ -6701,6 +6779,26 @@ func (m *MsgVoteInbound) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 19: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsCrossChainCall", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IsCrossChainCall = bool(v != 0) default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) diff --git a/x/emissions/abci.go b/x/emissions/abci.go index b4b75562ff..c1924ff2e2 100644 --- a/x/emissions/abci.go +++ b/x/emissions/abci.go @@ -15,6 +15,15 @@ import ( func BeginBlocker(ctx sdk.Context, keeper keeper.Keeper) { emissionPoolBalance := keeper.GetReservesFactor(ctx) + // reduce frequency of log messages + logEach10Blocks := func(message string) { + if ctx.BlockHeight()%10 == 0 { + ctx.Logger().Info(message) + } else { + ctx.Logger().Debug(message) + } + } + // Get the block rewards from the params params, found := keeper.GetParams(ctx) if !found { @@ -22,9 +31,17 @@ func BeginBlocker(ctx sdk.Context, keeper keeper.Keeper) { return } blockRewards := params.BlockRewardAmount + + // skip if block rewards are nil or not positive + if blockRewards.IsNil() || !blockRewards.IsPositive() { + logEach10Blocks("Block rewards are nil or not positive") + return + } + if blockRewards.GT(emissionPoolBalance) { - ctx.Logger(). - Info(fmt.Sprintf("Block rewards %s are greater than emission pool balance %s", blockRewards.String(), emissionPoolBalance.String())) + logEach10Blocks(fmt.Sprintf("Block rewards %s are greater than emission pool balance %s", + blockRewards.String(), emissionPoolBalance.String()), + ) return } @@ -44,7 +61,7 @@ func BeginBlocker(ctx sdk.Context, keeper keeper.Keeper) { ctx.Logger().Error(fmt.Sprintf("Error while distributing observer rewards %s", err)) return } - err = DistributeTssRewards(tmpCtx, tssSignerRewards, keeper.GetBankKeeper()) + err = DistributeTSSRewards(tmpCtx, tssSignerRewards, keeper.GetBankKeeper()) if err != nil { ctx.Logger().Error(fmt.Sprintf("Error while distributing tss signer rewards %s", err)) return @@ -166,9 +183,9 @@ func DistributeObserverRewards( return nil } -// DistributeTssRewards trasferes the allocated rewards to the Undistributed Tss Rewards Pool. +// DistributeTSSRewards trasferes the allocated rewards to the Undistributed Tss Rewards Pool. // This is done so that the reserves factor is properly calculated in the next block -func DistributeTssRewards(ctx sdk.Context, amount sdk.Int, bankKeeper types.BankKeeper) error { +func DistributeTSSRewards(ctx sdk.Context, amount sdk.Int, bankKeeper types.BankKeeper) error { coin := sdk.NewCoins(sdk.NewCoin(config.BaseDenom, amount)) - return bankKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, types.UndistributedTssRewardsPool, coin) + return bankKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, types.UndistributedTSSRewardsPool, coin) } diff --git a/x/emissions/abci_test.go b/x/emissions/abci_test.go index 26420231fd..681a8b1247 100644 --- a/x/emissions/abci_test.go +++ b/x/emissions/abci_test.go @@ -13,9 +13,10 @@ import ( "github.com/zeta-chain/node/pkg/sdkconfig" keepertest "github.com/zeta-chain/node/testutil/keeper" "github.com/zeta-chain/node/testutil/sample" - emissionsModule "github.com/zeta-chain/node/x/emissions" + "github.com/zeta-chain/node/x/emissions" + emissionskeeper "github.com/zeta-chain/node/x/emissions/keeper" emissionstypes "github.com/zeta-chain/node/x/emissions/types" - observerTypes "github.com/zeta-chain/node/x/observer/types" + observertypes "github.com/zeta-chain/node/x/observer/types" ) func TestBeginBlocker(t *testing.T) { @@ -35,14 +36,14 @@ func TestBeginBlocker(t *testing.T) { zk.ObserverKeeper.SetBallot(ctx, &ballot) ballotIdentifiers = append(ballotIdentifiers, ballot.BallotIdentifier) } - zk.ObserverKeeper.SetBallotList(ctx, &observerTypes.BallotListForHeight{ + zk.ObserverKeeper.SetBallotList(ctx, &observertypes.BallotListForHeight{ Height: 0, BallotsIndexList: ballotIdentifiers, }) //Act for i := 0; i < 100; i++ { - emissionsModule.BeginBlocker(ctx, *k) + emissions.BeginBlocker(ctx, *k) ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) } @@ -64,12 +65,12 @@ func TestBeginBlocker(t *testing.T) { zk.ObserverKeeper.SetBallot(ctx, &ballot) ballotIdentifiers = append(ballotIdentifiers, ballot.BallotIdentifier) } - zk.ObserverKeeper.SetBallotList(ctx, &observerTypes.BallotListForHeight{ + zk.ObserverKeeper.SetBallotList(ctx, &observertypes.BallotListForHeight{ Height: 0, BallotsIndexList: ballotIdentifiers, }) for i := 0; i < 100; i++ { - emissionsModule.BeginBlocker(ctx, *k) + emissions.BeginBlocker(ctx, *k) ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) } for _, observer := range observerSet.ObserverList { @@ -82,7 +83,7 @@ func TestBeginBlocker(t *testing.T) { k, ctx, sk, _ := keepertest.EmissionsKeeper(t) feeCollectorAddress := sk.AuthKeeper.GetModuleAccount(ctx, types.FeeCollectorName).GetAddress() for i := 0; i < 100; i++ { - emissionsModule.BeginBlocker(ctx, *k) + emissions.BeginBlocker(ctx, *k) ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) } require.True(t, sk.BankKeeper.GetBalance(ctx, feeCollectorAddress, config.BaseDenom).Amount.IsZero()) @@ -99,13 +100,13 @@ func TestBeginBlocker(t *testing.T) { ) require.NoError(t, err) // Setup module accounts for emission pools except for observer pool , so that the observer distribution fails - _ = sk.AuthKeeper.GetModuleAccount(ctx, emissionstypes.UndistributedTssRewardsPool).GetAddress() + _ = sk.AuthKeeper.GetModuleAccount(ctx, emissionstypes.UndistributedTSSRewardsPool).GetAddress() feeCollectorAddress := sk.AuthKeeper.GetModuleAccount(ctx, types.FeeCollectorName).GetAddress() _ = sk.AuthKeeper.GetModuleAccount(ctx, emissionstypes.ModuleName).GetAddress() for i := 0; i < 100; i++ { // produce a block - emissionsModule.BeginBlocker(ctx, *k) + emissions.BeginBlocker(ctx, *k) ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) } require.True(t, sk.BankKeeper.GetBalance(ctx, feeCollectorAddress, config.BaseDenom).Amount.IsZero()) @@ -138,7 +139,7 @@ func TestBeginBlocker(t *testing.T) { bankMock.On("SendCoinsFromModuleToModule", mock.Anything, emissionstypes.ModuleName, k.GetFeeCollector(), mock.Anything). Return(emissionstypes.ErrUnableToWithdrawEmissions).Once() - emissionsModule.BeginBlocker(ctx, *k) + emissions.BeginBlocker(ctx, *k) bankMock.AssertNumberOfCalls(t, "SendCoinsFromModuleToModule", 1) }) @@ -165,7 +166,7 @@ func TestBeginBlocker(t *testing.T) { bankMock.On("SendCoinsFromModuleToModule", mock.Anything, emissionstypes.ModuleName, emissionstypes.UndistributedObserverRewardsPool, mock.Anything). Return(emissionstypes.ErrUnableToWithdrawEmissions).Once() - emissionsModule.BeginBlocker(ctx, *k) + emissions.BeginBlocker(ctx, *k) bankMock.AssertNumberOfCalls(t, "SendCoinsFromModuleToModule", 2) }) @@ -196,9 +197,9 @@ func TestBeginBlocker(t *testing.T) { // fail third distribution bankMock.On("SendCoinsFromModuleToModule", - mock.Anything, emissionstypes.ModuleName, emissionstypes.UndistributedTssRewardsPool, mock.Anything). + mock.Anything, emissionstypes.ModuleName, emissionstypes.UndistributedTSSRewardsPool, mock.Anything). Return(emissionstypes.ErrUnableToWithdrawEmissions).Once() - emissionsModule.BeginBlocker(ctx, *k) + emissions.BeginBlocker(ctx, *k) bankMock.AssertNumberOfCalls(t, "SendCoinsFromModuleToModule", 3) }) @@ -216,7 +217,7 @@ func TestBeginBlocker(t *testing.T) { zk.ObserverKeeper.SetBallot(ctx, &ballot) ballotIdentifiers = append(ballotIdentifiers, ballot.BallotIdentifier) } - zk.ObserverKeeper.SetBallotList(ctx, &observerTypes.BallotListForHeight{ + zk.ObserverKeeper.SetBallotList(ctx, &observertypes.BallotListForHeight{ Height: 0, BallotsIndexList: ballotIdentifiers, }) @@ -232,7 +233,7 @@ func TestBeginBlocker(t *testing.T) { // Setup module accounts for emission pools undistributedObserverPoolAddress := sk.AuthKeeper.GetModuleAccount(ctx, emissionstypes.UndistributedObserverRewardsPool). GetAddress() - undistributedTssPoolAddress := sk.AuthKeeper.GetModuleAccount(ctx, emissionstypes.UndistributedTssRewardsPool). + undistributedTssPoolAddress := sk.AuthKeeper.GetModuleAccount(ctx, emissionstypes.UndistributedTSSRewardsPool). GetAddress() feeCollecterAddress := sk.AuthKeeper.GetModuleAccount(ctx, types.FeeCollectorName).GetAddress() emissionPool := sk.AuthKeeper.GetModuleAccount(ctx, emissionstypes.ModuleName).GetAddress() @@ -251,7 +252,7 @@ func TestBeginBlocker(t *testing.T) { for i := 0; i < numberOfTestBlocks; i++ { emissionPoolBeforeBlockDistribution := sk.BankKeeper.GetBalance(ctx, emissionPool, config.BaseDenom).Amount // produce a block - emissionsModule.BeginBlocker(ctx, *k) + emissions.BeginBlocker(ctx, *k) // require distribution amount emissionPoolBalanceAfterBlockDistribution := sk.BankKeeper.GetBalance( @@ -313,27 +314,29 @@ func TestBeginBlocker(t *testing.T) { func TestDistributeObserverRewards(t *testing.T) { sdkconfig.SetDefault(false) - k, ctx, _, _ := keepertest.EmissionsKeeper(t) observerSet := sample.ObserverSet(4) tt := []struct { - name string - votes [][]observerTypes.VoteType - totalRewardsForBlock sdkmath.Int - expectedRewards map[string]int64 - ballotStatus observerTypes.BallotStatus - slashAmount sdkmath.Int + name string + votes [][]observertypes.VoteType + observerStartingEmissions sdkmath.Int + totalRewardsForBlock sdkmath.Int + expectedRewards map[string]int64 + ballotStatus observertypes.BallotStatus + slashAmount sdkmath.Int + rewardsPerBlock sdkmath.LegacyDec }{ { name: "all observers rewarded correctly", - votes: [][]observerTypes.VoteType{ + votes: [][]observertypes.VoteType{ { - observerTypes.VoteType_SuccessObservation, - observerTypes.VoteType_SuccessObservation, - observerTypes.VoteType_SuccessObservation, - observerTypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, }, }, + observerStartingEmissions: sdkmath.NewInt(100), // total reward units would be 4 as all votes match the ballot status totalRewardsForBlock: sdkmath.NewInt(100), expectedRewards: map[string]int64{ @@ -342,19 +345,21 @@ func TestDistributeObserverRewards(t *testing.T) { observerSet.ObserverList[2]: 125, observerSet.ObserverList[3]: 125, }, - ballotStatus: observerTypes.BallotStatus_BallotFinalized_SuccessObservation, - slashAmount: sdkmath.NewInt(25), + ballotStatus: observertypes.BallotStatus_BallotFinalized_SuccessObservation, + slashAmount: sdkmath.NewInt(25), + rewardsPerBlock: emissionstypes.BlockReward, }, { name: "one observer slashed", - votes: [][]observerTypes.VoteType{ + votes: [][]observertypes.VoteType{ { - observerTypes.VoteType_FailureObservation, - observerTypes.VoteType_SuccessObservation, - observerTypes.VoteType_SuccessObservation, - observerTypes.VoteType_SuccessObservation, + observertypes.VoteType_FailureObservation, + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, }, }, + observerStartingEmissions: sdkmath.NewInt(100), // total reward units would be 3 as 3 votes match the ballot status totalRewardsForBlock: sdkmath.NewInt(75), expectedRewards: map[string]int64{ @@ -363,19 +368,21 @@ func TestDistributeObserverRewards(t *testing.T) { observerSet.ObserverList[2]: 125, observerSet.ObserverList[3]: 125, }, - ballotStatus: observerTypes.BallotStatus_BallotFinalized_SuccessObservation, - slashAmount: sdkmath.NewInt(25), + ballotStatus: observertypes.BallotStatus_BallotFinalized_SuccessObservation, + slashAmount: sdkmath.NewInt(25), + rewardsPerBlock: emissionstypes.BlockReward, }, { name: "all observer slashed", - votes: [][]observerTypes.VoteType{ + votes: [][]observertypes.VoteType{ { - observerTypes.VoteType_SuccessObservation, - observerTypes.VoteType_SuccessObservation, - observerTypes.VoteType_SuccessObservation, - observerTypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, }, }, + observerStartingEmissions: sdkmath.NewInt(100), // total reward units would be 0 as no votes match the ballot status totalRewardsForBlock: sdkmath.NewInt(100), expectedRewards: map[string]int64{ @@ -384,19 +391,21 @@ func TestDistributeObserverRewards(t *testing.T) { observerSet.ObserverList[2]: 75, observerSet.ObserverList[3]: 75, }, - ballotStatus: observerTypes.BallotStatus_BallotFinalized_FailureObservation, - slashAmount: sdkmath.NewInt(25), + ballotStatus: observertypes.BallotStatus_BallotFinalized_FailureObservation, + slashAmount: sdkmath.NewInt(25), + rewardsPerBlock: emissionstypes.BlockReward, }, { name: "slashed to zero if slash amount is greater than available emissions", - votes: [][]observerTypes.VoteType{ + votes: [][]observertypes.VoteType{ { - observerTypes.VoteType_SuccessObservation, - observerTypes.VoteType_SuccessObservation, - observerTypes.VoteType_SuccessObservation, - observerTypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, }, }, + observerStartingEmissions: sdkmath.NewInt(100), // total reward units would be 0 as no votes match the ballot status totalRewardsForBlock: sdkmath.NewInt(100), expectedRewards: map[string]int64{ @@ -405,25 +414,27 @@ func TestDistributeObserverRewards(t *testing.T) { observerSet.ObserverList[2]: 0, observerSet.ObserverList[3]: 0, }, - ballotStatus: observerTypes.BallotStatus_BallotFinalized_FailureObservation, - slashAmount: sdkmath.NewInt(2500), + ballotStatus: observertypes.BallotStatus_BallotFinalized_FailureObservation, + slashAmount: sdkmath.NewInt(2500), + rewardsPerBlock: emissionstypes.BlockReward, }, { name: "withdraw able emissions unchanged if rewards and slashes are equal", - votes: [][]observerTypes.VoteType{ + votes: [][]observertypes.VoteType{ { - observerTypes.VoteType_SuccessObservation, - observerTypes.VoteType_SuccessObservation, - observerTypes.VoteType_SuccessObservation, - observerTypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, }, { - observerTypes.VoteType_FailureObservation, - observerTypes.VoteType_SuccessObservation, - observerTypes.VoteType_SuccessObservation, - observerTypes.VoteType_SuccessObservation, + observertypes.VoteType_FailureObservation, + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, }, }, + observerStartingEmissions: sdkmath.NewInt(100), // total reward units would be 7 as 7 votes match the ballot status, including both ballots totalRewardsForBlock: sdkmath.NewInt(70), expectedRewards: map[string]int64{ @@ -432,19 +443,82 @@ func TestDistributeObserverRewards(t *testing.T) { observerSet.ObserverList[2]: 120, observerSet.ObserverList[3]: 120, }, - ballotStatus: observerTypes.BallotStatus_BallotFinalized_SuccessObservation, + ballotStatus: observertypes.BallotStatus_BallotFinalized_SuccessObservation, + slashAmount: sdkmath.NewInt(25), + rewardsPerBlock: emissionstypes.BlockReward, + }, + { + name: "no rewards if block reward is nil", + votes: [][]observertypes.VoteType{ + { + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, + }, + }, + observerStartingEmissions: sdkmath.NewInt(0), + // total reward units would be 4 as all votes match the ballot status + totalRewardsForBlock: sdkmath.NewInt(0), + expectedRewards: map[string]int64{ + observerSet.ObserverList[0]: 0, + observerSet.ObserverList[1]: 0, + observerSet.ObserverList[2]: 0, + observerSet.ObserverList[3]: 0, + }, + ballotStatus: observertypes.BallotStatus_BallotFinalized_SuccessObservation, slashAmount: sdkmath.NewInt(25), + //rewardsPerBlock: nil, + }, + { + name: "no rewards if block reward is negative", + votes: [][]observertypes.VoteType{ + { + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, + }, + }, + observerStartingEmissions: sdkmath.NewInt(0), + // total reward units would be 4 as all votes match the ballot status + totalRewardsForBlock: sdkmath.NewInt(0), + expectedRewards: map[string]int64{ + observerSet.ObserverList[0]: 0, + observerSet.ObserverList[1]: 0, + observerSet.ObserverList[2]: 0, + observerSet.ObserverList[3]: 0, + }, + ballotStatus: observertypes.BallotStatus_BallotFinalized_SuccessObservation, + slashAmount: sdkmath.NewInt(25), + rewardsPerBlock: sdk.NewDec(1).NegMut(), + }, + { + name: "no rewards if block reward is zero", + votes: [][]observertypes.VoteType{ + { + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, + observertypes.VoteType_SuccessObservation, + }, + }, + observerStartingEmissions: sdkmath.NewInt(0), + // total reward units would be 4 as all votes match the ballot status + totalRewardsForBlock: sdkmath.NewInt(0), + expectedRewards: map[string]int64{ + observerSet.ObserverList[0]: 0, + observerSet.ObserverList[1]: 0, + observerSet.ObserverList[2]: 0, + observerSet.ObserverList[3]: 0, + }, + ballotStatus: observertypes.BallotStatus_BallotFinalized_SuccessObservation, + slashAmount: sdkmath.NewInt(25), + rewardsPerBlock: sdk.ZeroDec(), }, } for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { - for _, observer := range observerSet.ObserverList { - k.SetWithdrawableEmission(ctx, emissionstypes.WithdrawableEmissions{ - Address: observer, - Amount: sdkmath.NewInt(100), - }) - } - // Keeper initialization k, ctx, sk, zk := keepertest.EmissionsKeeper(t) zk.ObserverKeeper.SetObserverSet(ctx, observerSet) @@ -457,24 +531,24 @@ func TestDistributeObserverRewards(t *testing.T) { err := sk.BankKeeper.MintCoins(ctx, emissionstypes.ModuleName, totalRewardCoins) require.NoError(t, err) - // Set starting emission for all observers to 100 so that we can calculate the rewards and slashes + // Set starting emission for all observers to a specified value so that we can calculate the rewards and slashes for _, observer := range observerSet.ObserverList { k.SetWithdrawableEmission(ctx, emissionstypes.WithdrawableEmissions{ Address: observer, - Amount: sdkmath.NewInt(100), + Amount: tc.observerStartingEmissions, }) } // Set the params params := emissionstypes.DefaultParams() params.ObserverSlashAmount = tc.slashAmount - err = k.SetParams(ctx, params) - require.NoError(t, err) + params.BlockRewardAmount = tc.rewardsPerBlock + setEmissionsParams(t, ctx, *k, params) // Set the ballot list ballotIdentifiers := []string{} for i, votes := range tc.votes { - ballot := observerTypes.Ballot{ + ballot := observertypes.Ballot{ BallotIdentifier: "ballot" + string(rune(i)), BallotStatus: tc.ballotStatus, VoterList: observerSet.ObserverList, @@ -483,14 +557,14 @@ func TestDistributeObserverRewards(t *testing.T) { zk.ObserverKeeper.SetBallot(ctx, &ballot) ballotIdentifiers = append(ballotIdentifiers, ballot.BallotIdentifier) } - zk.ObserverKeeper.SetBallotList(ctx, &observerTypes.BallotListForHeight{ + zk.ObserverKeeper.SetBallotList(ctx, &observertypes.BallotListForHeight{ Height: 0, BallotsIndexList: ballotIdentifiers, }) ctx = ctx.WithBlockHeight(100) // Distribute the rewards and check if the rewards are distributed correctly - err = emissionsModule.DistributeObserverRewards(ctx, tc.totalRewardsForBlock, *k, params) + err = emissions.DistributeObserverRewards(ctx, tc.totalRewardsForBlock, *k, params) require.NoError(t, err) for i, observer := range observerSet.ObserverList { @@ -507,3 +581,14 @@ func TestDistributeObserverRewards(t *testing.T) { }) } } + +// setEmissionsParams sets the emissions params in the store without validation +func setEmissionsParams(t *testing.T, ctx sdk.Context, k emissionskeeper.Keeper, params emissionstypes.Params) { + store := ctx.KVStore(k.GetStoreKey()) + bz, err := k.GetCodec().Marshal(¶ms) + if err != nil { + require.NoError(t, err) + } + + store.Set(emissionstypes.KeyPrefix(emissionstypes.ParamsKey), bz) +} diff --git a/x/emissions/keeper/params_test.go b/x/emissions/keeper/params_test.go index 72c752c3ad..41800829fe 100644 --- a/x/emissions/keeper/params_test.go +++ b/x/emissions/keeper/params_test.go @@ -38,7 +38,7 @@ func TestKeeper_GetParams(t *testing.T) { BallotMaturityBlocks: int64(emissionstypes.BallotMaturityBlocks), BlockRewardAmount: emissionstypes.BlockReward, }, - constainsErr: "slash amount cannot be less than 0", + constainsErr: "slash amount must not be negative", }, { name: "validator emission percentage too high", @@ -122,7 +122,7 @@ func TestKeeper_GetParams(t *testing.T) { BallotMaturityBlocks: -100, BlockRewardAmount: emissionstypes.BlockReward, }, - constainsErr: "ballot maturity types must be gte 0", + constainsErr: "ballot maturity types must not be negative", }, { name: "block reward amount too low", @@ -134,7 +134,7 @@ func TestKeeper_GetParams(t *testing.T) { BallotMaturityBlocks: int64(emissionstypes.BallotMaturityBlocks), BlockRewardAmount: sdkmath.LegacyMustNewDecFromStr("-10.00"), }, - constainsErr: "block reward amount cannot be less than 0", + constainsErr: "block reward amount must not be negative", }, } for _, tt := range tests { diff --git a/x/emissions/module.go b/x/emissions/module.go index 08676efb54..b445cf3d44 100644 --- a/x/emissions/module.go +++ b/x/emissions/module.go @@ -150,7 +150,7 @@ func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.Ra InitGenesis(ctx, am.keeper, genState) am.keeper.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) - am.keeper.GetAuthKeeper().GetModuleAccount(ctx, types.UndistributedTssRewardsPool) + am.keeper.GetAuthKeeper().GetModuleAccount(ctx, types.UndistributedTSSRewardsPool) am.keeper.GetAuthKeeper().GetModuleAccount(ctx, types.UndistributedObserverRewardsPool) return []abci.ValidatorUpdate{} diff --git a/x/emissions/types/keys.go b/x/emissions/types/keys.go index 0c71be1284..b800b1a529 100644 --- a/x/emissions/types/keys.go +++ b/x/emissions/types/keys.go @@ -10,7 +10,7 @@ const ( // ModuleName defines the module name ModuleName = "emissions" UndistributedObserverRewardsPool = ModuleName + "Observers" - UndistributedTssRewardsPool = ModuleName + "Tss" + UndistributedTSSRewardsPool = ModuleName + "Tss" // StoreKey defines the primary module store key StoreKey = ModuleName @@ -42,7 +42,7 @@ const ( var ( EmissionsModuleAddress = authtypes.NewModuleAddress(ModuleName) UndistributedObserverRewardsPoolAddress = authtypes.NewModuleAddress(UndistributedObserverRewardsPool) - UndistributedTssRewardsPoolAddress = authtypes.NewModuleAddress(UndistributedTssRewardsPool) + UndistributedTssRewardsPoolAddress = authtypes.NewModuleAddress(UndistributedTSSRewardsPool) // BlockReward is an initial block reward amount when emissions module was initialized. // The current value can be obtained from by querying the params BlockReward = sdk.MustNewDecFromStr("9620949074074074074.074070733466756687") diff --git a/x/emissions/types/message_update_params_test.go b/x/emissions/types/message_update_params_test.go index cf02f19000..c5722a19f2 100644 --- a/x/emissions/types/message_update_params_test.go +++ b/x/emissions/types/message_update_params_test.go @@ -29,7 +29,7 @@ func TestMsgUpdateParams_ValidateBasic(t *testing.T) { Params: params, } err := msg.ValidateBasic() - require.ErrorContains(t, err, "block reward amount cannot be less than 0") + require.ErrorContains(t, err, "block reward amount must not be negative") }) t.Run("valid", func(t *testing.T) { diff --git a/x/emissions/types/params.go b/x/emissions/types/params.go index 715c615b9d..582de6ffd4 100644 --- a/x/emissions/types/params.go +++ b/x/emissions/types/params.go @@ -63,7 +63,7 @@ func validateValidatorEmissionPercentage(i interface{}) error { if dec.GT(sdk.OneDec()) { return fmt.Errorf("validator emission percentage cannot be more than 100 percent") } - if dec.LT(sdk.ZeroDec()) { + if dec.IsNegative() { return fmt.Errorf("validator emission percentage cannot be less than 0 percent") } return nil @@ -78,7 +78,7 @@ func validateObserverEmissionPercentage(i interface{}) error { if dec.GT(sdk.OneDec()) { return fmt.Errorf("observer emission percentage cannot be more than 100 percent") } - if dec.LT(sdk.ZeroDec()) { + if dec.IsNegative() { return fmt.Errorf("observer emission percentage cannot be less than 0 percent") } return nil @@ -93,7 +93,7 @@ func validateTssEmissionPercentage(i interface{}) error { if dec.GT(sdk.OneDec()) { return fmt.Errorf("tss emission percentage cannot be more than 100 percent") } - if dec.LT(sdk.ZeroDec()) { + if dec.IsNegative() { return fmt.Errorf("tss emission percentage cannot be less than 0 percent") } return nil @@ -104,8 +104,11 @@ func validateObserverSlashAmount(i interface{}) error { if !ok { return fmt.Errorf("invalid parameter type: %T", i) } - if v.LT(sdk.ZeroInt()) { - return fmt.Errorf("slash amount cannot be less than 0") + if v.IsNil() { + return fmt.Errorf("observer slash amount cannot be nil") + } + if v.IsNegative() { + return fmt.Errorf("slash amount must not be negative") } return nil } @@ -117,7 +120,7 @@ func validateBallotMaturityBlocks(i interface{}) error { } if v < 0 { - return fmt.Errorf("ballot maturity types must be gte 0") + return fmt.Errorf("ballot maturity types must not be negative") } return nil @@ -128,8 +131,11 @@ func validateBlockRewardsAmount(i interface{}) error { if !ok { return fmt.Errorf("invalid parameter type: %T", i) } - if v.LT(sdkmath.LegacyZeroDec()) { - return fmt.Errorf("block reward amount cannot be less than 0") + if v.IsNil() { + return fmt.Errorf("block reward amount cannot be nil") + } + if v.IsNegative() { + return fmt.Errorf("block reward amount must not be negative") } return nil } diff --git a/x/emissions/types/params_test.go b/x/emissions/types/params_test.go index 6a8e90a98b..5facd08f9f 100644 --- a/x/emissions/types/params_test.go +++ b/x/emissions/types/params_test.go @@ -56,6 +56,7 @@ func TestValidateObserverSlashAmount(t *testing.T) { require.Error(t, validateObserverSlashAmount(10)) require.Error(t, validateObserverSlashAmount("10")) require.Error(t, validateObserverSlashAmount(sdkmath.NewInt(-10))) // Less than 0 + require.Error(t, validateObserverSlashAmount(nil)) require.NoError(t, validateObserverSlashAmount(sdkmath.NewInt(10))) } @@ -69,6 +70,7 @@ func TestValidateBlockRewardAmount(t *testing.T) { require.Error(t, validateBlockRewardsAmount("0.50")) require.Error(t, validateBlockRewardsAmount("-0.50")) require.Error(t, validateBlockRewardsAmount(sdkmath.LegacyMustNewDecFromStr("-0.50"))) + require.Error(t, validateBlockRewardsAmount(nil)) require.NoError(t, validateBlockRewardsAmount(sdkmath.LegacyMustNewDecFromStr("0.50"))) require.NoError(t, validateBlockRewardsAmount(sdkmath.LegacyZeroDec())) require.NoError(t, validateBlockRewardsAmount(BlockReward)) @@ -113,7 +115,7 @@ func TestValidate(t *testing.T) { t.Run("should error for negative block reward amount", func(t *testing.T) { params := NewParams() params.BlockRewardAmount = sdkmath.LegacyMustNewDecFromStr("-1.30") - require.ErrorContains(t, params.Validate(), "block reward amount cannot be less than 0") + require.ErrorContains(t, params.Validate(), "block reward amount must not be negative") }) } func TestParamsString(t *testing.T) { diff --git a/x/fungible/keeper/deposits.go b/x/fungible/keeper/deposits.go index 48c1deb771..b6a0c72c4e 100644 --- a/x/fungible/keeper/deposits.go +++ b/x/fungible/keeper/deposits.go @@ -35,6 +35,7 @@ func (k Keeper) ZRC20DepositAndCallContract( coinType coin.CoinType, asset string, protocolContractVersion crosschaintypes.ProtocolContractVersion, + isCrossChainCall bool, ) (*evmtypes.MsgEthereumTxResponse, bool, error) { // get ZRC20 contract zrc20Contract, _, err := k.getAndCheckZRC20(ctx, amount, senderChainID, coinType, asset) @@ -44,7 +45,17 @@ func (k Keeper) ZRC20DepositAndCallContract( // handle the deposit for protocol contract version 2 if protocolContractVersion == crosschaintypes.ProtocolContractVersion_V2 { - return k.ProcessV2Deposit(ctx, from, senderChainID, zrc20Contract, to, amount, message, coinType) + return k.ProcessV2Deposit( + ctx, + from, + senderChainID, + zrc20Contract, + to, + amount, + message, + coinType, + isCrossChainCall, + ) } // check if the receiver is a contract diff --git a/x/fungible/keeper/deposits_test.go b/x/fungible/keeper/deposits_test.go index f636ca19bd..3958ae191e 100644 --- a/x/fungible/keeper/deposits_test.go +++ b/x/fungible/keeper/deposits_test.go @@ -45,6 +45,7 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { coin.CoinType_Gas, sample.EthAddress().String(), crosschaintypes.ProtocolContractVersion_V1, + false, ) require.NoError(t, err) require.False(t, contractCall) @@ -78,6 +79,7 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { coin.CoinType_ERC20, assetAddress, crosschaintypes.ProtocolContractVersion_V1, + false, ) require.NoError(t, err) require.False(t, contractCall) @@ -111,6 +113,7 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { coin.CoinType_ERC20, assetAddress, crosschaintypes.ProtocolContractVersion_V1, + false, ) require.ErrorIs(t, err, types.ErrCallNonContract) }) @@ -153,6 +156,7 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { coin.CoinType_Gas, sample.EthAddress().String(), crosschaintypes.ProtocolContractVersion_V1, + false, ) require.NoError(t, err) require.False(t, contractCall) @@ -191,6 +195,7 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { coin.CoinType_Gas, sample.EthAddress().String(), crosschaintypes.ProtocolContractVersion_V1, + false, ) require.ErrorIs(t, err, types.ErrPausedZRC20) }) @@ -233,6 +238,7 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { coin.CoinType_Gas, sample.EthAddress().String(), crosschaintypes.ProtocolContractVersion_V1, + false, ) require.ErrorIs(t, err, types.ErrForeignCoinCapReached) }) @@ -260,6 +266,7 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { coin.CoinType_Gas, sample.EthAddress().String(), crosschaintypes.ProtocolContractVersion_V1, + false, ) require.ErrorIs(t, err, crosschaintypes.ErrGasCoinNotFound) }) @@ -287,6 +294,7 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { coin.CoinType_ERC20, assetAddress, crosschaintypes.ProtocolContractVersion_V1, + false, ) require.ErrorIs(t, err, crosschaintypes.ErrForeignCoinNotFound) }) @@ -318,6 +326,7 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { coin.CoinType_Gas, sample.EthAddress().String(), crosschaintypes.ProtocolContractVersion_V1, + false, ) require.NoError(t, err) require.True(t, contractCall) @@ -357,6 +366,7 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { coin.CoinType_Gas, sample.EthAddress().String(), crosschaintypes.ProtocolContractVersion_V1, + false, ) require.Error(t, err) require.True(t, contractCall) @@ -390,6 +400,7 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { coin.CoinType_Gas, sample.EthAddress().String(), crosschaintypes.ProtocolContractVersion_V2, + false, ) require.NoError(t, err) require.False(t, contractCall) diff --git a/x/fungible/keeper/v2_deposits.go b/x/fungible/keeper/v2_deposits.go index 812f2c0b81..16486dcbff 100644 --- a/x/fungible/keeper/v2_deposits.go +++ b/x/fungible/keeper/v2_deposits.go @@ -25,25 +25,27 @@ func (k Keeper) ProcessV2Deposit( amount *big.Int, message []byte, coinType coin.CoinType, + isCrossChainCall bool, ) (*evmtypes.MsgEthereumTxResponse, bool, error) { context := systemcontract.ZContext{ - Origin: from, - Sender: ethcommon.Address{}, + Origin: []byte{}, + Sender: ethcommon.BytesToAddress(from), ChainID: big.NewInt(senderChainID), } - if len(message) == 0 { - // simple deposit - res, err := k.DepositZRC20(ctx, zrc20Addr, to, amount) - return res, false, err - } else if coinType == coin.CoinType_NoAssetCall { + if coinType == coin.CoinType_NoAssetCall { // simple call res, err := k.CallExecute(ctx, context, zrc20Addr, amount, to, message) return res, true, err + } else if isCrossChainCall { + // call with asset + res, err := k.CallDepositAndCallZRC20(ctx, context, zrc20Addr, amount, to, message) + return res, true, err } - // deposit and call - res, err := k.CallDepositAndCallZRC20(ctx, context, zrc20Addr, amount, to, message) - return res, true, err + + // simple deposit + res, err := k.DepositZRC20(ctx, zrc20Addr, to, amount) + return res, false, err } // ProcessV2RevertDeposit handles a revert deposit from an inbound tx with protocol version 2 diff --git a/x/fungible/keeper/v2_deposits_test.go b/x/fungible/keeper/v2_deposits_test.go index e470480776..7b13eeef89 100644 --- a/x/fungible/keeper/v2_deposits_test.go +++ b/x/fungible/keeper/v2_deposits_test.go @@ -15,6 +15,40 @@ import ( "testing" ) +// getTestDAppNoMessageIndex queries the no message index of the test dapp v2 contract +func getTestDAppNoMessageIndex( + t *testing.T, + ctx sdk.Context, + k fungiblekeeper.Keeper, + contract, + account common.Address, +) string { + testDAppABI, err := testdappv2.TestDAppV2MetaData.GetAbi() + require.NoError(t, err) + res, err := k.CallEVM( + ctx, + *testDAppABI, + types.ModuleAddressEVM, + contract, + fungiblekeeper.BigIntZero, + nil, + false, + false, + "getNoMessageIndex", + account, + ) + require.NoError(t, err) + + unpacked, err := testDAppABI.Unpack("getNoMessageIndex", res.Ret) + require.NoError(t, err) + require.Len(t, unpacked, 1) + + index, ok := unpacked[0].(string) + require.True(t, ok) + + return index +} + // deployTestDAppV2 deploys the test dapp v2 contract and returns its address func deployTestDAppV2(t *testing.T, ctx sdk.Context, k *fungiblekeeper.Keeper, evmk types.EVMKeeper) common.Address { testDAppV2, err := k.DeployContract(ctx, testdappv2.TestDAppV2MetaData) @@ -105,6 +139,41 @@ func TestKeeper_ProcessV2Deposit(t *testing.T) { big.NewInt(42), []byte{}, coin.CoinType_Gas, + false, + ) + + // ASSERT + require.NoError(t, err) + require.False(t, contractCall) + + balance, err := k.BalanceOfZRC4(ctx, zrc20, receiver) + require.NoError(t, err) + require.Equal(t, big.NewInt(42), balance) + }) + + t.Run("should process no-call deposit, message should be ignored", func(t *testing.T) { + // ARRANGE + k, ctx, sdkk, _ := keepertest.FungibleKeeper(t) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + chainID := chains.DefaultChainsList()[0].ChainId + receiver := sample.EthAddress() + + // deploy the system contracts + deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) + zrc20 := setupGasCoin(t, ctx, k, sdkk.EvmKeeper, chainID, "foobar", "foobar") + + // ACT + _, contractCall, err := k.ProcessV2Deposit( + ctx, + sample.EthAddress().Bytes(), + chainID, + zrc20, + receiver, + big.NewInt(42), + []byte("foo"), + coin.CoinType_Gas, + false, ) // ASSERT @@ -140,6 +209,7 @@ func TestKeeper_ProcessV2Deposit(t *testing.T) { big.NewInt(82), []byte("foo"), coin.CoinType_Gas, + true, ) // ASSERT @@ -150,4 +220,52 @@ func TestKeeper_ProcessV2Deposit(t *testing.T) { require.Equal(t, big.NewInt(82), balance) assertTestDAppV2MessageAndAmount(t, ctx, k, testDapp, "foo", 82) }) + + t.Run("should process deposit and call with no message", func(t *testing.T) { + // ARRANGE + k, ctx, sdkk, _ := keepertest.FungibleKeeper(t) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + chainID := chains.DefaultChainsList()[0].ChainId + + // deploy test dapp + testDapp := deployTestDAppV2(t, ctx, k, sdkk.EvmKeeper) + + // deploy the system contracts + deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) + zrc20 := setupGasCoin(t, ctx, k, sdkk.EvmKeeper, chainID, "foobar", "foobar") + + sender := sample.EthAddress() + + // ACT + _, contractCall, err := k.ProcessV2Deposit( + ctx, + sender.Bytes(), + chainID, + zrc20, + testDapp, + big.NewInt(82), + []byte{}, + coin.CoinType_Gas, + true, + ) + + // ASSERT + require.NoError(t, err) + require.True(t, contractCall) + balance, err := k.BalanceOfZRC4(ctx, zrc20, testDapp) + require.NoError(t, err) + require.Equal(t, big.NewInt(82), balance) + + messageIndex := getTestDAppNoMessageIndex(t, ctx, *k, testDapp, sender) + + assertTestDAppV2MessageAndAmount( + t, + ctx, + k, + testDapp, + messageIndex, + 82, + ) + }) } diff --git a/x/fungible/keeper/v2_evm.go b/x/fungible/keeper/v2_evm.go index 4f76606151..b2b76dc428 100644 --- a/x/fungible/keeper/v2_evm.go +++ b/x/fungible/keeper/v2_evm.go @@ -187,7 +187,7 @@ func (k Keeper) CallExecuteRevert( revert.RevertContext{ Sender: common.HexToAddress(inboundSender), Asset: zrc20, - Amount: amount.Uint64(), + Amount: amount, RevertMessage: message, }, ) @@ -241,7 +241,7 @@ func (k Keeper) CallDepositAndRevert( revert.RevertContext{ Sender: common.HexToAddress(inboundSender), Asset: zrc20, - Amount: amount.Uint64(), + Amount: amount, RevertMessage: message, }, ) diff --git a/x/fungible/keeper/zrc20_cosmos_coin_mapping_test.go b/x/fungible/keeper/zrc20_cosmos_coin_mapping_test.go new file mode 100644 index 0000000000..16803a917c --- /dev/null +++ b/x/fungible/keeper/zrc20_cosmos_coin_mapping_test.go @@ -0,0 +1,414 @@ +package keeper_test + +import ( + "math/big" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/node/testutil/keeper" + "github.com/zeta-chain/node/testutil/sample" + fungiblekeeper "github.com/zeta-chain/node/x/fungible/keeper" + fungibletypes "github.com/zeta-chain/node/x/fungible/types" + "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" +) + +func Test_LockZRC20(t *testing.T) { + zrc20ABI, err := zrc20.ZRC20MetaData.GetAbi() + require.NoError(t, err) + + ts := setupChain(t) + + owner := fungibletypes.ModuleAddressEVM + locker := sample.EthAddress() + depositTotal := big.NewInt(1000) + allowanceTotal := big.NewInt(100) + higherThanAllowance := big.NewInt(101) + smallerThanAllowance := big.NewInt(99) + + // Make sure locker account exists in state. + accAddress := sdk.AccAddress(locker.Bytes()) + ts.fungibleKeeper.GetAuthKeeper().SetAccount(ts.ctx, authtypes.NewBaseAccount(accAddress, nil, 0, 0)) + + // Deposit 1000 ZRC20 tokens into the fungible. + ts.fungibleKeeper.DepositZRC20(ts.ctx, ts.zrc20Address, owner, depositTotal) + + t.Run("should fail when trying to lock zero amount", func(t *testing.T) { + // Check lock with zero amount. + err = ts.fungibleKeeper.LockZRC20(ts.ctx, zrc20ABI, ts.zrc20Address, locker, owner, locker, big.NewInt(0)) + require.Error(t, err) + require.ErrorIs(t, err, fungibletypes.ErrInvalidAmount) + }) + + t.Run("should fail when ZRC20 ABI is not properly initialized", func(t *testing.T) { + // Check lock with nil ABI. + err = ts.fungibleKeeper.LockZRC20(ts.ctx, nil, ts.zrc20Address, locker, owner, locker, big.NewInt(10)) + require.Error(t, err) + require.ErrorIs(t, err, fungibletypes.ErrZRC20NilABI) + }) + + t.Run("should fail when trying to lock a zero address ZRC20", func(t *testing.T) { + // Check lock with ZRC20 zero address. + err = ts.fungibleKeeper.LockZRC20(ts.ctx, zrc20ABI, common.Address{}, locker, owner, locker, big.NewInt(10)) + require.Error(t, err) + require.ErrorIs(t, err, fungibletypes.ErrZRC20ZeroAddress) + }) + + t.Run("should fail when trying to lock a non whitelisted ZRC20", func(t *testing.T) { + // Check lock with non whitelisted ZRC20. + err = ts.fungibleKeeper.LockZRC20(ts.ctx, zrc20ABI, sample.EthAddress(), locker, owner, locker, big.NewInt(10)) + require.Error(t, err) + require.ErrorIs(t, err, fungibletypes.ErrZRC20NotWhiteListed) + }) + + t.Run("should fail when trying to lock a higher amount than totalSupply", func(t *testing.T) { + approveAllowance(t, ts, zrc20ABI, owner, locker, big.NewInt(1000000000000000)) + + // Check lock with higher amount than totalSupply. + err = ts.fungibleKeeper.LockZRC20( + ts.ctx, + zrc20ABI, + ts.zrc20Address, + locker, + owner, + locker, + big.NewInt(1000000000000000), + ) + require.Error(t, err) + require.ErrorIs(t, err, fungibletypes.ErrInvalidAmount) + }) + + t.Run("should fail when trying to lock a higher amount than owned balance", func(t *testing.T) { + approveAllowance(t, ts, zrc20ABI, owner, locker, big.NewInt(1001)) + + // Check allowance smaller, equal and bigger than the amount. + err = ts.fungibleKeeper.LockZRC20(ts.ctx, zrc20ABI, ts.zrc20Address, locker, owner, locker, big.NewInt(1001)) + require.Error(t, err) + + // We do not check in LockZRC20 explicitly if the amount is bigger than the balance. + // Instead, the ERC20 transferFrom function will revert the transaction if the amount is bigger than the balance. + require.Contains(t, err.Error(), "execution reverted") + }) + + t.Run("should fail when trying to lock an amount higher than approved", func(t *testing.T) { + approveAllowance(t, ts, zrc20ABI, owner, locker, allowanceTotal) + + // Check allowance smaller, equal and bigger than the amount. + err = ts.fungibleKeeper.LockZRC20(ts.ctx, zrc20ABI, ts.zrc20Address, locker, owner, locker, higherThanAllowance) + require.Error(t, err) + require.Contains(t, err.Error(), "invalid allowance, got 100") + }) + + t.Run("should pass when trying to lock a valid approved amount", func(t *testing.T) { + approveAllowance(t, ts, zrc20ABI, owner, locker, allowanceTotal) + + err = ts.fungibleKeeper.LockZRC20(ts.ctx, zrc20ABI, ts.zrc20Address, locker, owner, locker, allowanceTotal) + require.NoError(t, err) + + ownerBalance, err := ts.fungibleKeeper.ZRC20BalanceOf(ts.ctx, zrc20ABI, ts.zrc20Address, owner) + require.NoError(t, err) + require.Equal(t, uint64(900), ownerBalance.Uint64()) + + lockerBalance, err := ts.fungibleKeeper.ZRC20BalanceOf(ts.ctx, zrc20ABI, ts.zrc20Address, locker) + require.NoError(t, err) + require.Equal(t, uint64(100), lockerBalance.Uint64()) + }) + + t.Run("should pass when trying to lock an amount smaller than approved", func(t *testing.T) { + approveAllowance(t, ts, zrc20ABI, owner, locker, allowanceTotal) + + err = ts.fungibleKeeper.LockZRC20( + ts.ctx, + zrc20ABI, + ts.zrc20Address, + locker, + owner, + locker, + smallerThanAllowance, + ) + require.NoError(t, err) + + // Note that balances are cumulative for all tests. That's why we check 801 and 199 here. + ownerBalance, err := ts.fungibleKeeper.ZRC20BalanceOf(ts.ctx, zrc20ABI, ts.zrc20Address, owner) + require.NoError(t, err) + require.Equal(t, uint64(801), ownerBalance.Uint64()) + + lockerBalance, err := ts.fungibleKeeper.ZRC20BalanceOf(ts.ctx, zrc20ABI, ts.zrc20Address, locker) + require.NoError(t, err) + require.Equal(t, uint64(199), lockerBalance.Uint64()) + }) +} + +func Test_UnlockZRC20(t *testing.T) { + zrc20ABI, err := zrc20.ZRC20MetaData.GetAbi() + require.NoError(t, err) + + ts := setupChain(t) + + owner := fungibletypes.ModuleAddressEVM + locker := sample.EthAddress() + depositTotal := big.NewInt(1000) + allowanceTotal := big.NewInt(100) + + // Make sure locker account exists in state. + accAddress := sdk.AccAddress(locker.Bytes()) + ts.fungibleKeeper.GetAuthKeeper().SetAccount(ts.ctx, authtypes.NewBaseAccount(accAddress, nil, 0, 0)) + + // Deposit 1000 ZRC20 tokens into the fungible. + ts.fungibleKeeper.DepositZRC20(ts.ctx, ts.zrc20Address, owner, depositTotal) + + // Approve allowance for locker to spend owner's ZRC20 tokens. + approveAllowance(t, ts, zrc20ABI, owner, locker, allowanceTotal) + + // Lock 100 ZRC20. + err = ts.fungibleKeeper.LockZRC20(ts.ctx, zrc20ABI, ts.zrc20Address, locker, owner, locker, allowanceTotal) + require.NoError(t, err) + + t.Run("should fail when trying to unlock zero amount", func(t *testing.T) { + err = ts.fungibleKeeper.UnlockZRC20(ts.ctx, zrc20ABI, ts.zrc20Address, owner, locker, big.NewInt(0)) + require.Error(t, err) + require.ErrorIs(t, err, fungibletypes.ErrInvalidAmount) + }) + + t.Run("should fail when ZRC20 ABI is not properly initialized", func(t *testing.T) { + err = ts.fungibleKeeper.UnlockZRC20(ts.ctx, nil, ts.zrc20Address, owner, locker, big.NewInt(10)) + require.Error(t, err) + require.ErrorIs(t, err, fungibletypes.ErrZRC20NilABI) + }) + + t.Run("should fail when trying to unlock a zero address ZRC20", func(t *testing.T) { + err = ts.fungibleKeeper.UnlockZRC20(ts.ctx, zrc20ABI, common.Address{}, owner, locker, big.NewInt(10)) + require.Error(t, err) + require.ErrorIs(t, err, fungibletypes.ErrZRC20ZeroAddress) + }) + + t.Run("should fail when trying to unlock a non whitelisted ZRC20", func(t *testing.T) { + err = ts.fungibleKeeper.UnlockZRC20(ts.ctx, zrc20ABI, sample.EthAddress(), owner, locker, big.NewInt(10)) + require.Error(t, err) + require.ErrorIs(t, err, fungibletypes.ErrZRC20NotWhiteListed) + }) + + t.Run("should fail when trying to unlock an amount bigger than locker's balance", func(t *testing.T) { + err = ts.fungibleKeeper.UnlockZRC20(ts.ctx, zrc20ABI, ts.zrc20Address, owner, locker, big.NewInt(1001)) + require.Error(t, err) + require.Contains(t, err.Error(), "invalid balance, got 100") + }) + + t.Run("should pass when trying to unlock a correct amount", func(t *testing.T) { + err = ts.fungibleKeeper.UnlockZRC20(ts.ctx, zrc20ABI, ts.zrc20Address, owner, locker, allowanceTotal) + require.NoError(t, err) + + ownerBalance, err := ts.fungibleKeeper.ZRC20BalanceOf(ts.ctx, zrc20ABI, ts.zrc20Address, owner) + require.NoError(t, err) + require.Equal(t, uint64(1000), ownerBalance.Uint64()) + + lockerBalance, err := ts.fungibleKeeper.ZRC20BalanceOf(ts.ctx, zrc20ABI, ts.zrc20Address, locker) + require.NoError(t, err) + require.Equal(t, uint64(0), lockerBalance.Uint64()) + }) +} + +func Test_CheckZRC20Allowance(t *testing.T) { + zrc20ABI, err := zrc20.ZRC20MetaData.GetAbi() + require.NoError(t, err) + + ts := setupChain(t) + + owner := fungibletypes.ModuleAddressEVM + spender := sample.EthAddress() + depositTotal := big.NewInt(1000) + allowanceTotal := big.NewInt(100) + higherThanAllowance := big.NewInt(101) + smallerThanAllowance := big.NewInt(99) + + // Make sure locker account exists in state. + accAddress := sdk.AccAddress(spender.Bytes()) + ts.fungibleKeeper.GetAuthKeeper().SetAccount(ts.ctx, authtypes.NewBaseAccount(accAddress, nil, 0, 0)) + + // Deposit ZRC20 tokens into the fungible. + ts.fungibleKeeper.DepositZRC20(ts.ctx, ts.zrc20Address, fungibletypes.ModuleAddressEVM, depositTotal) + + t.Run("should fail when checking zero amount", func(t *testing.T) { + err = ts.fungibleKeeper.CheckZRC20Allowance(ts.ctx, zrc20ABI, owner, spender, ts.zrc20Address, big.NewInt(0)) + require.Error(t, err) + require.ErrorAs(t, err, &fungibletypes.ErrInvalidAmount) + }) + + t.Run("should fail when allowance is not approved", func(t *testing.T) { + err = ts.fungibleKeeper.CheckZRC20Allowance(ts.ctx, zrc20ABI, owner, spender, ts.zrc20Address, big.NewInt(10)) + require.Error(t, err) + require.Contains(t, err.Error(), "invalid allowance, got 0") + }) + + t.Run("should fail when checking a higher amount than approved", func(t *testing.T) { + approveAllowance(t, ts, zrc20ABI, owner, spender, allowanceTotal) + + err = ts.fungibleKeeper.CheckZRC20Allowance( + ts.ctx, + zrc20ABI, + owner, + spender, + ts.zrc20Address, + higherThanAllowance, + ) + require.Error(t, err) + require.Contains(t, err.Error(), "invalid allowance, got 100") + }) + + t.Run("should pass when checking the same amount as approved", func(t *testing.T) { + approveAllowance(t, ts, zrc20ABI, owner, spender, allowanceTotal) + + err = ts.fungibleKeeper.CheckZRC20Allowance(ts.ctx, zrc20ABI, owner, spender, ts.zrc20Address, allowanceTotal) + require.NoError(t, err) + }) + + t.Run("should pass when checking a lower amount than approved", func(t *testing.T) { + approveAllowance(t, ts, zrc20ABI, owner, spender, allowanceTotal) + + err = ts.fungibleKeeper.CheckZRC20Allowance( + ts.ctx, + zrc20ABI, + owner, + spender, + ts.zrc20Address, + smallerThanAllowance, + ) + require.NoError(t, err) + }) +} + +func Test_IsValidZRC20(t *testing.T) { + ts := setupChain(t) + + t.Run("should fail when zrc20 address is zero", func(t *testing.T) { + err := ts.fungibleKeeper.IsValidZRC20(ts.ctx, common.Address{}) + require.Error(t, err) + require.ErrorAs(t, err, &fungibletypes.ErrZeroAddress) + }) + + t.Run("should fail when zrc20 is not whitelisted", func(t *testing.T) { + err := ts.fungibleKeeper.IsValidZRC20(ts.ctx, sample.EthAddress()) + require.Error(t, err) + require.ErrorAs(t, err, &fungibletypes.ErrZRC20NotWhiteListed) + }) + + t.Run("should pass when zrc20 is a valid whitelisted token", func(t *testing.T) { + err := ts.fungibleKeeper.IsValidZRC20(ts.ctx, ts.zrc20Address) + require.NoError(t, err) + }) +} + +func Test_IsValidDepositAmount(t *testing.T) { + ts := setupChain(t) + + t.Run("should fail when any input is nil", func(t *testing.T) { + isValid := ts.fungibleKeeper.IsValidDepositAmount(nil, big.NewInt(0), big.NewInt(0)) + require.False(t, isValid) + + isValid = ts.fungibleKeeper.IsValidDepositAmount(big.NewInt(0), nil, big.NewInt(0)) + require.False(t, isValid) + + isValid = ts.fungibleKeeper.IsValidDepositAmount(big.NewInt(0), big.NewInt(0), nil) + require.False(t, isValid) + }) + + t.Run("should fail when alreadyLocked + amountToDeposit > totalSupply", func(t *testing.T) { + isValid := ts.fungibleKeeper.IsValidDepositAmount(big.NewInt(1000), big.NewInt(500), big.NewInt(501)) + require.False(t, isValid) + }) + + t.Run("should pass when alreadyLocked + amountToDeposit = totalSupply", func(t *testing.T) { + isValid := ts.fungibleKeeper.IsValidDepositAmount(big.NewInt(1000), big.NewInt(500), big.NewInt(500)) + require.True(t, isValid) + }) + + t.Run("should pass when alreadyLocked + amountToDeposit < totalSupply", func(t *testing.T) { + isValid := ts.fungibleKeeper.IsValidDepositAmount(big.NewInt(1000), big.NewInt(500), big.NewInt(499)) + require.True(t, isValid) + }) +} + +/* + Test utils. +*/ + +type testSuite struct { + ctx sdk.Context + fungibleKeeper *fungiblekeeper.Keeper + sdkKeepers keeper.SDKKeepers + zrc20Address common.Address +} + +func setupChain(t *testing.T) testSuite { + // Initialize basic parameters to mock the chain. + fungibleKeeper, ctx, sdkKeepers, _ := keeper.FungibleKeeper(t) + chainID := getValidChainID(t) + + // Make sure the account store is initialized. + // This is completely needed for accounts to be created in the state. + fungibleKeeper.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + + // Deploy system contracts in order to deploy a ZRC20 token. + deploySystemContracts(t, ctx, fungibleKeeper, sdkKeepers.EvmKeeper) + + zrc20Address := setupGasCoin(t, ctx, fungibleKeeper, sdkKeepers.EvmKeeper, chainID, "ZRC20", "ZRC20") + + return testSuite{ + ctx, + fungibleKeeper, + sdkKeepers, + zrc20Address, + } +} + +func approveAllowance(t *testing.T, ts testSuite, zrc20ABI *abi.ABI, owner, spender common.Address, amount *big.Int) { + resAllowance, err := callEVM( + t, + ts.ctx, + ts.fungibleKeeper, + zrc20ABI, + owner, + ts.zrc20Address, + "approve", + []interface{}{spender, amount}, + ) + require.NoError(t, err, "error allowing bank to spend ZRC20 tokens") + + allowed, ok := resAllowance[0].(bool) + require.True(t, ok) + require.True(t, allowed) +} + +func callEVM( + t *testing.T, + ctx sdk.Context, + fungibleKeeper *fungiblekeeper.Keeper, + abi *abi.ABI, + from common.Address, + dst common.Address, + method string, + args []interface{}, +) ([]interface{}, error) { + res, err := fungibleKeeper.CallEVM( + ctx, // ctx + *abi, // abi + from, // from + dst, // to + big.NewInt(0), // value + nil, // gasLimit + true, // commit + true, // noEthereumTxEvent + method, // method + args..., // args + ) + require.NoError(t, err, "CallEVM error") + require.Equal(t, "", res.VmError, "res.VmError should be empty") + + ret, err := abi.Methods[method].Outputs.Unpack(res.Ret) + require.NoError(t, err, "Unpack error") + + return ret, nil +} diff --git a/x/fungible/keeper/zrc20_cosmos_coins_mapping.go b/x/fungible/keeper/zrc20_cosmos_coins_mapping.go new file mode 100644 index 0000000000..f7d152f749 --- /dev/null +++ b/x/fungible/keeper/zrc20_cosmos_coins_mapping.go @@ -0,0 +1,192 @@ +package keeper + +import ( + "fmt" + "math/big" + + "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + + "github.com/zeta-chain/node/pkg/crypto" + fungibletypes "github.com/zeta-chain/node/x/fungible/types" +) + +// LockZRC20 locks ZRC20 tokens in the specified address +// The caller must have approved the locker contract to spend the amount of ZRC20 tokens. +// Warning: This function does not mint cosmos coins, if the depositor needs to be rewarded +// it has to be implemented by the caller of this function. +func (k Keeper) LockZRC20( + ctx sdk.Context, + zrc20ABI *abi.ABI, + zrc20Address, spender, owner, locker common.Address, + amount *big.Int, +) error { + // owner is the EOA owner of the ZRC20 tokens. + // locker is the address that will lock the ZRC20 tokens, i.e: bank precompile. + if err := k.CheckZRC20Allowance(ctx, zrc20ABI, owner, locker, zrc20Address, amount); err != nil { + return errors.Wrap(err, "failed allowance check") + } + + // Check amount_to_be_locked <= total_erc20_balance - already_locked + // Max amount of ZRC20 tokens that exists in zEVM are the total supply. + totalSupply, err := k.ZRC20TotalSupply(ctx, zrc20ABI, zrc20Address) + if err != nil { + return errors.Wrap(err, "failed totalSupply check") + } + + // The alreadyLocked amount is the amount of ZRC20 tokens that have been locked by the locker. + // TODO: Implement list of whitelisted locker addresses (https://github.com/zeta-chain/node/issues/2991) + alreadyLocked, err := k.ZRC20BalanceOf(ctx, zrc20ABI, zrc20Address, locker) + if err != nil { + return errors.Wrap(err, "failed getting the ZRC20 already locked amount") + } + + if !k.IsValidDepositAmount(totalSupply, alreadyLocked, amount) { + return errors.Wrap(fungibletypes.ErrInvalidAmount, "amount to be locked is not valid") + } + + // Initiate a transferFrom the owner to the locker. This will lock the ZRC20 tokens. + // locker has to initiate the transaction and have enough allowance from owner. + transferred, err := k.ZRC20TransferFrom(ctx, zrc20ABI, zrc20Address, spender, owner, locker, amount) + if err != nil { + return errors.Wrap(err, "failed executing transferFrom") + } + + if !transferred { + return fmt.Errorf("transferFrom returned false (no success)") + } + + return nil +} + +// UnlockZRC20 unlocks ZRC20 tokens and sends them to the owner. +// Warning: Before unlocking ZRC20 tokens, the caller must check if +// the owner has enough collateral (cosmos coins) to be exchanged (burnt) for the ZRC20 tokens. +func (k Keeper) UnlockZRC20( + ctx sdk.Context, + zrc20ABI *abi.ABI, + zrc20Address, owner, locker common.Address, + amount *big.Int, +) error { + // Check if the account locking the ZRC20 tokens has enough balance. + if err := k.CheckZRC20Balance(ctx, zrc20ABI, zrc20Address, locker, amount); err != nil { + return errors.Wrap(err, "failed balance check") + } + + // transfer from the EOA locking the assets to the owner. + transferred, err := k.ZRC20Transfer(ctx, zrc20ABI, zrc20Address, locker, owner, amount) + if err != nil { + return errors.Wrap(err, "failed executing transfer") + } + + if !transferred { + return fmt.Errorf("transfer returned false (no success)") + } + + return nil +} + +// CheckZRC20Allowance checks if the allowance of ZRC20 tokens, +// is equal or greater than the provided amount. +func (k Keeper) CheckZRC20Allowance( + ctx sdk.Context, + zrc20ABI *abi.ABI, + owner, spender, zrc20Address common.Address, + amount *big.Int, +) error { + if zrc20ABI == nil { + return fungibletypes.ErrZRC20NilABI + } + + if amount.Sign() <= 0 || amount == nil { + return fungibletypes.ErrInvalidAmount + } + + if crypto.IsEmptyAddress(owner) || crypto.IsEmptyAddress(spender) { + return fungibletypes.ErrZeroAddress + } + + if err := k.IsValidZRC20(ctx, zrc20Address); err != nil { + return errors.Wrap(err, "ZRC20 is not valid") + } + + allowanceValue, err := k.ZRC20Allowance(ctx, zrc20ABI, zrc20Address, owner, spender) + if err != nil { + return errors.Wrap(err, "failed while checking spender's allowance") + } + + if allowanceValue.Cmp(amount) < 0 || allowanceValue.Cmp(big.NewInt(0)) <= 0 { + return fmt.Errorf("invalid allowance, got %s, wanted %s", allowanceValue.String(), amount.String()) + } + + return nil +} + +// CheckZRC20Balance checks if the balance of ZRC20 tokens, +// is equal or greater than the provided amount. +func (k Keeper) CheckZRC20Balance( + ctx sdk.Context, + zrc20ABI *abi.ABI, + zrc20Address, owner common.Address, + amount *big.Int, +) error { + if zrc20ABI == nil { + return fungibletypes.ErrZRC20NilABI + } + + if amount.Sign() <= 0 || amount == nil { + return fungibletypes.ErrInvalidAmount + } + + if err := k.IsValidZRC20(ctx, zrc20Address); err != nil { + return errors.Wrap(err, "ZRC20 is not valid") + } + + if crypto.IsEmptyAddress(owner) { + return fungibletypes.ErrZeroAddress + } + + // Check the ZRC20 balance of a given account. + // function balanceOf(address account) + balance, err := k.ZRC20BalanceOf(ctx, zrc20ABI, zrc20Address, owner) + if err != nil { + return errors.Wrap(err, "failed getting owner's ZRC20 balance") + } + + if balance.Cmp(amount) < 0 { + return fmt.Errorf("invalid balance, got %s, wanted %s", balance.String(), amount.String()) + } + + return nil +} + +// IsValidZRC20 returns an error whenever a ZRC20 is not whitelisted or paused. +func (k Keeper) IsValidZRC20(ctx sdk.Context, zrc20Address common.Address) error { + if crypto.IsEmptyAddress(zrc20Address) { + return fungibletypes.ErrZRC20ZeroAddress + } + + t, found := k.GetForeignCoins(ctx, zrc20Address.String()) + if !found { + return fungibletypes.ErrZRC20NotWhiteListed + } + + if t.Paused { + return fungibletypes.ErrPausedZRC20 + } + + return nil +} + +// IsValidDepositAmount checks "totalSupply >= amount_to_be_locked + amount_already_locked". +// A failure here means the user is trying to lock more than the available ZRC20 supply. +// This suggests that an actor is minting ZRC20 tokens out of thin air. +func (k Keeper) IsValidDepositAmount(totalSupply, alreadyLocked, amountToDeposit *big.Int) bool { + if totalSupply == nil || alreadyLocked == nil || amountToDeposit == nil { + return false + } + + return totalSupply.Cmp(alreadyLocked.Add(alreadyLocked, amountToDeposit)) >= 0 +} diff --git a/x/fungible/keeper/zrc20_methods.go b/x/fungible/keeper/zrc20_methods.go new file mode 100644 index 0000000000..5c9f2d4645 --- /dev/null +++ b/x/fungible/keeper/zrc20_methods.go @@ -0,0 +1,305 @@ +package keeper + +import ( + "fmt" + "math/big" + + "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + + "github.com/zeta-chain/node/pkg/crypto" + fungibletypes "github.com/zeta-chain/node/x/fungible/types" +) + +const ( + allowance = "allowance" + balanceOf = "balanceOf" + totalSupply = "totalSupply" + transfer = "transfer" + transferFrom = "transferFrom" +) + +// ZRC20Allowance returns the ZRC20 allowance for a given spender. +// The allowance has to be previously approved by the ZRC20 tokens owner. +func (k Keeper) ZRC20Allowance( + ctx sdk.Context, + zrc20ABI *abi.ABI, + zrc20Address, owner, spender common.Address, +) (*big.Int, error) { + if zrc20ABI == nil { + return nil, fungibletypes.ErrZRC20NilABI + } + + if crypto.IsEmptyAddress(owner) || crypto.IsEmptyAddress(spender) { + return nil, fungibletypes.ErrZeroAddress + } + + if err := k.IsValidZRC20(ctx, zrc20Address); err != nil { + return nil, err + } + + // function allowance(address owner, address spender) + args := []interface{}{owner, spender} + res, err := k.CallEVM( + ctx, + *zrc20ABI, + fungibletypes.ModuleAddressEVM, + zrc20Address, + big.NewInt(0), + nil, + true, + true, + allowance, + args..., + ) + if err != nil { + return nil, errors.Wrap(err, "EVM error calling ZRC20 allowance function") + } + + if res.VmError != "" { + return nil, fmt.Errorf("EVM execution error calling allowance: %s", res.VmError) + } + + ret, err := zrc20ABI.Methods[allowance].Outputs.Unpack(res.Ret) + if err != nil { + return nil, errors.Wrap(err, "failed to unpack ZRC20 allowance return value") + } + + if len(ret) == 0 { + return nil, fmt.Errorf("no data returned from 'allowance' method") + } + + allowanceValue, ok := ret[0].(*big.Int) + if !ok { + return nil, fmt.Errorf("ZRC20 allowance returned an unexpected type") + } + + return allowanceValue, nil +} + +// ZRC20BalanceOf checks the ZRC20 balance of a given EOA. +func (k Keeper) ZRC20BalanceOf( + ctx sdk.Context, + zrc20ABI *abi.ABI, + zrc20Address, owner common.Address, +) (*big.Int, error) { + if zrc20ABI == nil { + return nil, fungibletypes.ErrZRC20NilABI + } + + if crypto.IsEmptyAddress(owner) { + return nil, fungibletypes.ErrZeroAddress + } + + if err := k.IsValidZRC20(ctx, zrc20Address); err != nil { + return nil, err + } + + // function balanceOf(address account) + res, err := k.CallEVM( + ctx, + *zrc20ABI, + fungibletypes.ModuleAddressEVM, + zrc20Address, + big.NewInt(0), + nil, + true, + true, + balanceOf, + owner, + ) + if err != nil { + return nil, errors.Wrap(err, "EVM error calling ZRC20 balanceOf function") + } + + if res.VmError != "" { + return nil, fmt.Errorf("EVM execution error calling balanceOf: %s", res.VmError) + } + + ret, err := zrc20ABI.Methods[balanceOf].Outputs.Unpack(res.Ret) + if err != nil { + return nil, errors.Wrap(err, "failed to unpack ZRC20 balanceOf return value") + } + + if len(ret) == 0 { + return nil, fmt.Errorf("no data returned from 'balanceOf' method") + } + + balance, ok := ret[0].(*big.Int) + if !ok { + return nil, fmt.Errorf("ZRC20 balanceOf returned an unexpected type") + } + + return balance, nil +} + +// ZRC20TotalSupply returns the total supply of a ZRC20 token. +func (k Keeper) ZRC20TotalSupply( + ctx sdk.Context, + zrc20ABI *abi.ABI, + zrc20Address common.Address, +) (*big.Int, error) { + if zrc20ABI == nil { + return nil, fungibletypes.ErrZRC20NilABI + } + + if err := k.IsValidZRC20(ctx, zrc20Address); err != nil { + return nil, err + } + + // function totalSupply() public view virtual override returns (uint256) + res, err := k.CallEVM( + ctx, + *zrc20ABI, + fungibletypes.ModuleAddressEVM, + zrc20Address, + big.NewInt(0), + nil, + true, + true, + totalSupply, + ) + if err != nil { + return nil, errors.Wrap(err, "EVM error calling ZRC20 totalSupply function") + } + + if res.VmError != "" { + return nil, fmt.Errorf("EVM execution error calling totalSupply: %s", res.VmError) + } + + ret, err := zrc20ABI.Methods[totalSupply].Outputs.Unpack(res.Ret) + if err != nil { + return nil, errors.Wrap(err, "failed to unpack ZRC20 totalSupply return value") + } + + if len(ret) == 0 { + return nil, fmt.Errorf("no data returned from 'totalSupply' method") + } + + totalSupply, ok := ret[0].(*big.Int) + if !ok { + return nil, fmt.Errorf("ZRC20 totalSupply returned an unexpected type") + } + + return totalSupply, nil +} + +// ZRC20Transfer transfers ZRC20 tokens from the sender to the recipient. +func (k Keeper) ZRC20Transfer( + ctx sdk.Context, + zrc20ABI *abi.ABI, + zrc20Address, from, to common.Address, + amount *big.Int, +) (bool, error) { + if zrc20ABI == nil { + return false, fungibletypes.ErrZRC20NilABI + } + + if crypto.IsEmptyAddress(from) || crypto.IsEmptyAddress(to) { + return false, fungibletypes.ErrZeroAddress + } + + if err := k.IsValidZRC20(ctx, zrc20Address); err != nil { + return false, err + } + + // function transfer(address recipient, uint256 amount) + args := []interface{}{to, amount} + res, err := k.CallEVM( + ctx, + *zrc20ABI, + from, + zrc20Address, + big.NewInt(0), + nil, + true, + true, + transfer, + args..., + ) + if err != nil { + return false, errors.Wrap(err, "EVM error calling ZRC20 transfer function") + } + + if res.VmError != "" { + return false, fmt.Errorf("EVM execution error in transfer: %s", res.VmError) + } + + ret, err := zrc20ABI.Methods[transfer].Outputs.Unpack(res.Ret) + if err != nil { + return false, errors.Wrap(err, "failed to unpack ZRC20 transfer return value") + } + + if len(ret) == 0 { + return false, fmt.Errorf("no data returned from 'transfer' method") + } + + transferred, ok := ret[0].(bool) + if !ok { + return false, fmt.Errorf("transfer returned an unexpected value") + } + + return transferred, nil +} + +// ZRC20TransferFrom transfers ZRC20 tokens "from" to the EOA "to". +// The transaction is started by the spender. +// Requisite: the original EOA must have approved the spender to spend the tokens. +func (k Keeper) ZRC20TransferFrom( + ctx sdk.Context, + zrc20ABI *abi.ABI, + zrc20Address, spender, from, to common.Address, + amount *big.Int, +) (bool, error) { + if zrc20ABI == nil { + return false, fungibletypes.ErrZRC20NilABI + } + + if crypto.IsEmptyAddress(from) || crypto.IsEmptyAddress(to) || crypto.IsEmptyAddress(spender) { + return false, fungibletypes.ErrZeroAddress + } + + if err := k.IsValidZRC20(ctx, zrc20Address); err != nil { + return false, err + } + + // function transferFrom(address sender, address recipient, uint256 amount) + args := []interface{}{from, to, amount} + res, err := k.CallEVM( + ctx, + *zrc20ABI, + spender, + zrc20Address, + big.NewInt(0), + nil, + true, + true, + transferFrom, + args..., + ) + if err != nil { + return false, errors.Wrap(err, "EVM error calling ZRC20 transferFrom function") + } + + if res.VmError != "" { + return false, fmt.Errorf("EVM execution error in transferFrom: %s", res.VmError) + } + + ret, err := zrc20ABI.Methods[transferFrom].Outputs.Unpack(res.Ret) + if err != nil { + return false, errors.Wrap(err, "failed to unpack ZRC20 transferFrom return value") + } + + if len(ret) == 0 { + return false, fmt.Errorf("no data returned from 'transferFrom' method") + } + + transferred, ok := ret[0].(bool) + if !ok { + return false, fmt.Errorf("transferFrom returned an unexpected value") + } + + return transferred, nil +} diff --git a/x/fungible/keeper/zrc20_methods_test.go b/x/fungible/keeper/zrc20_methods_test.go new file mode 100644 index 0000000000..7b124f3050 --- /dev/null +++ b/x/fungible/keeper/zrc20_methods_test.go @@ -0,0 +1,341 @@ +package keeper_test + +import ( + "math/big" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/node/testutil/sample" + fungibletypes "github.com/zeta-chain/node/x/fungible/types" + "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" +) + +func Test_ZRC20Allowance(t *testing.T) { + // Instantiate the ZRC20 ABI only one time. + // This avoids instantiating it every time deposit or withdraw are called. + zrc20ABI, err := zrc20.ZRC20MetaData.GetAbi() + require.NoError(t, err) + + ts := setupChain(t) + + t.Run("should fail when ZRC20ABI is nil", func(t *testing.T) { + _, err := ts.fungibleKeeper.ZRC20Allowance(ts.ctx, nil, ts.zrc20Address, common.Address{}, common.Address{}) + require.Error(t, err) + require.ErrorAs(t, err, &fungibletypes.ErrZRC20NilABI) + }) + + t.Run("should fail when owner is zero address", func(t *testing.T) { + _, err := ts.fungibleKeeper.ZRC20Allowance( + ts.ctx, + zrc20ABI, + ts.zrc20Address, + common.Address{}, + sample.EthAddress(), + ) + require.Error(t, err) + require.ErrorAs(t, err, &fungibletypes.ErrZeroAddress) + }) + + t.Run("should fail when spender is zero address", func(t *testing.T) { + _, err := ts.fungibleKeeper.ZRC20Allowance( + ts.ctx, + zrc20ABI, + ts.zrc20Address, + sample.EthAddress(), + common.Address{}, + ) + require.Error(t, err) + require.ErrorAs(t, err, &fungibletypes.ErrZeroAddress) + }) + + t.Run("should fail when zrc20 address is zero address", func(t *testing.T) { + _, err := ts.fungibleKeeper.ZRC20Allowance( + ts.ctx, + zrc20ABI, + common.Address{}, + sample.EthAddress(), + fungibletypes.ModuleAddressEVM, + ) + require.Error(t, err) + require.ErrorAs(t, err, &fungibletypes.ErrZRC20ZeroAddress) + }) + + t.Run("should pass with correct input", func(t *testing.T) { + allowance, err := ts.fungibleKeeper.ZRC20Allowance( + ts.ctx, + zrc20ABI, + ts.zrc20Address, + fungibletypes.ModuleAddressEVM, + sample.EthAddress(), + ) + require.NoError(t, err) + require.Equal(t, uint64(0), allowance.Uint64()) + }) +} + +func Test_ZRC20BalanceOf(t *testing.T) { + // Instantiate the ZRC20 ABI only one time. + // This avoids instantiating it every time deposit or withdraw are called. + zrc20ABI, err := zrc20.ZRC20MetaData.GetAbi() + require.NoError(t, err) + + ts := setupChain(t) + + t.Run("should fail when ZRC20ABI is nil", func(t *testing.T) { + _, err := ts.fungibleKeeper.ZRC20BalanceOf(ts.ctx, nil, ts.zrc20Address, common.Address{}) + require.Error(t, err) + require.ErrorAs(t, err, &fungibletypes.ErrZRC20NilABI) + }) + + t.Run("should fail when owner is zero address", func(t *testing.T) { + _, err := ts.fungibleKeeper.ZRC20BalanceOf(ts.ctx, zrc20ABI, ts.zrc20Address, common.Address{}) + require.Error(t, err) + require.ErrorAs(t, err, &fungibletypes.ErrZeroAddress) + }) + + t.Run("should fail when zrc20 address is zero address", func(t *testing.T) { + _, err := ts.fungibleKeeper.ZRC20BalanceOf(ts.ctx, zrc20ABI, common.Address{}, sample.EthAddress()) + require.Error(t, err) + require.ErrorAs(t, err, &fungibletypes.ErrZRC20ZeroAddress) + }) + + t.Run("should pass with correct input", func(t *testing.T) { + balance, err := ts.fungibleKeeper.ZRC20BalanceOf( + ts.ctx, + zrc20ABI, + ts.zrc20Address, + fungibletypes.ModuleAddressEVM, + ) + require.NoError(t, err) + require.Equal(t, uint64(0), balance.Uint64()) + }) +} + +func Test_ZRC20TotalSupply(t *testing.T) { + // Instantiate the ZRC20 ABI only one time. + // This avoids instantiating it every time deposit or withdraw are called. + zrc20ABI, err := zrc20.ZRC20MetaData.GetAbi() + require.NoError(t, err) + + ts := setupChain(t) + + t.Run("should fail when ZRC20ABI is nil", func(t *testing.T) { + _, err := ts.fungibleKeeper.ZRC20TotalSupply(ts.ctx, nil, ts.zrc20Address) + require.Error(t, err) + require.ErrorAs(t, err, &fungibletypes.ErrZRC20NilABI) + }) + + t.Run("should fail when zrc20 address is zero address", func(t *testing.T) { + _, err := ts.fungibleKeeper.ZRC20TotalSupply(ts.ctx, zrc20ABI, common.Address{}) + require.Error(t, err) + require.ErrorAs(t, err, &fungibletypes.ErrZRC20ZeroAddress) + }) + + t.Run("should pass with correct input", func(t *testing.T) { + totalSupply, err := ts.fungibleKeeper.ZRC20TotalSupply(ts.ctx, zrc20ABI, ts.zrc20Address) + require.NoError(t, err) + require.Equal(t, uint64(10000000), totalSupply.Uint64()) + }) +} + +func Test_ZRC20Transfer(t *testing.T) { + // Instantiate the ZRC20 ABI only one time. + // This avoids instantiating it every time deposit or withdraw are called. + zrc20ABI, err := zrc20.ZRC20MetaData.GetAbi() + require.NoError(t, err) + + ts := setupChain(t) + + // Make sure sample.EthAddress() exists as an ethermint account in state. + accAddress := sdk.AccAddress(sample.EthAddress().Bytes()) + ts.fungibleKeeper.GetAuthKeeper().SetAccount(ts.ctx, authtypes.NewBaseAccount(accAddress, nil, 0, 0)) + + t.Run("should fail when ZRC20ABI is nil", func(t *testing.T) { + _, err := ts.fungibleKeeper.ZRC20Transfer( + ts.ctx, + nil, + ts.zrc20Address, + common.Address{}, + common.Address{}, + big.NewInt(0), + ) + require.Error(t, err) + require.ErrorAs(t, err, &fungibletypes.ErrZRC20NilABI) + }) + + t.Run("should fail when owner is zero address", func(t *testing.T) { + _, err := ts.fungibleKeeper.ZRC20Transfer( + ts.ctx, + zrc20ABI, + ts.zrc20Address, + common.Address{}, + sample.EthAddress(), + big.NewInt(0), + ) + require.Error(t, err) + require.ErrorAs(t, err, &fungibletypes.ErrZeroAddress) + }) + + t.Run("should fail when spender is zero address", func(t *testing.T) { + _, err := ts.fungibleKeeper.ZRC20Transfer( + ts.ctx, + zrc20ABI, + ts.zrc20Address, + sample.EthAddress(), + common.Address{}, + big.NewInt(0), + ) + require.Error(t, err) + require.ErrorAs(t, err, &fungibletypes.ErrZeroAddress) + }) + + t.Run("should fail when zrc20 address is zero address", func(t *testing.T) { + _, err := ts.fungibleKeeper.ZRC20Transfer( + ts.ctx, + zrc20ABI, + common.Address{}, + sample.EthAddress(), + fungibletypes.ModuleAddressEVM, + big.NewInt(0), + ) + require.Error(t, err) + require.ErrorAs(t, err, &fungibletypes.ErrZRC20ZeroAddress) + }) + + t.Run("should pass with correct input", func(t *testing.T) { + ts.fungibleKeeper.DepositZRC20(ts.ctx, ts.zrc20Address, fungibletypes.ModuleAddressEVM, big.NewInt(10)) + transferred, err := ts.fungibleKeeper.ZRC20Transfer( + ts.ctx, + zrc20ABI, + ts.zrc20Address, + fungibletypes.ModuleAddressEVM, + sample.EthAddress(), + big.NewInt(10), + ) + require.NoError(t, err) + require.True(t, transferred) + }) +} + +func Test_ZRC20TransferFrom(t *testing.T) { + // Instantiate the ZRC20 ABI only one time. + // This avoids instantiating it every time deposit or withdraw are called. + zrc20ABI, err := zrc20.ZRC20MetaData.GetAbi() + require.NoError(t, err) + + ts := setupChain(t) + + // Make sure sample.EthAddress() exists as an ethermint account in state. + accAddress := sdk.AccAddress(sample.EthAddress().Bytes()) + ts.fungibleKeeper.GetAuthKeeper().SetAccount(ts.ctx, authtypes.NewBaseAccount(accAddress, nil, 0, 0)) + + t.Run("should fail when ZRC20ABI is nil", func(t *testing.T) { + _, err := ts.fungibleKeeper.ZRC20TransferFrom( + ts.ctx, + nil, + ts.zrc20Address, + common.Address{}, + common.Address{}, + common.Address{}, + big.NewInt(0), + ) + require.Error(t, err) + require.ErrorAs(t, err, &fungibletypes.ErrZRC20NilABI) + }) + + t.Run("should fail when from is zero address", func(t *testing.T) { + _, err := ts.fungibleKeeper.ZRC20TransferFrom( + ts.ctx, + zrc20ABI, + ts.zrc20Address, + sample.EthAddress(), + common.Address{}, + sample.EthAddress(), + big.NewInt(0), + ) + require.Error(t, err) + require.ErrorAs(t, err, &fungibletypes.ErrZeroAddress) + }) + + t.Run("should fail when to is zero address", func(t *testing.T) { + _, err := ts.fungibleKeeper.ZRC20TransferFrom( + ts.ctx, + zrc20ABI, + ts.zrc20Address, + sample.EthAddress(), + sample.EthAddress(), + common.Address{}, + big.NewInt(0), + ) + require.Error(t, err) + require.ErrorAs(t, err, &fungibletypes.ErrZeroAddress) + }) + + t.Run("should fail when spender is zero address", func(t *testing.T) { + _, err := ts.fungibleKeeper.ZRC20TransferFrom( + ts.ctx, + zrc20ABI, + ts.zrc20Address, + common.Address{}, + sample.EthAddress(), + sample.EthAddress(), + big.NewInt(0), + ) + require.Error(t, err) + require.ErrorAs(t, err, &fungibletypes.ErrZeroAddress) + }) + + t.Run("should fail when zrc20 address is zero address", func(t *testing.T) { + _, err := ts.fungibleKeeper.ZRC20TransferFrom( + ts.ctx, + zrc20ABI, + common.Address{}, + sample.EthAddress(), + sample.EthAddress(), + fungibletypes.ModuleAddressEVM, + big.NewInt(0), + ) + require.Error(t, err) + require.ErrorAs(t, err, &fungibletypes.ErrZRC20ZeroAddress) + }) + + t.Run("should fail without an allowance approval", func(t *testing.T) { + // Deposit ZRC20 into fungible EOA. + ts.fungibleKeeper.DepositZRC20(ts.ctx, ts.zrc20Address, fungibletypes.ModuleAddressEVM, big.NewInt(1000)) + + // Transferring the tokens with transferFrom without approval should fail. + _, err = ts.fungibleKeeper.ZRC20TransferFrom( + ts.ctx, + zrc20ABI, + ts.zrc20Address, + fungibletypes.ModuleAddressEVM, + sample.EthAddress(), + fungibletypes.ModuleAddressEVM, + big.NewInt(10), + ) + require.Error(t, err) + }) + + t.Run("should success with an allowance approval", func(t *testing.T) { + // Deposit ZRC20 into fungible EOA. + ts.fungibleKeeper.DepositZRC20(ts.ctx, ts.zrc20Address, fungibletypes.ModuleAddressEVM, big.NewInt(1000)) + + // Approve allowance to sample.EthAddress() to spend 10 ZRC20 tokens. + approveAllowance(t, ts, zrc20ABI, fungibletypes.ModuleAddressEVM, sample.EthAddress(), big.NewInt(10)) + + // Transferring the tokens with transferFrom without approval should fail. + _, err = ts.fungibleKeeper.ZRC20TransferFrom( + ts.ctx, + zrc20ABI, + ts.zrc20Address, + fungibletypes.ModuleAddressEVM, + sample.EthAddress(), + fungibletypes.ModuleAddressEVM, + big.NewInt(10), + ) + require.Error(t, err) + }) +} diff --git a/x/fungible/types/errors.go b/x/fungible/types/errors.go index 7e426a3178..cf333c9545 100644 --- a/x/fungible/types/errors.go +++ b/x/fungible/types/errors.go @@ -29,4 +29,9 @@ var ( ErrNilGasPrice = cosmoserrors.Register(ModuleName, 1127, "nil gas price") ErrAccountNotFound = cosmoserrors.Register(ModuleName, 1128, "account not found") ErrGatewayContractNotSet = cosmoserrors.Register(ModuleName, 1129, "gateway contract not set") + ErrZRC20ZeroAddress = cosmoserrors.Register(ModuleName, 1130, "ZRC20 address cannot be zero") + ErrZRC20NotWhiteListed = cosmoserrors.Register(ModuleName, 1131, "ZRC20 is not whitelisted") + ErrZRC20NilABI = cosmoserrors.Register(ModuleName, 1132, "ZRC20 ABI is nil") + ErrZeroAddress = cosmoserrors.Register(ModuleName, 1133, "address cannot be zero") + ErrInvalidAmount = cosmoserrors.Register(ModuleName, 1134, "invalid amount") ) diff --git a/x/fungible/types/keys.go b/x/fungible/types/keys.go index 777cfd7c41..4c02d9c2ef 100644 --- a/x/fungible/types/keys.go +++ b/x/fungible/types/keys.go @@ -27,17 +27,10 @@ func KeyPrefix(p string) []byte { } var ( - ModuleAddress = authtypes.NewModuleAddress(ModuleName) - //ModuleAddressEVM common.EVMAddress + ModuleAddress = authtypes.NewModuleAddress(ModuleName) ModuleAddressEVM = common.BytesToAddress(ModuleAddress.Bytes()) - AdminAddress = "zeta1rx9r8hff0adaqhr5tuadkzj4e7ns2ntg446vtt" ) -func init() { - //fmt.Printf("ModuleAddressEVM of %s: %s\n", ModuleName, ModuleAddressEVM.String()) - // 0x735b14BB79463307AAcBED86DAf3322B1e6226aB -} - const ( SystemContractKey = "SystemContract-value-" ) diff --git a/x/observer/genesis.go b/x/observer/genesis.go index 46ed737b6f..12a92887c2 100644 --- a/x/observer/genesis.go +++ b/x/observer/genesis.go @@ -26,15 +26,12 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) btcChainParams.IsSupported = true goerliChainParams := types.GetDefaultGoerliLocalnetChainParams() goerliChainParams.IsSupported = true - solanaChainParams := types.GetDefaultSolanaLocalnetChainParams() - solanaChainParams.IsSupported = true zetaPrivnetChainParams := types.GetDefaultZetaPrivnetChainParams() zetaPrivnetChainParams.IsSupported = true k.SetChainParamsList(ctx, types.ChainParamsList{ ChainParams: []*types.ChainParams{ btcChainParams, goerliChainParams, - solanaChainParams, zetaPrivnetChainParams, }, }) diff --git a/x/observer/genesis_test.go b/x/observer/genesis_test.go index 096dc0db43..13f9a54008 100644 --- a/x/observer/genesis_test.go +++ b/x/observer/genesis_test.go @@ -68,15 +68,12 @@ func TestGenesis(t *testing.T) { btcChainParams.IsSupported = true goerliChainParams := types.GetDefaultGoerliLocalnetChainParams() goerliChainParams.IsSupported = true - solanaChainParams := types.GetDefaultSolanaLocalnetChainParams() - solanaChainParams.IsSupported = true zetaPrivnetChainParams := types.GetDefaultZetaPrivnetChainParams() zetaPrivnetChainParams.IsSupported = true localnetChainParams := types.ChainParamsList{ ChainParams: []*types.ChainParams{ btcChainParams, goerliChainParams, - solanaChainParams, zetaPrivnetChainParams, }, } @@ -107,15 +104,12 @@ func TestGenesis(t *testing.T) { btcChainParams.IsSupported = true goerliChainParams := types.GetDefaultGoerliLocalnetChainParams() goerliChainParams.IsSupported = true - solanaChainParams := types.GetDefaultSolanaLocalnetChainParams() - solanaChainParams.IsSupported = true zetaPrivnetChainParams := types.GetDefaultZetaPrivnetChainParams() zetaPrivnetChainParams.IsSupported = true localnetChainParams := types.ChainParamsList{ ChainParams: []*types.ChainParams{ btcChainParams, goerliChainParams, - solanaChainParams, zetaPrivnetChainParams, }, } diff --git a/x/observer/keeper/hooks.go b/x/observer/keeper/hooks.go index ec002b5a2f..631abd00e3 100644 --- a/x/observer/keeper/hooks.go +++ b/x/observer/keeper/hooks.go @@ -15,7 +15,7 @@ type Hooks struct { func (h Hooks) AfterValidatorRemoved(ctx sdk.Context, _ sdk.ConsAddress, valAddr sdk.ValAddress) error { err := h.k.CleanObservers(ctx, valAddr) if err != nil { - return err + ctx.Logger().Error("Error cleaning observer set", "error", err) } return nil } @@ -23,7 +23,7 @@ func (h Hooks) AfterValidatorRemoved(ctx sdk.Context, _ sdk.ConsAddress, valAddr func (h Hooks) AfterValidatorBeginUnbonding(ctx sdk.Context, _ sdk.ConsAddress, valAddr sdk.ValAddress) error { err := h.k.CheckAndCleanObserver(ctx, valAddr) if err != nil { - return err + ctx.Logger().Error("Error cleaning observer set", "error", err) } return nil } @@ -31,7 +31,7 @@ func (h Hooks) AfterValidatorBeginUnbonding(ctx sdk.Context, _ sdk.ConsAddress, func (h Hooks) AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error { err := h.k.CheckAndCleanObserverDelegator(ctx, valAddr, delAddr) if err != nil { - return err + ctx.Logger().Error("Error cleaning observer set", "error", err) } return nil } @@ -39,7 +39,7 @@ func (h Hooks) AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, func (h Hooks) BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) error { err := h.k.CleanSlashedValidator(ctx, valAddr, fraction) if err != nil { - return err + ctx.Logger().Error("Error cleaning observer set", "error", err) } return nil } diff --git a/x/observer/keeper/hooks_test.go b/x/observer/keeper/hooks_test.go index 7b5ef7ed64..09088bed44 100644 --- a/x/observer/keeper/hooks_test.go +++ b/x/observer/keeper/hooks_test.go @@ -139,15 +139,20 @@ func TestKeeper_AfterDelegationModified(t *testing.T) { } func TestKeeper_BeforeValidatorSlashed(t *testing.T) { - t.Run("should error if validator not found", func(t *testing.T) { + t.Run("should not error if validator not found", func(t *testing.T) { k, ctx, _, _ := keepertest.ObserverKeeper(t) r := rand.New(rand.NewSource(9)) validator := sample.Validator(t, r) + os := sample.ObserverSet(10) + k.SetObserverSet(ctx, os) hooks := k.Hooks() err := hooks.BeforeValidatorSlashed(ctx, validator.GetOperator(), sdk.NewDec(1)) - require.Error(t, err) + require.NoError(t, err) + storedOs, found := k.GetObserverSet(ctx) + require.True(t, found) + require.Equal(t, os, storedOs) }) t.Run("should not error if observer set not found", func(t *testing.T) { diff --git a/x/observer/keeper/msg_server_vote_blame.go b/x/observer/keeper/msg_server_vote_blame.go index 74a3654657..d320ff54db 100644 --- a/x/observer/keeper/msg_server_vote_blame.go +++ b/x/observer/keeper/msg_server_vote_blame.go @@ -6,7 +6,7 @@ import ( sdkerrors "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - crosschainTypes "github.com/zeta-chain/node/x/crosschain/types" + cctypes "github.com/zeta-chain/node/x/crosschain/types" "github.com/zeta-chain/node/x/observer/types" ) @@ -21,9 +21,7 @@ func (k msgServer) VoteBlame( // GetChainFromChainID makes sure we are getting only supported chains , if a chain support has been turned on using gov proposal, this function returns nil observationChain, found := k.GetSupportedChainFromChainID(ctx, msg.ChainId) if !found { - return nil, sdkerrors.Wrapf( - crosschainTypes.ErrUnsupportedChain, - "%s, ChainID %d", voteBlameID, msg.ChainId) + return nil, sdkerrors.Wrapf(cctypes.ErrUnsupportedChain, "%s, ChainID %d", voteBlameID, msg.ChainId) } if ok := k.IsNonTombstonedObserver(ctx, msg.Creator); !ok { diff --git a/x/observer/keeper/voting.go b/x/observer/keeper/voting.go index 1f624da5aa..9314c1bca2 100644 --- a/x/observer/keeper/voting.go +++ b/x/observer/keeper/voting.go @@ -5,6 +5,7 @@ import ( sdkerrors "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/pkg/errors" "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/x/observer/types" @@ -134,12 +135,12 @@ func (k Keeper) CheckObserverSelfDelegation(ctx sdk.Context, accAddress string) } validator, found := k.stakingKeeper.GetValidator(ctx, valAddress) if !found { - return types.ErrNotValidator + return errors.Wrapf(types.ErrNotValidator, "validator : %s", valAddress) } delegation, found := k.stakingKeeper.GetDelegation(ctx, selfdelAddr, valAddress) if !found { - return types.ErrSelfDelegation + return errors.Wrapf(types.ErrSelfDelegation, "self delegation : %s , valAddres : %s", selfdelAddr, valAddress) } minDelegation, err := types.GetMinObserverDelegationDec() diff --git a/x/observer/types/chain_params.go b/x/observer/types/chain_params.go index d9d1cc2294..b1a5335e58 100644 --- a/x/observer/types/chain_params.go +++ b/x/observer/types/chain_params.go @@ -11,7 +11,6 @@ import ( "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/constant" - solanacontracts "github.com/zeta-chain/node/pkg/contracts/solana" ) var ( @@ -148,7 +147,6 @@ func GetDefaultChainParams() ChainParamsList { GetDefaultMumbaiTestnetChainParams(), GetDefaultBtcTestnetChainParams(), GetDefaultBtcRegtestChainParams(), - GetDefaultSolanaLocalnetChainParams(), GetDefaultGoerliLocalnetChainParams(), }, } @@ -299,25 +297,6 @@ func GetDefaultBtcRegtestChainParams() *ChainParams { IsSupported: false, } } -func GetDefaultSolanaLocalnetChainParams() *ChainParams { - return &ChainParams{ - ChainId: chains.SolanaLocalnet.ChainId, - ConfirmationCount: 32, - ZetaTokenContractAddress: constant.EVMZeroAddress, - ConnectorContractAddress: constant.EVMZeroAddress, - Erc20CustodyContractAddress: constant.EVMZeroAddress, - GasPriceTicker: 5, - WatchUtxoTicker: 0, - InboundTicker: 2, - OutboundTicker: 2, - OutboundScheduleInterval: 2, - OutboundScheduleLookahead: 5, - BallotThreshold: DefaultBallotThreshold, - MinObserverDelegation: DefaultMinObserverDelegation, - IsSupported: false, - GatewayAddress: solanacontracts.SolanaGatewayProgramID, - } -} func GetDefaultGoerliLocalnetChainParams() *ChainParams { return &ChainParams{ ChainId: chains.GoerliLocalnet.ChainId, diff --git a/zetaclient/chains/base/observer.go b/zetaclient/chains/base/observer.go index 6cf8af9de6..f089f26815 100644 --- a/zetaclient/chains/base/observer.go +++ b/zetaclient/chains/base/observer.go @@ -135,19 +135,19 @@ func NewObserver( return &ob, nil } -// Start starts the observer. Returns true if the observer was already started (noop). +// Start starts the observer. Returns false if it's already started (noop). func (ob *Observer) Start() bool { ob.mu.Lock() defer ob.Mu().Unlock() // noop if ob.started { - return true + return false } ob.started = true - return false + return true } // Stop notifies all goroutines to stop and closes the database. @@ -186,13 +186,18 @@ func (ob *Observer) WithChain(chain chains.Chain) *Observer { // ChainParams returns the chain params for the observer. func (ob *Observer) ChainParams() observertypes.ChainParams { + ob.mu.Lock() + defer ob.mu.Unlock() + return ob.chainParams } -// WithChainParams attaches a new chain params to the observer. -func (ob *Observer) WithChainParams(params observertypes.ChainParams) *Observer { +// SetChainParams attaches a new chain params to the observer. +func (ob *Observer) SetChainParams(params observertypes.ChainParams) { + ob.mu.Lock() + defer ob.mu.Unlock() + ob.chainParams = params - return ob } // ZetacoreClient returns the zetacore client for the observer. @@ -329,7 +334,12 @@ func (ob *Observer) Logger() *ObserverLogger { // WithLogger attaches a new logger to the observer. func (ob *Observer) WithLogger(logger Logger) *Observer { - chainLogger := logger.Std.With().Int64(logs.FieldChain, ob.chain.ChainId).Logger() + chainLogger := logger.Std. + With(). + Int64(logs.FieldChain, ob.chain.ChainId). + Str(logs.FieldChainNetwork, ob.chain.Network.String()). + Logger() + ob.logger = ObserverLogger{ Chain: chainLogger, Inbound: chainLogger.With().Str(logs.FieldModule, logs.ModNameInbound).Logger(), @@ -338,6 +348,7 @@ func (ob *Observer) WithLogger(logger Logger) *Observer { Headers: chainLogger.With().Str(logs.FieldModule, logs.ModNameHeaders).Logger(), Compliance: logger.Compliance, } + return ob } @@ -461,22 +472,35 @@ func (ob *Observer) PostVoteInbound( msg *crosschaintypes.MsgVoteInbound, retryGasLimit uint64, ) (string, error) { - txHash := msg.InboundHash - coinType := msg.CoinType - chainID := ob.Chain().ChainId - zetaHash, ballot, err := ob.ZetacoreClient(). - PostVoteInbound(ctx, zetacore.PostVoteInboundGasLimit, retryGasLimit, msg) - if err != nil { - ob.logger.Inbound.Err(err). - Msgf("inbound detected: error posting vote for chain %d token %s inbound %s", chainID, coinType, txHash) + const gasLimit = zetacore.PostVoteInboundGasLimit + + var ( + txHash = msg.InboundHash + coinType = msg.CoinType + chainID = ob.Chain().ChainId + ) + + zetaHash, ballot, err := ob.ZetacoreClient().PostVoteInbound(ctx, gasLimit, retryGasLimit, msg) + + lf := map[string]any{ + "inbound.chain_id": chainID, + "inbound.coin_type": coinType.String(), + "inbound.external_tx_hash": txHash, + "inbound.ballot_index": ballot, + "inbound.zeta_tx_hash": zetaHash, + } + + switch { + case err != nil: + ob.logger.Inbound.Error().Err(err).Fields(lf).Msg("inbound detected: error posting vote") return "", err - } else if zetaHash != "" { - ob.logger.Inbound.Info().Msgf("inbound detected: chain %d token %s inbound %s vote %s ballot %s", chainID, coinType, txHash, zetaHash, ballot) - } else { - ob.logger.Inbound.Info().Msgf("inbound detected: chain %d token %s inbound %s already voted on ballot %s", chainID, coinType, txHash, ballot) + case zetaHash == "": + ob.logger.Inbound.Info().Fields(lf).Msg("inbound detected: already voted on ballot") + default: + ob.logger.Inbound.Info().Fields(lf).Msgf("inbound detected: vote posted") } - return ballot, err + return ballot, nil } // AlertOnRPCLatency prints an alert if the RPC latency exceeds the threshold. diff --git a/zetaclient/chains/base/observer_test.go b/zetaclient/chains/base/observer_test.go index 0e772e31f9..0c53bea35c 100644 --- a/zetaclient/chains/base/observer_test.go +++ b/zetaclient/chains/base/observer_test.go @@ -175,15 +175,6 @@ func TestObserverGetterAndSetter(t *testing.T) { require.Equal(t, newChain, ob.Chain()) }) - t.Run("should be able to update chain params", func(t *testing.T) { - ob := createObserver(t, chain, defaultAlertLatency) - - // update chain params - newChainParams := *sample.ChainParams(chains.BscMainnet.ChainId) - ob = ob.WithChainParams(newChainParams) - require.True(t, observertypes.ChainParamsEqual(newChainParams, ob.ChainParams())) - }) - t.Run("should be able to update zetacore client", func(t *testing.T) { ob := createObserver(t, chain, defaultAlertLatency) diff --git a/zetaclient/chains/bitcoin/observer/inbound.go b/zetaclient/chains/bitcoin/observer/inbound.go index e75d9cc1a0..1461096763 100644 --- a/zetaclient/chains/bitcoin/observer/inbound.go +++ b/zetaclient/chains/bitcoin/observer/inbound.go @@ -14,8 +14,8 @@ import ( "github.com/pkg/errors" "github.com/rs/zerolog" - "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/coin" + "github.com/zeta-chain/node/pkg/memo" crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" "github.com/zeta-chain/node/zetaclient/chains/bitcoin" "github.com/zeta-chain/node/zetaclient/chains/interfaces" @@ -59,7 +59,7 @@ func (ob *Observer) WatchInbound(ctx context.Context) error { return err } - ticker, err := types.NewDynamicTicker("Bitcoin_WatchInbound", ob.GetChainParams().InboundTicker) + ticker, err := types.NewDynamicTicker("Bitcoin_WatchInbound", ob.ChainParams().InboundTicker) if err != nil { ob.logger.Inbound.Error().Err(err).Msg("error creating ticker") return err @@ -89,7 +89,7 @@ func (ob *Observer) WatchInbound(ctx context.Context) error { ob.logger.Inbound.Debug().Err(err).Msg("WatchInbound: Bitcoin node is not enabled") } } - ticker.UpdateInterval(ob.GetChainParams().InboundTicker, ob.logger.Inbound) + ticker.UpdateInterval(ob.ChainParams().InboundTicker, ob.logger.Inbound) case <-ob.StopChannel(): ob.logger.Inbound.Info().Msgf("WatchInbound stopped for chain %d", ob.Chain().ChainId) return nil @@ -205,7 +205,7 @@ func (ob *Observer) WatchInboundTracker(ctx context.Context) error { return err } - ticker, err := types.NewDynamicTicker("Bitcoin_WatchInboundTracker", ob.GetChainParams().InboundTicker) + ticker, err := types.NewDynamicTicker("Bitcoin_WatchInboundTracker", ob.ChainParams().InboundTicker) if err != nil { ob.logger.Inbound.Err(err).Msg("error creating ticker") return err @@ -224,7 +224,7 @@ func (ob *Observer) WatchInboundTracker(ctx context.Context) error { Err(err). Msgf("error observing inbound tracker for chain %d", ob.Chain().ChainId) } - ticker.UpdateInterval(ob.GetChainParams().InboundTicker, ob.logger.Inbound) + ticker.UpdateInterval(ob.ChainParams().InboundTicker, ob.logger.Inbound) case <-ob.StopChannel(): ob.logger.Inbound.Info().Msgf("WatchInboundTracker stopped for chain %d", ob.Chain().ChainId) return nil @@ -419,7 +419,7 @@ func (ob *Observer) GetInboundVoteMessageFromBtcEvent(inbound *BTCInboundEvent) // TODO(revamp): move all compliance related functions in a specific file func (ob *Observer) DoesInboundContainsRestrictedAddress(inTx *BTCInboundEvent) bool { receiver := "" - parsedAddress, _, err := chains.ParseAddressAndData(hex.EncodeToString(inTx.MemoBytes)) + parsedAddress, _, err := memo.DecodeLegacyMemoHex(hex.EncodeToString(inTx.MemoBytes)) if err == nil && parsedAddress != (ethcommon.Address{}) { receiver = parsedAddress.Hex() } @@ -431,10 +431,27 @@ func (ob *Observer) DoesInboundContainsRestrictedAddress(inTx *BTCInboundEvent) return false } -// GetBtcEvent either returns a valid BTCInboundEvent or nil +// GetBtcEvent returns a valid BTCInboundEvent or nil +// it uses witness data to extract the sender address, except for mainnet +func GetBtcEvent( + rpcClient interfaces.BTCRPCClient, + tx btcjson.TxRawResult, + tssAddress string, + blockNumber uint64, + logger zerolog.Logger, + netParams *chaincfg.Params, + depositorFee float64, +) (*BTCInboundEvent, error) { + if netParams.Name == chaincfg.MainNetParams.Name { + return GetBtcEventWithoutWitness(rpcClient, tx, tssAddress, blockNumber, logger, netParams, depositorFee) + } + return GetBtcEventWithWitness(rpcClient, tx, tssAddress, blockNumber, logger, netParams, depositorFee) +} + +// GetBtcEventWithoutWitness either returns a valid BTCInboundEvent or nil // Note: the caller should retry the tx on error (e.g., GetSenderAddressByVin failed) // TODO(revamp): simplify this function -func GetBtcEvent( +func GetBtcEventWithoutWitness( rpcClient interfaces.BTCRPCClient, tx btcjson.TxRawResult, tssAddress string, diff --git a/zetaclient/chains/bitcoin/observer/inbound_test.go b/zetaclient/chains/bitcoin/observer/inbound_test.go index 2b7a333501..8b01e222a1 100644 --- a/zetaclient/chains/bitcoin/observer/inbound_test.go +++ b/zetaclient/chains/bitcoin/observer/inbound_test.go @@ -189,13 +189,13 @@ func TestGetSenderAddressByVin(t *testing.T) { }) } -func TestGetBtcEvent(t *testing.T) { +func TestGetBtcEventWithoutWitness(t *testing.T) { // load archived inbound P2WPKH raw result // https://mempool.space/tx/847139aa65aa4a5ee896375951cbf7417cfc8a4d6f277ec11f40cd87319f04aa txHash := "847139aa65aa4a5ee896375951cbf7417cfc8a4d6f277ec11f40cd87319f04aa" chain := chains.BitcoinMainnet - // GetBtcEvent arguments + // GetBtcEventWithoutWitness arguments tx := testutils.LoadBTCInboundRawResult(t, TestDataDir, chain.ChainId, txHash, false) tssAddress := testutils.TSSAddressBTCMainnet blockNumber := uint64(835640) @@ -227,7 +227,15 @@ func TestGetBtcEvent(t *testing.T) { rpcClient := testrpc.CreateBTCRPCAndLoadTx(t, TestDataDir, chain.ChainId, preHash) // get BTC event - event, err := observer.GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + event, err := observer.GetBtcEventWithoutWitness( + rpcClient, + *tx, + tssAddress, + blockNumber, + log.Logger, + net, + depositorFee, + ) require.NoError(t, err) require.Equal(t, eventExpected, event) }) @@ -243,7 +251,15 @@ func TestGetBtcEvent(t *testing.T) { rpcClient := testrpc.CreateBTCRPCAndLoadTx(t, TestDataDir, chain.ChainId, preHash) // get BTC event - event, err := observer.GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + event, err := observer.GetBtcEventWithoutWitness( + rpcClient, + *tx, + tssAddress, + blockNumber, + log.Logger, + net, + depositorFee, + ) require.NoError(t, err) require.Equal(t, eventExpected, event) }) @@ -259,7 +275,15 @@ func TestGetBtcEvent(t *testing.T) { rpcClient := testrpc.CreateBTCRPCAndLoadTx(t, TestDataDir, chain.ChainId, preHash) // get BTC event - event, err := observer.GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + event, err := observer.GetBtcEventWithoutWitness( + rpcClient, + *tx, + tssAddress, + blockNumber, + log.Logger, + net, + depositorFee, + ) require.NoError(t, err) require.Equal(t, eventExpected, event) }) @@ -275,7 +299,15 @@ func TestGetBtcEvent(t *testing.T) { rpcClient := testrpc.CreateBTCRPCAndLoadTx(t, TestDataDir, chain.ChainId, preHash) // get BTC event - event, err := observer.GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + event, err := observer.GetBtcEventWithoutWitness( + rpcClient, + *tx, + tssAddress, + blockNumber, + log.Logger, + net, + depositorFee, + ) require.NoError(t, err) require.Equal(t, eventExpected, event) }) @@ -291,7 +323,15 @@ func TestGetBtcEvent(t *testing.T) { rpcClient := testrpc.CreateBTCRPCAndLoadTx(t, TestDataDir, chain.ChainId, preHash) // get BTC event - event, err := observer.GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + event, err := observer.GetBtcEventWithoutWitness( + rpcClient, + *tx, + tssAddress, + blockNumber, + log.Logger, + net, + depositorFee, + ) require.NoError(t, err) require.Equal(t, eventExpected, event) }) @@ -303,7 +343,15 @@ func TestGetBtcEvent(t *testing.T) { // get BTC event rpcClient := mocks.NewBTCRPCClient(t) - event, err := observer.GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + event, err := observer.GetBtcEventWithoutWitness( + rpcClient, + *tx, + tssAddress, + blockNumber, + log.Logger, + net, + depositorFee, + ) require.NoError(t, err) require.Nil(t, event) }) @@ -315,13 +363,29 @@ func TestGetBtcEvent(t *testing.T) { // modify the tx to have Vout[0] a P2SH output tx.Vout[0].ScriptPubKey.Hex = strings.Replace(tx.Vout[0].ScriptPubKey.Hex, "0014", "a914", 1) - event, err := observer.GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + event, err := observer.GetBtcEventWithoutWitness( + rpcClient, + *tx, + tssAddress, + blockNumber, + log.Logger, + net, + depositorFee, + ) require.NoError(t, err) require.Nil(t, event) // append 1 byte to script to make it longer than 22 bytes tx.Vout[0].ScriptPubKey.Hex = tx.Vout[0].ScriptPubKey.Hex + "00" - event, err = observer.GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + event, err = observer.GetBtcEventWithoutWitness( + rpcClient, + *tx, + tssAddress, + blockNumber, + log.Logger, + net, + depositorFee, + ) require.NoError(t, err) require.Nil(t, event) }) @@ -333,7 +397,15 @@ func TestGetBtcEvent(t *testing.T) { // get BTC event rpcClient := mocks.NewBTCRPCClient(t) - event, err := observer.GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + event, err := observer.GetBtcEventWithoutWitness( + rpcClient, + *tx, + tssAddress, + blockNumber, + log.Logger, + net, + depositorFee, + ) require.NoError(t, err) require.Nil(t, event) }) @@ -345,7 +417,15 @@ func TestGetBtcEvent(t *testing.T) { // get BTC event rpcClient := mocks.NewBTCRPCClient(t) - event, err := observer.GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + event, err := observer.GetBtcEventWithoutWitness( + rpcClient, + *tx, + tssAddress, + blockNumber, + log.Logger, + net, + depositorFee, + ) require.NoError(t, err) require.Nil(t, event) }) @@ -357,7 +437,15 @@ func TestGetBtcEvent(t *testing.T) { // get BTC event rpcClient := mocks.NewBTCRPCClient(t) - event, err := observer.GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + event, err := observer.GetBtcEventWithoutWitness( + rpcClient, + *tx, + tssAddress, + blockNumber, + log.Logger, + net, + depositorFee, + ) require.NoError(t, err) require.Nil(t, event) }) @@ -369,7 +457,15 @@ func TestGetBtcEvent(t *testing.T) { // get BTC event rpcClient := mocks.NewBTCRPCClient(t) - event, err := observer.GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + event, err := observer.GetBtcEventWithoutWitness( + rpcClient, + *tx, + tssAddress, + blockNumber, + log.Logger, + net, + depositorFee, + ) require.NoError(t, err) require.Nil(t, event) }) @@ -392,7 +488,15 @@ func TestGetBtcEvent(t *testing.T) { rpcClient.On("GetRawTransaction", mock.Anything).Return(btcutil.NewTx(msgTx), nil) // get BTC event - event, err := observer.GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + event, err := observer.GetBtcEventWithoutWitness( + rpcClient, + *tx, + tssAddress, + blockNumber, + log.Logger, + net, + depositorFee, + ) require.NoError(t, err) require.Nil(t, event) }) @@ -417,7 +521,15 @@ func TestGetBtcEventErrors(t *testing.T) { // get BTC event rpcClient := mocks.NewBTCRPCClient(t) - event, err := observer.GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + event, err := observer.GetBtcEventWithoutWitness( + rpcClient, + *tx, + tssAddress, + blockNumber, + log.Logger, + net, + depositorFee, + ) require.Error(t, err) require.Nil(t, event) }) @@ -429,7 +541,15 @@ func TestGetBtcEventErrors(t *testing.T) { // get BTC event rpcClient := mocks.NewBTCRPCClient(t) - event, err := observer.GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + event, err := observer.GetBtcEventWithoutWitness( + rpcClient, + *tx, + tssAddress, + blockNumber, + log.Logger, + net, + depositorFee, + ) require.ErrorContains(t, err, "no input found") require.Nil(t, event) }) @@ -443,8 +563,94 @@ func TestGetBtcEventErrors(t *testing.T) { rpcClient.On("GetRawTransaction", mock.Anything).Return(nil, errors.New("rpc error")) // get BTC event - event, err := observer.GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + event, err := observer.GetBtcEventWithoutWitness( + rpcClient, + *tx, + tssAddress, + blockNumber, + log.Logger, + net, + depositorFee, + ) require.ErrorContains(t, err, "error getting sender address") require.Nil(t, event) }) } + +func TestGetBtcEvent(t *testing.T) { + t.Run("should not decode inbound event with witness with mainnet chain", func(t *testing.T) { + // load archived inbound P2WPKH raw result + // https://mempool.space/tx/847139aa65aa4a5ee896375951cbf7417cfc8a4d6f277ec11f40cd87319f04aa + chain := chains.BitcoinMainnet + tssAddress := testutils.TSSAddressBTCMainnet + blockNumber := uint64(835640) + net := &chaincfg.MainNetParams + // 2.992e-05, see avgFeeRate https://mempool.space/api/v1/blocks/835640 + depositorFee := bitcoin.DepositorFee(22 * clientcommon.BTCOutboundGasPriceMultiplier) + txHash2 := "37777defed8717c581b4c0509329550e344bdc14ac38f71fc050096887e535c8" + tx := testutils.LoadBTCInboundRawResult(t, TestDataDir, chain.ChainId, txHash2, false) + rpcClient := mocks.NewBTCRPCClient(t) + // get BTC event + event, err := observer.GetBtcEvent( + rpcClient, + *tx, + tssAddress, + blockNumber, + log.Logger, + net, + depositorFee, + ) + require.NoError(t, err) + require.Equal(t, (*observer.BTCInboundEvent)(nil), event) + }) + + t.Run("should support legacy BTC inbound event parsing for mainnet", func(t *testing.T) { + // load archived inbound P2WPKH raw result + // https://mempool.space/tx/847139aa65aa4a5ee896375951cbf7417cfc8a4d6f277ec11f40cd87319f04aa + txHash := "847139aa65aa4a5ee896375951cbf7417cfc8a4d6f277ec11f40cd87319f04aa" + chain := chains.BitcoinMainnet + + // GetBtcEventWithoutWitness arguments + tx := testutils.LoadBTCInboundRawResult(t, TestDataDir, chain.ChainId, txHash, false) + tssAddress := testutils.TSSAddressBTCMainnet + blockNumber := uint64(835640) + net := &chaincfg.MainNetParams + + // fee rate of above tx is 28 sat/vB + depositorFee := bitcoin.DepositorFee(28 * clientcommon.BTCOutboundGasPriceMultiplier) + + // expected result + memo, err := hex.DecodeString(tx.Vout[1].ScriptPubKey.Hex[4:]) + require.NoError(t, err) + eventExpected := &observer.BTCInboundEvent{ + FromAddress: "bc1q68kxnq52ahz5vd6c8czevsawu0ux9nfrzzrh6e", + ToAddress: tssAddress, + Value: tx.Vout[0].Value - depositorFee, // 6192 sataoshis + DepositorFee: depositorFee, + MemoBytes: memo, + BlockNumber: blockNumber, + TxHash: tx.Txid, + } + + // https://mempool.space/tx/c5d224963832fc0b9a597251c2342a17b25e481a88cc9119008e8f8296652697 + preHash := "c5d224963832fc0b9a597251c2342a17b25e481a88cc9119008e8f8296652697" + tx.Vin[0].Txid = preHash + tx.Vin[0].Vout = 2 + eventExpected.FromAddress = "bc1q68kxnq52ahz5vd6c8czevsawu0ux9nfrzzrh6e" + // load previous raw tx so so mock rpc client can return it + rpcClient := testrpc.CreateBTCRPCAndLoadTx(t, TestDataDir, chain.ChainId, preHash) + + // get BTC event + event, err := observer.GetBtcEvent( + rpcClient, + *tx, + tssAddress, + blockNumber, + log.Logger, + net, + depositorFee, + ) + require.NoError(t, err) + require.Equal(t, eventExpected, event) + }) +} diff --git a/zetaclient/chains/bitcoin/observer/observer.go b/zetaclient/chains/bitcoin/observer/observer.go index 80781fdd33..f78d81af9c 100644 --- a/zetaclient/chains/bitcoin/observer/observer.go +++ b/zetaclient/chains/bitcoin/observer/observer.go @@ -162,25 +162,9 @@ func (ob *Observer) WithBtcClient(client interfaces.BTCRPCClient) { ob.btcClient = client } -// SetChainParams sets the chain params for the observer -// Note: chain params is accessed concurrently -func (ob *Observer) SetChainParams(params observertypes.ChainParams) { - ob.Mu().Lock() - defer ob.Mu().Unlock() - ob.WithChainParams(params) -} - -// GetChainParams returns the chain params for the observer -// Note: chain params is accessed concurrently -func (ob *Observer) GetChainParams() observertypes.ChainParams { - ob.Mu().Lock() - defer ob.Mu().Unlock() - return ob.ChainParams() -} - // Start starts the Go routine processes to observe the Bitcoin chain func (ob *Observer) Start(ctx context.Context) { - if noop := ob.Observer.Start(); noop { + if ok := ob.Observer.Start(); !ok { ob.Logger().Chain.Info().Msgf("observer is already started for chain %d", ob.Chain().ChainId) return } @@ -219,12 +203,12 @@ func (ob *Observer) ConfirmationsThreshold(amount *big.Int) int64 { if amount.Cmp(big.NewInt(BigValueSats)) >= 0 { return BigValueConfirmationCount } - if BigValueConfirmationCount < ob.GetChainParams().ConfirmationCount { + if BigValueConfirmationCount < ob.ChainParams().ConfirmationCount { return BigValueConfirmationCount } // #nosec G115 always in range - return int64(ob.GetChainParams().ConfirmationCount) + return int64(ob.ChainParams().ConfirmationCount) } // WatchGasPrice watches Bitcoin chain for gas rate and post to zetacore @@ -238,25 +222,25 @@ func (ob *Observer) WatchGasPrice(ctx context.Context) error { } // start gas price ticker - ticker, err := clienttypes.NewDynamicTicker("Bitcoin_WatchGasPrice", ob.GetChainParams().GasPriceTicker) + ticker, err := clienttypes.NewDynamicTicker("Bitcoin_WatchGasPrice", ob.ChainParams().GasPriceTicker) if err != nil { return errors.Wrapf(err, "NewDynamicTicker error") } ob.logger.GasPrice.Info().Msgf("WatchGasPrice started for chain %d with interval %d", - ob.Chain().ChainId, ob.GetChainParams().GasPriceTicker) + ob.Chain().ChainId, ob.ChainParams().GasPriceTicker) defer ticker.Stop() for { select { case <-ticker.C(): - if !ob.GetChainParams().IsSupported { + if !ob.ChainParams().IsSupported { continue } err := ob.PostGasPrice(ctx) if err != nil { ob.logger.GasPrice.Error().Err(err).Msgf("PostGasPrice error for chain %d", ob.Chain().ChainId) } - ticker.UpdateInterval(ob.GetChainParams().GasPriceTicker, ob.logger.GasPrice) + ticker.UpdateInterval(ob.ChainParams().GasPriceTicker, ob.logger.GasPrice) case <-ob.StopChannel(): ob.logger.GasPrice.Info().Msgf("WatchGasPrice stopped for chain %d", ob.Chain().ChainId) return nil @@ -316,7 +300,7 @@ func (ob *Observer) PostGasPrice(ctx context.Context) error { // WatchUTXOs watches bitcoin chain for UTXOs owned by the TSS address // TODO(revamp): move ticker related functions to a specific file func (ob *Observer) WatchUTXOs(ctx context.Context) error { - ticker, err := clienttypes.NewDynamicTicker("Bitcoin_WatchUTXOs", ob.GetChainParams().WatchUtxoTicker) + ticker, err := clienttypes.NewDynamicTicker("Bitcoin_WatchUTXOs", ob.ChainParams().WatchUtxoTicker) if err != nil { ob.logger.UTXOs.Error().Err(err).Msg("error creating ticker") return err @@ -326,7 +310,7 @@ func (ob *Observer) WatchUTXOs(ctx context.Context) error { for { select { case <-ticker.C(): - if !ob.GetChainParams().IsSupported { + if !ob.ChainParams().IsSupported { continue } err := ob.FetchUTXOs(ctx) @@ -341,7 +325,7 @@ func (ob *Observer) WatchUTXOs(ctx context.Context) error { ob.logger.UTXOs.Debug().Err(err).Msg("No wallet is loaded") } } - ticker.UpdateInterval(ob.GetChainParams().WatchUtxoTicker, ob.logger.UTXOs) + ticker.UpdateInterval(ob.ChainParams().WatchUtxoTicker, ob.logger.UTXOs) case <-ob.StopChannel(): ob.logger.UTXOs.Info().Msgf("WatchUTXOs stopped for chain %d", ob.Chain().ChainId) return nil diff --git a/zetaclient/chains/bitcoin/observer/outbound.go b/zetaclient/chains/bitcoin/observer/outbound.go index 3b25d8c0c1..16c8d24b81 100644 --- a/zetaclient/chains/bitcoin/observer/outbound.go +++ b/zetaclient/chains/bitcoin/observer/outbound.go @@ -32,7 +32,7 @@ func (ob *Observer) WatchOutbound(ctx context.Context) error { return errors.Wrap(err, "unable to get app from context") } - ticker, err := types.NewDynamicTicker("Bitcoin_WatchOutbound", ob.GetChainParams().OutboundTicker) + ticker, err := types.NewDynamicTicker("Bitcoin_WatchOutbound", ob.ChainParams().OutboundTicker) if err != nil { return errors.Wrap(err, "unable to create dynamic ticker") } @@ -106,7 +106,7 @@ func (ob *Observer) WatchOutbound(ctx context.Context) error { ob.logger.Outbound.Error().Msgf("WatchOutbound: included multiple (%d) outbound for chain %d nonce %d", txCount, chainID, tracker.Nonce) } } - ticker.UpdateInterval(ob.GetChainParams().OutboundTicker, ob.logger.Outbound) + ticker.UpdateInterval(ob.ChainParams().OutboundTicker, ob.logger.Outbound) case <-ob.StopChannel(): ob.logger.Outbound.Info().Msgf("WatchOutbound stopped for chain %d", chainID) return nil diff --git a/zetaclient/chains/bitcoin/observer/rpc_status.go b/zetaclient/chains/bitcoin/observer/rpc_status.go index e0fc3c651d..03688f4aa4 100644 --- a/zetaclient/chains/bitcoin/observer/rpc_status.go +++ b/zetaclient/chains/bitcoin/observer/rpc_status.go @@ -16,7 +16,7 @@ func (ob *Observer) watchRPCStatus(_ context.Context) error { for { select { case <-ticker.C: - if !ob.GetChainParams().IsSupported { + if !ob.ChainParams().IsSupported { continue } diff --git a/zetaclient/chains/bitcoin/observer/witness.go b/zetaclient/chains/bitcoin/observer/witness.go index 696629b59a..86b22f95cf 100644 --- a/zetaclient/chains/bitcoin/observer/witness.go +++ b/zetaclient/chains/bitcoin/observer/witness.go @@ -58,7 +58,7 @@ func GetBtcEventWithWitness( memo = candidate logger.Debug().Msgf("GetBtcEventWithWitness: found inscription memo %s in tx %s", hex.EncodeToString(memo), tx.Txid) } else { - return nil, errors.Errorf("error getting memo for inbound: %s", tx.Txid) + return nil, nil } // event found, get sender address diff --git a/zetaclient/chains/evm/cctx.go b/zetaclient/chains/evm/cctx.go index 019dbfe3a0..597347a0a5 100644 --- a/zetaclient/chains/evm/cctx.go +++ b/zetaclient/chains/evm/cctx.go @@ -47,10 +47,10 @@ func ParseOutboundTypeFromCCTX(cctx types.CrossChainTx) OutboundType { case coin.CoinType_Gas: switch cctx.CctxStatus.Status { case types.CctxStatus_PendingOutbound: - if len(cctx.RelayedMessage) == 0 { - return OutboundTypeGasWithdraw - } else { + if cctx.InboundParams.IsCrossChainCall { return OutboundTypeGasWithdrawAndCall + } else { + return OutboundTypeGasWithdraw } case types.CctxStatus_PendingRevert: if cctx.RevertOptions.CallOnRevert { @@ -62,10 +62,10 @@ func ParseOutboundTypeFromCCTX(cctx types.CrossChainTx) OutboundType { case coin.CoinType_ERC20: switch cctx.CctxStatus.Status { case types.CctxStatus_PendingOutbound: - if len(cctx.RelayedMessage) == 0 { - return OutboundTypeERC20Withdraw - } else { + if cctx.InboundParams.IsCrossChainCall { return OutboundTypeERC20WithdrawAndCall + } else { + return OutboundTypeERC20Withdraw } case types.CctxStatus_PendingRevert: if cctx.RevertOptions.CallOnRevert { diff --git a/zetaclient/chains/evm/cctx_test.go b/zetaclient/chains/evm/cctx_test.go new file mode 100644 index 0000000000..e61202fd13 --- /dev/null +++ b/zetaclient/chains/evm/cctx_test.go @@ -0,0 +1,105 @@ +package evm_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/node/pkg/coin" + "github.com/zeta-chain/node/x/crosschain/types" + "github.com/zeta-chain/node/zetaclient/chains/evm" +) + +func TestParseOutboundTypeFromCCTX(t *testing.T) { + tests := []struct { + name string + cctx types.CrossChainTx + expected evm.OutboundType + }{ + { + name: "Gas withdraw and call", + cctx: types.CrossChainTx{ + InboundParams: &types.InboundParams{ + CoinType: coin.CoinType_Gas, + IsCrossChainCall: true, + }, + CctxStatus: &types.Status{ + Status: types.CctxStatus_PendingOutbound, + }, + }, + expected: evm.OutboundTypeGasWithdrawAndCall, + }, + { + name: "Gas withdraw", + cctx: types.CrossChainTx{ + InboundParams: &types.InboundParams{ + CoinType: coin.CoinType_Gas, + IsCrossChainCall: false, + }, + CctxStatus: &types.Status{ + Status: types.CctxStatus_PendingOutbound, + }, + }, + expected: evm.OutboundTypeGasWithdraw, + }, + { + name: "ERC20 withdraw and call", + cctx: types.CrossChainTx{ + InboundParams: &types.InboundParams{ + CoinType: coin.CoinType_ERC20, + IsCrossChainCall: true, + }, + CctxStatus: &types.Status{ + Status: types.CctxStatus_PendingOutbound, + }, + }, + expected: evm.OutboundTypeERC20WithdrawAndCall, + }, + { + name: "ERC20 withdraw revert and call on revert", + cctx: types.CrossChainTx{ + InboundParams: &types.InboundParams{ + CoinType: coin.CoinType_ERC20, + }, + CctxStatus: &types.Status{ + Status: types.CctxStatus_PendingRevert, + }, + RevertOptions: types.RevertOptions{ + CallOnRevert: true, + }, + }, + expected: evm.OutboundTypeERC20WithdrawRevertAndCallOnRevert, + }, + { + name: "No asset call", + cctx: types.CrossChainTx{ + InboundParams: &types.InboundParams{ + CoinType: coin.CoinType_NoAssetCall, + }, + CctxStatus: &types.Status{ + Status: types.CctxStatus_PendingOutbound, + }, + }, + expected: evm.OutboundTypeCall, + }, + { + name: "ZETA gives Uuknown outbound type", + cctx: types.CrossChainTx{ + InboundParams: &types.InboundParams{ + CoinType: coin.CoinType_Zeta, + }, + CctxStatus: &types.Status{ + Status: types.CctxStatus_PendingOutbound, + }, + }, + expected: evm.OutboundTypeUnknown, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := evm.ParseOutboundTypeFromCCTX(tt.cctx) + require.Equal(t, tt.expected, result) + }) + } +} diff --git a/zetaclient/chains/evm/constant.go b/zetaclient/chains/evm/constant.go index 65398b3013..01b229591a 100644 --- a/zetaclient/chains/evm/constant.go +++ b/zetaclient/chains/evm/constant.go @@ -47,6 +47,10 @@ const ( // [signature, sender, receiver] TopicsGatewayDeposit = 3 + // TopicsGatewayDepositAndCall is the number of topics for a gateway deposit and call event + // [signature, sender, receiver] + TopicsGatewayDepositAndCall = 3 + // TopicsGatewayCall is the number of topics for a gateway call event // [signature, sender, receiver] TopicsGatewayCall = 3 diff --git a/zetaclient/chains/evm/observer/inbound.go b/zetaclient/chains/evm/observer/inbound.go index 0e1cb6b84d..02ab1a0b6f 100644 --- a/zetaclient/chains/evm/observer/inbound.go +++ b/zetaclient/chains/evm/observer/inbound.go @@ -20,10 +20,9 @@ import ( "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/erc20custody.sol" "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/zetaconnector.non-eth.sol" - "github.com/zeta-chain/node/pkg/bg" - "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/coin" "github.com/zeta-chain/node/pkg/constant" + "github.com/zeta-chain/node/pkg/memo" "github.com/zeta-chain/node/pkg/ticker" "github.com/zeta-chain/node/x/crosschain/types" "github.com/zeta-chain/node/zetaclient/chains/evm" @@ -39,23 +38,20 @@ import ( // TODO(revamp): move ticker function to a separate file func (ob *Observer) WatchInbound(ctx context.Context) error { sampledLogger := ob.Logger().Inbound.Sample(&zerolog.BasicSampler{N: 10}) - interval := ticker.SecondsFromUint64(ob.GetChainParams().InboundTicker) + interval := ticker.SecondsFromUint64(ob.ChainParams().InboundTicker) task := func(ctx context.Context, t *ticker.Ticker) error { return ob.watchInboundOnce(ctx, t, sampledLogger) } - t := ticker.New(interval, task) - - bg.Work(ctx, func(_ context.Context) error { - <-ob.StopChannel() - t.Stop() - ob.Logger().Inbound.Info().Msg("WatchInbound stopped") - return nil - }) - ob.Logger().Inbound.Info().Msgf("WatchInbound started") - return t.Run(ctx) + return ticker.Run( + ctx, + interval, + task, + ticker.WithStopChan(ob.StopChannel()), + ticker.WithLogger(ob.Logger().Inbound, "WatchInbound"), + ) } func (ob *Observer) watchInboundOnce(ctx context.Context, t *ticker.Ticker, sampledLogger zerolog.Logger) error { @@ -74,7 +70,7 @@ func (ob *Observer) watchInboundOnce(ctx context.Context, t *ticker.Ticker, samp ob.Logger().Inbound.Err(err).Msg("WatchInbound: observeInbound error") } - newInterval := ticker.SecondsFromUint64(ob.GetChainParams().InboundTicker) + newInterval := ticker.SecondsFromUint64(ob.ChainParams().InboundTicker) t.SetInterval(newInterval) return nil @@ -91,7 +87,7 @@ func (ob *Observer) WatchInboundTracker(ctx context.Context) error { ticker, err := clienttypes.NewDynamicTicker( fmt.Sprintf("EVM_WatchInboundTracker_%d", ob.Chain().ChainId), - ob.GetChainParams().InboundTicker, + ob.ChainParams().InboundTicker, ) if err != nil { ob.Logger().Inbound.Err(err).Msg("error creating ticker") @@ -110,7 +106,7 @@ func (ob *Observer) WatchInboundTracker(ctx context.Context) error { if err != nil { ob.Logger().Inbound.Err(err).Msg("ProcessInboundTrackers error") } - ticker.UpdateInterval(ob.GetChainParams().InboundTicker, ob.Logger().Inbound) + ticker.UpdateInterval(ob.ChainParams().InboundTicker, ob.Logger().Inbound) case <-ob.StopChannel(): ob.Logger().Inbound.Info().Msgf("WatchInboundTracker stopped for chain %d", ob.Chain().ChainId) return nil @@ -191,10 +187,10 @@ func (ob *Observer) ObserveInbound(ctx context.Context, sampledLogger zerolog.Lo metrics.GetBlockByNumberPerChain.WithLabelValues(ob.Chain().Name).Inc() // skip if current height is too low - if blockNumber < ob.GetChainParams().ConfirmationCount { + if blockNumber < ob.ChainParams().ConfirmationCount { return fmt.Errorf("observeInbound: skipping observer, current block number %d is too low", blockNumber) } - confirmedBlockNum := blockNumber - ob.GetChainParams().ConfirmationCount + confirmedBlockNum := blockNumber - ob.ChainParams().ConfirmationCount // skip if no new block is confirmed lastScanned := ob.LastBlockScanned() @@ -243,6 +239,12 @@ func (ob *Observer) ObserveInbound(ctx context.Context, sampledLogger zerolog.Lo Err(err). Msgf("ObserveInbound: error observing call events from Gateway contract") } + lastScannedGatewayDepositAndCall, err := ob.ObserveGatewayDepositAndCall(ctx, startBlock, toBlock) + if err != nil { + ob.Logger().Inbound.Error(). + Err(err). + Msgf("ObserveInbound: error observing depositAndCall events from Gateway contract") + } // note: using lowest height for all 3 events is not perfect, but it's simple and good enough lastScannedLowest := lastScannedZetaSent @@ -258,6 +260,9 @@ func (ob *Observer) ObserveInbound(ctx context.Context, sampledLogger zerolog.Lo if lastScannedGatewayCall < lastScannedLowest { lastScannedLowest = lastScannedGatewayCall } + if lastScannedGatewayDepositAndCall < lastScannedLowest { + lastScannedLowest = lastScannedGatewayDepositAndCall + } // update last scanned block height for all 3 events (ZetaSent, Deposited, TssRecvd), ignore db error if lastScannedLowest > lastScanned { @@ -615,7 +620,7 @@ func (ob *Observer) CheckAndVoteInboundTokenGas( // HasEnoughConfirmations checks if the given receipt has enough confirmations func (ob *Observer) HasEnoughConfirmations(receipt *ethtypes.Receipt, lastHeight uint64) bool { - confHeight := receipt.BlockNumber.Uint64() + ob.GetChainParams().ConfirmationCount + confHeight := receipt.BlockNumber.Uint64() + ob.ChainParams().ConfirmationCount return lastHeight >= confHeight } @@ -626,7 +631,7 @@ func (ob *Observer) BuildInboundVoteMsgForDepositedEvent( ) *types.MsgVoteInbound { // compliance check maybeReceiver := "" - parsedAddress, _, err := chains.ParseAddressAndData(hex.EncodeToString(event.Message)) + parsedAddress, _, err := memo.DecodeLegacyMemoHex(hex.EncodeToString(event.Message)) if err == nil && parsedAddress != (ethcommon.Address{}) { maybeReceiver = parsedAddress.Hex() } @@ -736,7 +741,7 @@ func (ob *Observer) BuildInboundVoteMsgForTokenSentToTSS( // compliance check maybeReceiver := "" - parsedAddress, _, err := chains.ParseAddressAndData(message) + parsedAddress, _, err := memo.DecodeLegacyMemoHex(message) if err == nil && parsedAddress != (ethcommon.Address{}) { maybeReceiver = parsedAddress.Hex() } diff --git a/zetaclient/chains/evm/observer/observer.go b/zetaclient/chains/evm/observer/observer.go index 39de0eedea..8823b13c47 100644 --- a/zetaclient/chains/evm/observer/observer.go +++ b/zetaclient/chains/evm/observer/observer.go @@ -116,39 +116,23 @@ func (ob *Observer) WithEvmJSONRPC(client interfaces.EVMJSONRPCClient) { ob.evmJSONRPC = client } -// SetChainParams sets the chain params for the observer -// Note: chain params is accessed concurrently -func (ob *Observer) SetChainParams(params observertypes.ChainParams) { - ob.Mu().Lock() - defer ob.Mu().Unlock() - ob.WithChainParams(params) -} - -// GetChainParams returns the chain params for the observer -// Note: chain params is accessed concurrently -func (ob *Observer) GetChainParams() observertypes.ChainParams { - ob.Mu().Lock() - defer ob.Mu().Unlock() - return ob.ChainParams() -} - // GetConnectorContract returns the non-Eth connector address and binder func (ob *Observer) GetConnectorContract() (ethcommon.Address, *zetaconnector.ZetaConnectorNonEth, error) { - addr := ethcommon.HexToAddress(ob.GetChainParams().ConnectorContractAddress) + addr := ethcommon.HexToAddress(ob.ChainParams().ConnectorContractAddress) contract, err := zetaconnector.NewZetaConnectorNonEth(addr, ob.evmClient) return addr, contract, err } // GetConnectorContractEth returns the Eth connector address and binder func (ob *Observer) GetConnectorContractEth() (ethcommon.Address, *zetaconnectoreth.ZetaConnectorEth, error) { - addr := ethcommon.HexToAddress(ob.GetChainParams().ConnectorContractAddress) + addr := ethcommon.HexToAddress(ob.ChainParams().ConnectorContractAddress) contract, err := FetchConnectorContractEth(addr, ob.evmClient) return addr, contract, err } // GetERC20CustodyContract returns ERC20Custody contract address and binder func (ob *Observer) GetERC20CustodyContract() (ethcommon.Address, *erc20custody.ERC20Custody, error) { - addr := ethcommon.HexToAddress(ob.GetChainParams().Erc20CustodyContractAddress) + addr := ethcommon.HexToAddress(ob.ChainParams().Erc20CustodyContractAddress) contract, err := erc20custody.NewERC20Custody(addr, ob.evmClient) return addr, contract, err } @@ -158,14 +142,14 @@ func (ob *Observer) GetERC20CustodyContract() (ethcommon.Address, *erc20custody. // this simplify the migration process v1 will be completely removed in the future // currently the ABI for withdraw is identical, therefore both contract instances can be used func (ob *Observer) GetERC20CustodyV2Contract() (ethcommon.Address, *erc20custodyv2.ERC20Custody, error) { - addr := ethcommon.HexToAddress(ob.GetChainParams().Erc20CustodyContractAddress) + addr := ethcommon.HexToAddress(ob.ChainParams().Erc20CustodyContractAddress) contract, err := erc20custodyv2.NewERC20Custody(addr, ob.evmClient) return addr, contract, err } // GetGatewayContract returns the gateway contract address and binder func (ob *Observer) GetGatewayContract() (ethcommon.Address, *gatewayevm.GatewayEVM, error) { - addr := ethcommon.HexToAddress(ob.GetChainParams().GatewayAddress) + addr := ethcommon.HexToAddress(ob.ChainParams().GatewayAddress) contract, err := gatewayevm.NewGatewayEVM(addr, ob.evmClient) return addr, contract, err } @@ -190,7 +174,7 @@ func FetchZetaTokenContract( // Start all observation routines for the evm chain func (ob *Observer) Start(ctx context.Context) { - if noop := ob.Observer.Start(); noop { + if ok := ob.Observer.Start(); !ok { ob.Logger().Chain.Info().Msgf("observer is already started for chain %d", ob.Chain().ChainId) return } diff --git a/zetaclient/chains/evm/observer/observer_gas.go b/zetaclient/chains/evm/observer/observer_gas.go index 754f0349c5..3ebca1d3a9 100644 --- a/zetaclient/chains/evm/observer/observer_gas.go +++ b/zetaclient/chains/evm/observer/observer_gas.go @@ -22,27 +22,27 @@ func (ob *Observer) WatchGasPrice(ctx context.Context) error { // start gas price ticker ticker, err := clienttypes.NewDynamicTicker( fmt.Sprintf("EVM_WatchGasPrice_%d", ob.Chain().ChainId), - ob.GetChainParams().GasPriceTicker, + ob.ChainParams().GasPriceTicker, ) if err != nil { ob.Logger().GasPrice.Error().Err(err).Msg("NewDynamicTicker error") return err } ob.Logger().GasPrice.Info().Msgf("WatchGasPrice started for chain %d with interval %d", - ob.Chain().ChainId, ob.GetChainParams().GasPriceTicker) + ob.Chain().ChainId, ob.ChainParams().GasPriceTicker) defer ticker.Stop() for { select { case <-ticker.C(): - if !ob.GetChainParams().IsSupported { + if !ob.ChainParams().IsSupported { continue } err = ob.PostGasPrice(ctx) if err != nil { ob.Logger().GasPrice.Error().Err(err).Msgf("PostGasPrice error for chain %d", ob.Chain().ChainId) } - ticker.UpdateInterval(ob.GetChainParams().GasPriceTicker, ob.Logger().GasPrice) + ticker.UpdateInterval(ob.ChainParams().GasPriceTicker, ob.Logger().GasPrice) case <-ob.StopChannel(): ob.Logger().GasPrice.Info().Msg("WatchGasPrice stopped") return nil diff --git a/zetaclient/chains/evm/observer/outbound.go b/zetaclient/chains/evm/observer/outbound.go index 2534c47aab..0bab913592 100644 --- a/zetaclient/chains/evm/observer/outbound.go +++ b/zetaclient/chains/evm/observer/outbound.go @@ -44,7 +44,7 @@ func (ob *Observer) WatchOutbound(ctx context.Context) error { chainID := ob.Chain().ChainId ticker, err := clienttypes.NewDynamicTicker( fmt.Sprintf("EVM_WatchOutbound_%d", ob.Chain().ChainId), - ob.GetChainParams().OutboundTicker, + ob.ChainParams().OutboundTicker, ) if err != nil { ob.Logger().Outbound.Error().Err(err).Msg("error creating ticker") @@ -72,7 +72,7 @@ func (ob *Observer) WatchOutbound(ctx context.Context) error { Msgf("WatchOutbound: error ProcessOutboundTrackers for chain %d", chainID) } - ticker.UpdateInterval(ob.GetChainParams().OutboundTicker, ob.Logger().Outbound) + ticker.UpdateInterval(ob.ChainParams().OutboundTicker, ob.Logger().Outbound) case <-ob.StopChannel(): ob.Logger().Outbound.Info().Msg("WatchOutbound: stopped") return nil diff --git a/zetaclient/chains/evm/observer/outbound_test.go b/zetaclient/chains/evm/observer/outbound_test.go index 7b8e47f40f..5011e5660a 100644 --- a/zetaclient/chains/evm/observer/outbound_test.go +++ b/zetaclient/chains/evm/observer/outbound_test.go @@ -105,7 +105,7 @@ func Test_IsOutboundProcessed(t *testing.T) { ob.SetTxNReceipt(nonce, receipt, outbound) // set connector contract address to an arbitrary address to make event parsing fail - chainParamsNew := ob.GetChainParams() + chainParamsNew := ob.ChainParams() chainParamsNew.ConnectorContractAddress = sample.EthAddress().Hex() ob.SetChainParams(chainParamsNew) continueKeysign, err := ob.VoteOutboundIfConfirmed(ctx, cctx) diff --git a/zetaclient/chains/evm/observer/rpc_status.go b/zetaclient/chains/evm/observer/rpc_status.go index c63e9e775a..68c7629523 100644 --- a/zetaclient/chains/evm/observer/rpc_status.go +++ b/zetaclient/chains/evm/observer/rpc_status.go @@ -17,7 +17,7 @@ func (ob *Observer) watchRPCStatus(ctx context.Context) error { for { select { case <-ticker.C: - if !ob.GetChainParams().IsSupported { + if !ob.ChainParams().IsSupported { continue } diff --git a/zetaclient/chains/evm/observer/v2_inbound.go b/zetaclient/chains/evm/observer/v2_inbound.go index 246dda603a..b19f0e9f85 100644 --- a/zetaclient/chains/evm/observer/v2_inbound.go +++ b/zetaclient/chains/evm/observer/v2_inbound.go @@ -175,6 +175,12 @@ func (ob *Observer) newDepositInboundVote(event *gatewayevm.GatewayEVMDeposited) coinType = coin.CoinType_Gas } + // to maintain compatibility with previous gateway version, deposit event with a non-empty payload is considered as a call + isCrossChainCall := false + if len(event.Payload) > 0 { + isCrossChainCall = true + } + return *types.NewMsgVoteInbound( ob.ZetacoreClient().GetKeys().GetOperatorAddress().String(), event.Sender.Hex(), @@ -193,6 +199,7 @@ func (ob *Observer) newDepositInboundVote(event *gatewayevm.GatewayEVMDeposited) types.ProtocolContractVersion_V2, false, // currently not relevant since calls are not arbitrary types.WithEVMRevertOptions(event.RevertOptions), + types.WithCrossChainCall(isCrossChainCall), ) } @@ -330,3 +337,141 @@ func (ob *Observer) newCallInboundVote(event *gatewayevm.GatewayEVMCalled) types types.WithEVMRevertOptions(event.RevertOptions), ) } + +// ObserveGatewayDepositAndCall queries the gateway contract for deposit and call events +// returns the last block successfully scanned +func (ob *Observer) ObserveGatewayDepositAndCall(ctx context.Context, startBlock, toBlock uint64) (uint64, error) { + gatewayAddr, gatewayContract, err := ob.GetGatewayContract() + if err != nil { + // lastScanned is startBlock - 1 + return startBlock - 1, errors.Wrap(err, "can't get gateway contract") + } + + // get iterator for the events for the block range + eventIterator, err := gatewayContract.FilterDepositedAndCalled(&bind.FilterOpts{ + Start: startBlock, + End: &toBlock, + Context: ctx, + }, []ethcommon.Address{}, []ethcommon.Address{}) + if err != nil { + return startBlock - 1, errors.Wrapf( + err, + "error filtering deposits from block %d to %d for chain %d", + startBlock, + toBlock, + ob.Chain().ChainId, + ) + } + + // parse and validate events + events := ob.parseAndValidateDepositAndCallEvents(eventIterator, gatewayAddr) + + // increment prom counter + metrics.GetFilterLogsPerChain.WithLabelValues(ob.Chain().Name).Inc() + + // post to zetacore + lastScanned := uint64(0) + for _, event := range events { + // remember which block we are scanning (there could be multiple events in the same block) + if event.Raw.BlockNumber > lastScanned { + lastScanned = event.Raw.BlockNumber + } + + // check if the event is processable + if !ob.checkEventProcessability(event.Sender, event.Receiver, event.Raw.TxHash, event.Payload) { + continue + } + + msg := ob.newDepositAndCallInboundVote(event) + + ob.Logger().Inbound.Info(). + Msgf("ObserveGateway: DepositAndCall inbound detected on chain %d tx %s block %d from %s value %s message %s", + ob.Chain(). + ChainId, event.Raw.TxHash.Hex(), event.Raw.BlockNumber, event.Sender.Hex(), event.Amount.String(), hex.EncodeToString(event.Payload)) + + _, err = ob.PostVoteInbound(ctx, &msg, zetacore.PostVoteInboundExecutionGasLimit) + if err != nil { + // decrement the last scanned block so we have to re-scan from this block next time + return lastScanned - 1, errors.Wrap(err, "error posting vote inbound") + } + } + + // successfully processed all events in [startBlock, toBlock] + return toBlock, nil +} + +// parseAndValidateDepositAndCallEvents collects and sorts events by block number, tx index, and log index +func (ob *Observer) parseAndValidateDepositAndCallEvents( + iterator *gatewayevm.GatewayEVMDepositedAndCalledIterator, + gatewayAddr ethcommon.Address, +) []*gatewayevm.GatewayEVMDepositedAndCalled { + // collect and sort events by block number, then tx index, then log index (ascending) + events := make([]*gatewayevm.GatewayEVMDepositedAndCalled, 0) + for iterator.Next() { + events = append(events, iterator.Event) + err := evm.ValidateEvmTxLog(&iterator.Event.Raw, gatewayAddr, "", evm.TopicsGatewayDepositAndCall) + if err == nil { + events = append(events, iterator.Event) + continue + } + ob.Logger().Inbound.Warn(). + Err(err). + Msgf("ObserveGateway: invalid DepositedAndCalled event in tx %s on chain %d at height %d", + iterator.Event.Raw.TxHash.Hex(), ob.Chain().ChainId, iterator.Event.Raw.BlockNumber) + } + sort.SliceStable(events, func(i, j int) bool { + if events[i].Raw.BlockNumber == events[j].Raw.BlockNumber { + if events[i].Raw.TxIndex == events[j].Raw.TxIndex { + return events[i].Raw.Index < events[j].Raw.Index + } + return events[i].Raw.TxIndex < events[j].Raw.TxIndex + } + return events[i].Raw.BlockNumber < events[j].Raw.BlockNumber + }) + + // filter events from same tx + filtered := make([]*gatewayevm.GatewayEVMDepositedAndCalled, 0) + guard := make(map[string]bool) + for _, event := range events { + // guard against multiple events in the same tx + if guard[event.Raw.TxHash.Hex()] { + ob.Logger().Inbound.Warn(). + Msgf("ObserveGateway: multiple remote call events detected in same tx %s", event.Raw.TxHash) + continue + } + guard[event.Raw.TxHash.Hex()] = true + filtered = append(filtered, event) + } + + return filtered +} + +// newDepositInboundVote creates a MsgVoteInbound message for a Deposit event +func (ob *Observer) newDepositAndCallInboundVote(event *gatewayevm.GatewayEVMDepositedAndCalled) types.MsgVoteInbound { + // if event.Asset is zero, it's a native token + coinType := coin.CoinType_ERC20 + if crypto.IsEmptyAddress(event.Asset) { + coinType = coin.CoinType_Gas + } + + return *types.NewMsgVoteInbound( + ob.ZetacoreClient().GetKeys().GetOperatorAddress().String(), + event.Sender.Hex(), + ob.Chain().ChainId, + "", + event.Receiver.Hex(), + ob.ZetacoreClient().Chain().ChainId, + sdkmath.NewUintFromBigInt(event.Amount), + hex.EncodeToString(event.Payload), + event.Raw.TxHash.Hex(), + event.Raw.BlockNumber, + 1_500_000, + coinType, + event.Asset.Hex(), + event.Raw.Index, + types.ProtocolContractVersion_V2, + false, // currently not relevant since calls are not arbitrary + types.WithEVMRevertOptions(event.RevertOptions), + types.WithCrossChainCall(true), + ) +} diff --git a/zetaclient/chains/evm/signer/gas.go b/zetaclient/chains/evm/signer/gas.go index 540302b91c..6727758edd 100644 --- a/zetaclient/chains/evm/signer/gas.go +++ b/zetaclient/chains/evm/signer/gas.go @@ -92,7 +92,16 @@ func gasFromCCTX(cctx *types.CrossChainTx, logger zerolog.Logger) (Gas, error) { case err != nil: return Gas{}, errors.Wrap(err, "unable to parse priorityFee") case gasPrice.Cmp(priorityFee) == -1: - return Gas{}, fmt.Errorf("gasPrice (%d) is less than priorityFee (%d)", gasPrice.Int64(), priorityFee.Int64()) + logger.Warn(). + Str("cctx.initial_priority_fee", priorityFee.String()). + Str("cctx.forced_priority_fee", gasPrice.String()). + Msg("gasPrice is less than priorityFee, setting priorityFee = gasPrice") + + // this should in theory never happen, but this reported bug might be a cause: https://github.com/zeta-chain/node/issues/2954 + // in this case we lower the priorityFee to the gasPrice to ensure the transaction is valid + // the only potential issue is the transaction might not cover the baseFee + // the gas stability pool mechanism help to mitigate this issue + priorityFee = big.NewInt(0).Set(gasPrice) } return Gas{ diff --git a/zetaclient/chains/evm/signer/gas_test.go b/zetaclient/chains/evm/signer/gas_test.go index 71bbf97b5d..aa9a683b37 100644 --- a/zetaclient/chains/evm/signer/gas_test.go +++ b/zetaclient/chains/evm/signer/gas_test.go @@ -98,9 +98,16 @@ func TestGasFromCCTX(t *testing.T) { errorContains: "unable to parse priorityFee: big.Int is negative", }, { - name: "gasPrice is less than priorityFee", - cctx: makeCCTX(123_000, gwei(4).String(), gwei(5).String()), - errorContains: "gasPrice (4000000000) is less than priorityFee (5000000000)", + name: "gasPrice is less than priorityFee", + cctx: makeCCTX(123_000, gwei(4).String(), gwei(5).String()), + assert: func(t *testing.T, g Gas) { + assert.False(t, g.isLegacy()) + assertGasEquals(t, Gas{ + Limit: 123_000, + Price: gwei(4), + PriorityFee: gwei(4), + }, g) + }, }, { name: "gasPrice is invalid", diff --git a/zetaclient/chains/evm/signer/outbound_data.go b/zetaclient/chains/evm/signer/outbound_data.go index 0cc65c19e2..200ae1f70b 100644 --- a/zetaclient/chains/evm/signer/outbound_data.go +++ b/zetaclient/chains/evm/signer/outbound_data.go @@ -10,6 +10,7 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" "github.com/rs/zerolog" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" "github.com/zeta-chain/node/pkg/coin" "github.com/zeta-chain/node/x/crosschain/types" @@ -42,6 +43,9 @@ type OutboundData struct { // revertOptions field contains data detailing the revert options revertOptions types.RevertOptions + + // callOptions field determinenes if call is arbitrary or authenticated and contains gasLimit + callOptions types.CallOptions } // NewOutboundData creates OutboundData from the given CCTX. @@ -67,14 +71,16 @@ func NewOutboundData( } var ( - to ethcommon.Address - toChainID *big.Int + to ethcommon.Address + toChainID *big.Int + callOptions types.CallOptions ) // in protocol contract v2, receiver is always set in the outbound if cctx.ProtocolContractVersion == types.ProtocolContractVersion_V2 { to = ethcommon.HexToAddress(cctx.GetCurrentOutboundParam().Receiver) toChainID = big.NewInt(cctx.GetCurrentOutboundParam().ReceiverChainId) + callOptions = *cctx.GetCurrentOutboundParam().CallOptions } else { // recipient + destination chain var skip bool @@ -140,9 +146,27 @@ func NewOutboundData( outboundParams: outboundParams, revertOptions: cctx.RevertOptions, + + callOptions: callOptions, }, false, nil } +func (o OutboundData) MessageContext() (gatewayevm.MessageContext, error) { + if o.callOptions == (types.CallOptions{}) { + return gatewayevm.MessageContext{}, errors.New("call options not found") + } + // if sender is provided in messageContext call is authenticated and target is Callable.onCall + // otherwise, call is arbitrary + messageContext := gatewayevm.MessageContext{ + Sender: o.sender, + } + if o.callOptions.IsArbitraryCall { + messageContext.Sender = ethcommon.Address{} + } + + return messageContext, nil +} + func getCCTXIndex(cctx *types.CrossChainTx) ([32]byte, error) { // `0x` + `64 chars`. Two chars ranging `00...FF` represent one byte (64 chars = 32 bytes) if len(cctx.Index) != (2 + 64) { diff --git a/zetaclient/chains/evm/signer/v2_sign.go b/zetaclient/chains/evm/signer/v2_sign.go index 510c0e2a4d..443f304785 100644 --- a/zetaclient/chains/evm/signer/v2_sign.go +++ b/zetaclient/chains/evm/signer/v2_sign.go @@ -19,7 +19,6 @@ import ( // bytes calldata data func (signer *Signer) signGatewayExecute( ctx context.Context, - sender string, txData *OutboundData, ) (*ethtypes.Transaction, error) { gatewayABI, err := gatewayevm.GatewayEVMMetaData.GetAbi() @@ -27,21 +26,16 @@ func (signer *Signer) signGatewayExecute( return nil, errors.Wrap(err, "unable to get GatewayEVMMetaData ABI") } + messageContext, err := txData.MessageContext() + if err != nil { + return nil, err + } + var data []byte - if txData.outboundParams.CallOptions.IsArbitraryCall { - data, err = gatewayABI.Pack("execute", txData.to, txData.message) - if err != nil { - return nil, fmt.Errorf("execute pack error: %w", err) - } - } else { - messageContext := gatewayevm.MessageContext{ - Sender: common.HexToAddress(sender), - } - data, err = gatewayABI.Pack("execute0", messageContext, txData.to, txData.message) - if err != nil { - return nil, fmt.Errorf("execute0 pack error: %w", err) - } + data, err = gatewayABI.Pack("execute", messageContext, txData.to, txData.message) + if err != nil { + return nil, fmt.Errorf("execute pack error: %w", err) } tx, _, _, err := signer.Sign( @@ -81,7 +75,7 @@ func (signer *Signer) signGatewayExecuteRevert( revert.RevertContext{ Sender: common.HexToAddress(inboundSender), Asset: txData.asset, - Amount: txData.amount.Uint64(), + Amount: txData.amount, RevertMessage: txData.revertOptions.RevertMessage, }, ) @@ -155,7 +149,19 @@ func (signer *Signer) signERC20CustodyWithdrawAndCall( return nil, errors.Wrap(err, "unable to get ERC20CustodyMetaData ABI") } - data, err := erc20CustodyV2ABI.Pack("withdrawAndCall", txData.to, txData.asset, txData.amount, txData.message) + messageContext, err := txData.MessageContext() + if err != nil { + return nil, err + } + + data, err := erc20CustodyV2ABI.Pack( + "withdrawAndCall", + messageContext, + txData.to, + txData.asset, + txData.amount, + txData.message, + ) if err != nil { return nil, fmt.Errorf("withdraw pack error: %w", err) } @@ -201,7 +207,7 @@ func (signer *Signer) signERC20CustodyWithdrawRevert( revert.RevertContext{ Sender: common.HexToAddress(inboundSender), Asset: txData.asset, - Amount: txData.amount.Uint64(), + Amount: txData.amount, RevertMessage: txData.revertOptions.RevertMessage, }, ) diff --git a/zetaclient/chains/evm/signer/v2_signer.go b/zetaclient/chains/evm/signer/v2_signer.go index e687454b3b..664663cf0f 100644 --- a/zetaclient/chains/evm/signer/v2_signer.go +++ b/zetaclient/chains/evm/signer/v2_signer.go @@ -27,7 +27,7 @@ func (signer *Signer) SignOutboundFromCCTXV2( case evm.OutboundTypeGasWithdrawAndCall, evm.OutboundTypeCall: // both gas withdraw and call and no-asset call uses gateway execute // no-asset call simply hash msg.value == 0 - return signer.signGatewayExecute(ctx, cctx.InboundParams.Sender, outboundData) + return signer.signGatewayExecute(ctx, outboundData) case evm.OutboundTypeGasWithdrawRevertAndCallOnRevert: return signer.signGatewayExecuteRevert(ctx, cctx.InboundParams.Sender, outboundData) case evm.OutboundTypeERC20WithdrawRevertAndCallOnRevert: diff --git a/zetaclient/chains/interfaces/interfaces.go b/zetaclient/chains/interfaces/interfaces.go index ab58456de6..c89a77e4b8 100644 --- a/zetaclient/chains/interfaces/interfaces.go +++ b/zetaclient/chains/interfaces/interfaces.go @@ -39,15 +39,21 @@ const ( // ChainObserver is the interface for chain observer type ChainObserver interface { + // Start starts the observer Start(ctx context.Context) + + // Stop stops the observer Stop() - VoteOutboundIfConfirmed( - ctx context.Context, - cctx *crosschaintypes.CrossChainTx, - ) (bool, error) + + // ChainParams returns observer chain params (might be out of date with zetacore) + ChainParams() observertypes.ChainParams + + // SetChainParams sets observer chain params SetChainParams(observertypes.ChainParams) - GetChainParams() observertypes.ChainParams - WatchInboundTracker(ctx context.Context) error + + // VoteOutboundIfConfirmed checks outbound status and returns (continueKeySign, error) + // todo we should make this simpler. + VoteOutboundIfConfirmed(ctx context.Context, cctx *crosschaintypes.CrossChainTx) (bool, error) } // ChainSigner is the interface to sign transactions for a chain diff --git a/zetaclient/chains/solana/observer/inbound.go b/zetaclient/chains/solana/observer/inbound.go index d9819bd53c..1441150ada 100644 --- a/zetaclient/chains/solana/observer/inbound.go +++ b/zetaclient/chains/solana/observer/inbound.go @@ -38,7 +38,7 @@ func (ob *Observer) WatchInbound(ctx context.Context) error { ticker, err := clienttypes.NewDynamicTicker( fmt.Sprintf("Solana_WatchInbound_%d", ob.Chain().ChainId), - ob.GetChainParams().InboundTicker, + ob.ChainParams().InboundTicker, ) if err != nil { ob.Logger().Inbound.Error().Err(err).Msg("error creating ticker") diff --git a/zetaclient/chains/solana/observer/inbound_tracker.go b/zetaclient/chains/solana/observer/inbound_tracker.go index 70b6e9702f..19f8d26d04 100644 --- a/zetaclient/chains/solana/observer/inbound_tracker.go +++ b/zetaclient/chains/solana/observer/inbound_tracker.go @@ -21,7 +21,7 @@ func (ob *Observer) WatchInboundTracker(ctx context.Context) error { ticker, err := clienttypes.NewDynamicTicker( fmt.Sprintf("Solana_WatchInboundTracker_%d", ob.Chain().ChainId), - ob.GetChainParams().InboundTicker, + ob.ChainParams().InboundTicker, ) if err != nil { ob.Logger().Inbound.Err(err).Msg("error creating ticker") @@ -42,7 +42,7 @@ func (ob *Observer) WatchInboundTracker(ctx context.Context) error { Err(err). Msgf("WatchInboundTracker: error ProcessInboundTrackers for chain %d", ob.Chain().ChainId) } - ticker.UpdateInterval(ob.GetChainParams().InboundTicker, ob.Logger().Inbound) + ticker.UpdateInterval(ob.ChainParams().InboundTicker, ob.Logger().Inbound) case <-ob.StopChannel(): ob.Logger().Inbound.Info().Msgf("WatchInboundTracker stopped for chain %d", ob.Chain().ChainId) return nil diff --git a/zetaclient/chains/solana/observer/observer.go b/zetaclient/chains/solana/observer/observer.go index 634bdfd635..0548fcd6d3 100644 --- a/zetaclient/chains/solana/observer/observer.go +++ b/zetaclient/chains/solana/observer/observer.go @@ -96,25 +96,9 @@ func (ob *Observer) WithSolClient(client interfaces.SolanaRPCClient) { ob.solClient = client } -// SetChainParams sets the chain params for the observer -// Note: chain params is accessed concurrently -func (ob *Observer) SetChainParams(params observertypes.ChainParams) { - ob.Mu().Lock() - defer ob.Mu().Unlock() - ob.WithChainParams(params) -} - -// GetChainParams returns the chain params for the observer -// Note: chain params is accessed concurrently -func (ob *Observer) GetChainParams() observertypes.ChainParams { - ob.Mu().Lock() - defer ob.Mu().Unlock() - return ob.ChainParams() -} - // Start starts the Go routine processes to observe the Solana chain func (ob *Observer) Start(ctx context.Context) { - if noop := ob.Observer.Start(); noop { + if ok := ob.Observer.Start(); !ok { ob.Logger().Chain.Info().Msgf("observer is already started for chain %d", ob.Chain().ChainId) return } diff --git a/zetaclient/chains/solana/observer/observer_gas.go b/zetaclient/chains/solana/observer/observer_gas.go index 80747e2efb..03291d6a54 100644 --- a/zetaclient/chains/solana/observer/observer_gas.go +++ b/zetaclient/chains/solana/observer/observer_gas.go @@ -42,26 +42,26 @@ func (ob *Observer) WatchGasPrice(ctx context.Context) error { // start gas price ticker ticker, err := clienttypes.NewDynamicTicker( fmt.Sprintf("Solana_WatchGasPrice_%d", ob.Chain().ChainId), - ob.GetChainParams().GasPriceTicker, + ob.ChainParams().GasPriceTicker, ) if err != nil { return errors.Wrapf(err, "NewDynamicTicker error") } ob.Logger().GasPrice.Info().Msgf("WatchGasPrice started for chain %d with interval %d", - ob.Chain().ChainId, ob.GetChainParams().GasPriceTicker) + ob.Chain().ChainId, ob.ChainParams().GasPriceTicker) defer ticker.Stop() for { select { case <-ticker.C(): - if !ob.GetChainParams().IsSupported { + if !ob.ChainParams().IsSupported { continue } err = ob.PostGasPrice(ctx) if err != nil { ob.Logger().GasPrice.Error().Err(err).Msgf("PostGasPrice error for chain %d", ob.Chain().ChainId) } - ticker.UpdateInterval(ob.GetChainParams().GasPriceTicker, ob.Logger().GasPrice) + ticker.UpdateInterval(ob.ChainParams().GasPriceTicker, ob.Logger().GasPrice) case <-ob.StopChannel(): ob.Logger().GasPrice.Info().Msgf("WatchGasPrice stopped for chain %d", ob.Chain().ChainId) return nil diff --git a/zetaclient/chains/solana/observer/outbound.go b/zetaclient/chains/solana/observer/outbound.go index 7ff968ea93..e185b1a27d 100644 --- a/zetaclient/chains/solana/observer/outbound.go +++ b/zetaclient/chains/solana/observer/outbound.go @@ -36,7 +36,7 @@ func (ob *Observer) WatchOutbound(ctx context.Context) error { chainID := ob.Chain().ChainId ticker, err := clienttypes.NewDynamicTicker( fmt.Sprintf("Solana_WatchOutbound_%d", chainID), - ob.GetChainParams().OutboundTicker, + ob.ChainParams().OutboundTicker, ) if err != nil { ob.Logger().Outbound.Error().Err(err).Msg("error creating ticker") @@ -63,7 +63,7 @@ func (ob *Observer) WatchOutbound(ctx context.Context) error { Msgf("WatchOutbound: error ProcessOutboundTrackers for chain %d", chainID) } - ticker.UpdateInterval(ob.GetChainParams().OutboundTicker, ob.Logger().Outbound) + ticker.UpdateInterval(ob.ChainParams().OutboundTicker, ob.Logger().Outbound) case <-ob.StopChannel(): ob.Logger().Outbound.Info().Msgf("WatchOutbound: watcher stopped for chain %d", chainID) return nil diff --git a/zetaclient/chains/solana/observer/rpc_status.go b/zetaclient/chains/solana/observer/rpc_status.go index ff3d02f679..1b16492076 100644 --- a/zetaclient/chains/solana/observer/rpc_status.go +++ b/zetaclient/chains/solana/observer/rpc_status.go @@ -17,7 +17,7 @@ func (ob *Observer) watchRPCStatus(ctx context.Context) error { for { select { case <-ticker.C: - if !ob.GetChainParams().IsSupported { + if !ob.ChainParams().IsSupported { continue } diff --git a/zetaclient/chains/ton/config.go b/zetaclient/chains/ton/config.go index ee7eaac701..731287756e 100644 --- a/zetaclient/chains/ton/config.go +++ b/zetaclient/chains/ton/config.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "net/http" + "net/url" "time" "github.com/tonkeeper/tongo/config" @@ -38,3 +39,16 @@ func ConfigFromURL(ctx context.Context, url string) (*GlobalConfigurationFile, e return config.ParseConfig(res.Body) } + +func ConfigFromPath(path string) (*GlobalConfigurationFile, error) { + return config.ParseConfigFile(path) +} + +// ConfigFromSource returns a parsed configuration file from a URL or a file path. +func ConfigFromSource(ctx context.Context, urlOrPath string) (*GlobalConfigurationFile, error) { + if u, err := url.Parse(urlOrPath); err == nil { + return ConfigFromURL(ctx, u.String()) + } + + return ConfigFromPath(urlOrPath) +} diff --git a/zetaclient/chains/ton/liteapi/client.go b/zetaclient/chains/ton/liteapi/client.go new file mode 100644 index 0000000000..25b0efcf39 --- /dev/null +++ b/zetaclient/chains/ton/liteapi/client.go @@ -0,0 +1,230 @@ +package liteapi + +import ( + "context" + "fmt" + "slices" + "strconv" + "strings" + + lru "github.com/hashicorp/golang-lru" + "github.com/pkg/errors" + "github.com/tonkeeper/tongo/liteapi" + "github.com/tonkeeper/tongo/tlb" + "github.com/tonkeeper/tongo/ton" + + zetaton "github.com/zeta-chain/node/zetaclient/chains/ton" +) + +// Client extends liteapi.Client with some high-level tools +// Reference: https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl +type Client struct { + *liteapi.Client + blockCache *lru.Cache +} + +const ( + pageSize = 200 + blockCacheSize = 250 +) + +// New Client constructor. +func New(client *liteapi.Client) *Client { + blockCache, _ := lru.New(blockCacheSize) + + return &Client{Client: client, blockCache: blockCache} +} + +// NewFromSource creates a new client from a URL or a file path. +func NewFromSource(ctx context.Context, urlOrPath string) (*Client, error) { + cfg, err := zetaton.ConfigFromSource(ctx, urlOrPath) + if err != nil { + return nil, errors.Wrap(err, "unable to get config") + } + + client, err := liteapi.NewClient( + liteapi.WithConfigurationFile(*cfg), + liteapi.WithDetectArchiveNodes(), + ) + if err != nil { + return nil, errors.Wrap(err, "unable to create client") + } + + return New(client), nil +} + +// GetBlockHeader returns block header by block ID. +// Uses LRU cache for network efficiency. +// I haven't found what mode means but `0` works fine. +func (c *Client) GetBlockHeader(ctx context.Context, blockID ton.BlockIDExt, mode uint32) (tlb.BlockInfo, error) { + if c.blockCache == nil { + return tlb.BlockInfo{}, errors.New("block cache is not initialized") + } + + cached, ok := c.getBlockHeaderCache(blockID) + if ok { + return cached, nil + } + + header, err := c.Client.GetBlockHeader(ctx, blockID, mode) + if err != nil { + return tlb.BlockInfo{}, err + } + + c.setBlockHeaderCache(blockID, header) + + return header, nil +} + +func (c *Client) getBlockHeaderCache(blockID ton.BlockIDExt) (tlb.BlockInfo, bool) { + raw, ok := c.blockCache.Get(blockID.String()) + if !ok { + return tlb.BlockInfo{}, false + } + + header, ok := raw.(tlb.BlockInfo) + + return header, ok +} + +func (c *Client) setBlockHeaderCache(blockID ton.BlockIDExt, header tlb.BlockInfo) { + c.blockCache.Add(blockID.String(), header) +} + +// GetFirstTransaction scrolls through the transactions of the given account to find the first one. +// Note that it might fail w/o using an archival node. Also returns the number of +// scrolled transactions for this account i.e. total transactions +func (c *Client) GetFirstTransaction(ctx context.Context, acc ton.AccountID) (*ton.Transaction, int, error) { + lt, hash, err := c.getLastTransactionHash(ctx, acc) + if err != nil { + return nil, 0, err + } + + var ( + tx *ton.Transaction + scrolled int + ) + + for { + hashBits := ton.Bits256(hash) + + txs, err := c.GetTransactions(ctx, pageSize, acc, lt, hashBits) + if err != nil { + return nil, scrolled, errors.Wrapf(err, "unable to get transactions [lt %d, hash %s]", lt, hashBits.Hex()) + } + + if len(txs) == 0 { + break + } + + scrolled += len(txs) + + tx = &txs[len(txs)-1] + + // Not we take the latest item in the list (oldest tx in the page) + // and set it as the new last tx + lt, hash = tx.PrevTransLt, tx.PrevTransHash + } + + if tx == nil { + return nil, scrolled, fmt.Errorf("no transactions found [lt %d, hash %s]", lt, ton.Bits256(hash).Hex()) + } + + return tx, scrolled, nil +} + +// GetTransactionsSince returns all account transactions since the given logicalTime and hash (exclusive). +// The result is ordered from oldest to newest. Used to detect new txs to observe. +func (c *Client) GetTransactionsSince( + ctx context.Context, + acc ton.AccountID, + oldestLT uint64, + oldestHash ton.Bits256, +) ([]ton.Transaction, error) { + lt, hash, err := c.getLastTransactionHash(ctx, acc) + if err != nil { + return nil, err + } + + var result []ton.Transaction + + for { + hashBits := ton.Bits256(hash) + + // note that ton liteapi works in the reverse order. + // Here we go from the LATEST txs to the oldest at N txs per page + txs, err := c.GetTransactions(ctx, pageSize, acc, lt, hashBits) + if err != nil { + return nil, errors.Wrapf(err, "unable to get transactions [lt %d, hash %s]", lt, hashBits.Hex()) + } + + if len(txs) == 0 { + break + } + + for i := range txs { + found := txs[i].Lt == oldestLT && txs[i].Hash() == tlb.Bits256(oldestHash) + if !found { + continue + } + + // early exit + result = append(result, txs[:i]...) + + return result, nil + } + + // otherwise, append all page results + result = append(result, txs...) + + // prepare pagination params for the next page + oldestIndex := len(txs) - 1 + + lt, hash = txs[oldestIndex].PrevTransLt, txs[oldestIndex].PrevTransHash + } + + // reverse the result to get the oldest tx first + slices.Reverse(result) + + return result, nil +} + +// getLastTransactionHash returns logical time and hash of the last transaction +func (c *Client) getLastTransactionHash(ctx context.Context, acc ton.AccountID) (uint64, tlb.Bits256, error) { + state, err := c.GetAccountState(ctx, acc) + if err != nil { + return 0, tlb.Bits256{}, errors.Wrap(err, "unable to get account state") + } + + if state.Account.Status() != tlb.AccountActive { + return 0, tlb.Bits256{}, errors.New("account is not active") + } + + return state.LastTransLt, state.LastTransHash, nil +} + +// TransactionHashToString converts logicalTime and hash to string +func TransactionHashToString(lt uint64, hash ton.Bits256) string { + return fmt.Sprintf("%d:%s", lt, hash.Hex()) +} + +// TransactionHashFromString parses encoded string into logicalTime and hash +func TransactionHashFromString(encoded string) (uint64, ton.Bits256, error) { + parts := strings.Split(encoded, ":") + if len(parts) != 2 { + return 0, ton.Bits256{}, fmt.Errorf("invalid encoded string format") + } + + lt, err := strconv.ParseUint(parts[0], 10, 64) + if err != nil { + return 0, ton.Bits256{}, fmt.Errorf("invalid logical time: %w", err) + } + + var hashBits ton.Bits256 + + if err = hashBits.FromHex(parts[1]); err != nil { + return 0, ton.Bits256{}, fmt.Errorf("invalid hash: %w", err) + } + + return lt, hashBits, nil +} diff --git a/zetaclient/chains/ton/liteapi/client_live_test.go b/zetaclient/chains/ton/liteapi/client_live_test.go new file mode 100644 index 0000000000..ed3c850dd8 --- /dev/null +++ b/zetaclient/chains/ton/liteapi/client_live_test.go @@ -0,0 +1,198 @@ +package liteapi + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tonkeeper/tongo/config" + "github.com/tonkeeper/tongo/liteapi" + "github.com/tonkeeper/tongo/tlb" + "github.com/tonkeeper/tongo/ton" + "github.com/zeta-chain/node/zetaclient/common" +) + +func TestClient(t *testing.T) { + if !common.LiveTestEnabled() { + t.Skip("Live tests are disabled") + } + + var ( + ctx = context.Background() + client = New(mustCreateClient(t)) + ) + + t.Run("GetFirstTransaction", func(t *testing.T) { + t.Run("Account doesn't exist", func(t *testing.T) { + // ARRANGE + accountID, err := ton.ParseAccountID("0:55798cb7b87168251a7c39f6806b8c202f6caa0f617a76f4070b3fdacfd056a2") + require.NoError(t, err) + + // ACT + tx, scrolled, err := client.GetFirstTransaction(ctx, accountID) + + // ASSERT + require.ErrorContains(t, err, "account is not active") + require.Zero(t, scrolled) + require.Nil(t, tx) + }) + + t.Run("All good", func(t *testing.T) { + // ARRANGE + // Given sample account id (a dev wallet) + // https://tonviewer.com/UQCVlMcZ7EyV9maDsvscoLCd5KQfb7CHukyNJluWpMzlD0vr?section=transactions + accountID, err := ton.ParseAccountID("UQCVlMcZ7EyV9maDsvscoLCd5KQfb7CHukyNJluWpMzlD0vr") + require.NoError(t, err) + + // Given expected hash for the first tx + const expect = "b73df4853ca02a040df46f56635d6b8f49b554d5f556881ab389111bbfce4498" + + // as of 2024-09-18 + const expectedTransactions = 23 + + start := time.Now() + + // ACT + tx, scrolled, err := client.GetFirstTransaction(ctx, accountID) + + finish := time.Since(start) + + // ASSERT + require.NoError(t, err) + + assert.GreaterOrEqual(t, scrolled, expectedTransactions) + assert.Equal(t, expect, tx.Hash().Hex()) + + t.Logf("Time taken %s; transactions scanned: %d", finish.String(), scrolled) + }) + }) + + t.Run("GetTransactionsUntil", func(t *testing.T) { + // ARRANGE + // Given sample account id (dev wallet) + // https://tonviewer.com/UQCVlMcZ7EyV9maDsvscoLCd5KQfb7CHukyNJluWpMzlD0vr?section=transactions + accountID, err := ton.ParseAccountID("UQCVlMcZ7EyV9maDsvscoLCd5KQfb7CHukyNJluWpMzlD0vr") + require.NoError(t, err) + + const getUntilLT = uint64(48645164000001) + const getUntilHash = `2e107215e634bbc3492bdf4b1466d59432623295072f59ab526d15737caa9531` + + // as of 2024-09-20 + const expectedTX = 3 + + var hash ton.Bits256 + require.NoError(t, hash.FromHex(getUntilHash)) + + start := time.Now() + + // ACT + // https://tonviewer.com/UQCVlMcZ7EyV9maDsvscoLCd5KQfb7CHukyNJluWpMzlD0vr?section=transactions + txs, err := client.GetTransactionsSince(ctx, accountID, getUntilLT, hash) + + finish := time.Since(start) + + // ASSERT + require.NoError(t, err) + + t.Logf("Time taken %s; transactions fetched: %d", finish.String(), len(txs)) + for _, tx := range txs { + printTx(t, tx) + } + + mustContainTX(t, txs, "a6672a0e80193c1f705ef1cf45a5883441b8252523b1d08f7656c80e400c74a8") + assert.GreaterOrEqual(t, len(txs), expectedTX) + }) + + t.Run("GetBlockHeader", func(t *testing.T) { + // ARRANGE + // Given sample account id (dev wallet) + // https://tonscan.org/address/UQCVlMcZ7EyV9maDsvscoLCd5KQfb7CHukyNJluWpMzlD0vr + accountID, err := ton.ParseAccountID("UQCVlMcZ7EyV9maDsvscoLCd5KQfb7CHukyNJluWpMzlD0vr") + require.NoError(t, err) + + const getUntilLT = uint64(48645164000001) + const getUntilHash = `2e107215e634bbc3492bdf4b1466d59432623295072f59ab526d15737caa9531` + + var hash ton.Bits256 + require.NoError(t, hash.FromHex(getUntilHash)) + + txs, err := client.GetTransactions(ctx, 1, accountID, getUntilLT, hash) + require.NoError(t, err) + require.Len(t, txs, 1) + + // Given a block + blockID := txs[0].BlockID + + // ACT + header, err := client.GetBlockHeader(ctx, blockID, 0) + + // ASSERT + require.NoError(t, err) + require.NotZero(t, header.MinRefMcSeqno) + require.Equal(t, header.MinRefMcSeqno, header.MasterRef.Master.SeqNo) + }) +} + +func mustCreateClient(t *testing.T) *liteapi.Client { + client, err := liteapi.NewClient( + liteapi.WithConfigurationFile(mustFetchConfig(t)), + liteapi.WithDetectArchiveNodes(), + ) + + require.NoError(t, err) + + return client +} + +func mustFetchConfig(t *testing.T) config.GlobalConfigurationFile { + // archival light client for mainnet + const url = "https://api.tontech.io/ton/archive-mainnet.autoconf.json" + + res, err := http.Get(url) + require.NoError(t, err) + require.Equal(t, http.StatusOK, res.StatusCode) + + defer res.Body.Close() + + conf, err := config.ParseConfig(res.Body) + require.NoError(t, err) + + return *conf +} + +func mustContainTX(t *testing.T, txs []ton.Transaction, hash string) { + var h ton.Bits256 + require.NoError(t, h.FromHex(hash)) + + for _, tx := range txs { + if tx.Hash() == tlb.Bits256(h) { + return + } + } + + t.Fatalf("transaction %q not found", hash) +} + +func printTx(t *testing.T, tx ton.Transaction) { + b, err := json.MarshalIndent(simplifyTx(tx), "", " ") + require.NoError(t, err) + + t.Logf("TX %s", string(b)) +} + +func simplifyTx(tx ton.Transaction) map[string]any { + return map[string]any{ + "block": fmt.Sprintf("shard: %d, seqno: %d", tx.BlockID.Shard, tx.BlockID.Seqno), + "hash": tx.Hash().Hex(), + "logicalTime": tx.Lt, + "unixTime": time.Unix(int64(tx.Transaction.Now), 0).UTC().String(), + "outMessagesCount": tx.OutMsgCnt, + // "inMessageInfo": tx.Msgs.InMsg.Value.Value.Info.IntMsgInfo, + // "outMessages": tx.Msgs.OutMsgs, + } +} diff --git a/zetaclient/chains/ton/liteapi/client_test.go b/zetaclient/chains/ton/liteapi/client_test.go new file mode 100644 index 0000000000..a1148540be --- /dev/null +++ b/zetaclient/chains/ton/liteapi/client_test.go @@ -0,0 +1,100 @@ +package liteapi + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestHashes(t *testing.T) { + const sample = `48644940000001:e02b8c7cec103e08175ade8106619a8908707623c31451df2a68497c7d23d15a` + + lt, hash, err := TransactionHashFromString(sample) + require.NoError(t, err) + + require.Equal(t, uint64(48644940000001), lt) + require.Equal(t, "e02b8c7cec103e08175ade8106619a8908707623c31451df2a68497c7d23d15a", hash.Hex()) + require.Equal(t, sample, TransactionHashToString(lt, hash)) +} + +func TestTransactionHashFromString(t *testing.T) { + for _, tt := range []struct { + name string + raw string + error bool + lt uint64 + hash string + }{ + { + name: "real example", + raw: "163000003:d0415f655644db6ee1260b1fa48e9f478e938823e8b293054fbae1f3511b77c5", + lt: 163000003, + hash: "d0415f655644db6ee1260b1fa48e9f478e938823e8b293054fbae1f3511b77c5", + }, + { + name: "zero lt", + raw: "0:0000000000000000000000000000000000000000000000000000000000000000", + lt: 0, + hash: "0000000000000000000000000000000000000000000000000000000000000000", + }, + { + name: "big lt", + raw: "999999999999:fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0", + lt: 999_999_999_999, + hash: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0", + }, + { + name: "missing colon", + raw: "123456abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + error: true, + }, + { + name: "missing logical time", + raw: ":abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + error: true, + }, + { + name: "hash length", + raw: "123456:abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcde", + error: true, + }, + { + name: "non-numeric logical time", + raw: "notanumber:abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef", + error: true, + }, + { + name: "non-hex hash", + raw: "123456:xyz123xyz123xyz123xyz123xyz123xyz123xyz123xyz123xyz123xyz123xyz123", + error: true, + }, + { + name: "empty string", + raw: "", + error: true, + }, + { + name: "Invalid - only logical time, no hash", + raw: "123456:", + error: true, + }, + { + name: "Invalid - too many parts (extra colon)", + raw: "123456:abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef:extra", + error: true, + }, + } { + t.Run(tt.name, func(t *testing.T) { + lt, hash, err := TransactionHashFromString(tt.raw) + + if tt.error { + require.Error(t, err) + return + } + + require.NoError(t, err) + require.Equal(t, tt.lt, lt) + require.Equal(t, hash.Hex(), tt.hash) + }) + } +} diff --git a/zetaclient/chains/ton/observer/inbound.go b/zetaclient/chains/ton/observer/inbound.go new file mode 100644 index 0000000000..95f9a510d7 --- /dev/null +++ b/zetaclient/chains/ton/observer/inbound.go @@ -0,0 +1,260 @@ +package observer + +import ( + "context" + "encoding/hex" + "fmt" + + "cosmossdk.io/math" + "github.com/pkg/errors" + "github.com/rs/zerolog" + "github.com/tonkeeper/tongo/ton" + + "github.com/zeta-chain/node/pkg/coin" + toncontracts "github.com/zeta-chain/node/pkg/contracts/ton" + "github.com/zeta-chain/node/pkg/ticker" + "github.com/zeta-chain/node/zetaclient/chains/ton/liteapi" + zctx "github.com/zeta-chain/node/zetaclient/context" + "github.com/zeta-chain/node/zetaclient/zetacore" +) + +const ( + // MaxTransactionsPerTick is the maximum number of transactions to process on a ticker + MaxTransactionsPerTick = 100 +) + +func (ob *Observer) watchInbound(ctx context.Context) error { + app, err := zctx.FromContext(ctx) + if err != nil { + return err + } + + var ( + chainID = ob.Chain().ChainId + initialInterval = ticker.SecondsFromUint64(ob.ChainParams().InboundTicker) + sampledLogger = ob.Logger().Inbound.Sample(&zerolog.BasicSampler{N: 10}) + ) + + ob.Logger().Inbound.Info().Msgf("WatchInbound started for chain %d", chainID) + + task := func(ctx context.Context, t *ticker.Ticker) error { + if !app.IsInboundObservationEnabled() { + sampledLogger.Info().Msgf("WatchInbound: inbound observation is disabled for chain %d", chainID) + return nil + } + + if err := ob.observeInbound(ctx); err != nil { + ob.Logger().Inbound.Err(err).Msg("WatchInbound: observeInbound error") + } + + newInterval := ticker.SecondsFromUint64(ob.ChainParams().InboundTicker) + t.SetInterval(newInterval) + + return nil + } + + return ticker.Run( + ctx, + initialInterval, + task, + ticker.WithStopChan(ob.StopChannel()), + ticker.WithLogger(ob.Logger().Inbound, "WatchInbound"), + ) +} + +func (ob *Observer) observeInbound(ctx context.Context) error { + if err := ob.ensureLastScannedTX(ctx); err != nil { + return errors.Wrap(err, "unable to ensure last scanned tx") + } + + // extract logicalTime and tx hash from last scanned tx + lt, hashBits, err := liteapi.TransactionHashFromString(ob.LastTxScanned()) + if err != nil { + return errors.Wrapf(err, "unable to parse last scanned tx %q", ob.LastTxScanned()) + } + + txs, err := ob.client.GetTransactionsSince(ctx, ob.gateway.AccountID(), lt, hashBits) + if err != nil { + return errors.Wrap(err, "unable to get transactions") + } + + switch { + case len(txs) == 0: + // noop + return nil + case len(txs) > MaxTransactionsPerTick: + ob.Logger().Inbound.Info(). + Msgf("observeInbound: got %d transactions. Taking first %d", len(txs), MaxTransactionsPerTick) + + txs = txs[:MaxTransactionsPerTick] + default: + ob.Logger().Inbound.Info().Msgf("observeInbound: got %d transactions", len(txs)) + } + + for i := range txs { + tx := txs[i] + + parsedTX, skip, err := ob.gateway.ParseAndFilter(tx, toncontracts.FilterInbounds) + if err != nil { + return errors.Wrap(err, "unable to parse and filter tx") + } + + if skip { + ob.Logger().Inbound.Info().Fields(txLogFields(&tx)).Msg("observeInbound: skipping tx") + ob.setLastScannedTX(&tx) + + continue + } + + if _, err := ob.voteInbound(ctx, parsedTX); err != nil { + ob.Logger().Inbound. + Error().Err(err). + Fields(txLogFields(&tx)). + Msg("observeInbound: unable to vote for tx") + + return errors.Wrapf(err, "unable to vote for inbound tx %s", tx.Hash().Hex()) + } + + ob.setLastScannedTX(&parsedTX.Transaction) + } + + return nil +} + +func (ob *Observer) voteInbound(ctx context.Context, tx *toncontracts.Transaction) (string, error) { + // noop + if tx.Operation == toncontracts.OpDonate { + ob.Logger().Inbound.Info(). + Uint64("tx.lt", tx.Lt). + Str("tx.hash", tx.Hash().Hex()). + Msg("Thank you rich folk for your donation!") + + return "", nil + } + + // TODO: Add compliance check + // https://github.com/zeta-chain/node/issues/2916 + + blockHeader, err := ob.client.GetBlockHeader(ctx, tx.BlockID, 0) + if err != nil { + return "", errors.Wrapf(err, "unable to get block header %s", tx.BlockID.String()) + } + + sender, amount, memo, err := extractInboundData(tx) + if err != nil { + return "", err + } + + seqno := blockHeader.MinRefMcSeqno + + return ob.voteDeposit(ctx, tx, sender, amount, memo, seqno) +} + +// extractInboundData parses Gateway tx into deposit (TON sender, amount, memo) +func extractInboundData(tx *toncontracts.Transaction) (string, math.Uint, []byte, error) { + switch tx.Operation { + case toncontracts.OpDeposit: + d, err := tx.Deposit() + if err != nil { + return "", math.NewUint(0), nil, err + } + + return d.Sender.ToRaw(), d.Amount, d.Memo(), nil + case toncontracts.OpDepositAndCall: + d, err := tx.DepositAndCall() + if err != nil { + return "", math.NewUint(0), nil, err + } + + return d.Sender.ToRaw(), d.Amount, d.Memo(), nil + default: + return "", math.NewUint(0), nil, fmt.Errorf("unknown operation %d", tx.Operation) + } +} + +func (ob *Observer) voteDeposit( + ctx context.Context, + tx *toncontracts.Transaction, + sender string, + amount math.Uint, + memo []byte, + seqno uint32, +) (string, error) { + const ( + eventIndex = 0 // not a smart contract call + coinType = coin.CoinType_Gas + asset = "" // empty for gas coin + gasLimit = 0 + retryGasLimit = zetacore.PostVoteInboundExecutionGasLimit + ) + + var ( + operatorAddress = ob.ZetacoreClient().GetKeys().GetOperatorAddress() + inboundHash = liteapi.TransactionHashToString(tx.Lt, ton.Bits256(tx.Hash())) + ) + + // TODO: use protocol contract v2 for deposit + // https://github.com/zeta-chain/node/issues/2967 + + msg := zetacore.GetInboundVoteMessage( + sender, + ob.Chain().ChainId, + sender, + sender, + ob.ZetacoreClient().Chain().ChainId, + amount, + hex.EncodeToString(memo), + inboundHash, + uint64(seqno), + gasLimit, + coinType, + asset, + operatorAddress.String(), + eventIndex, + ) + + return ob.PostVoteInbound(ctx, msg, retryGasLimit) +} + +func (ob *Observer) ensureLastScannedTX(ctx context.Context) error { + // noop + if ob.LastTxScanned() != "" { + return nil + } + + tx, _, err := ob.client.GetFirstTransaction(ctx, ob.gateway.AccountID()) + if err != nil { + return err + } + + ob.setLastScannedTX(tx) + + return nil +} + +func (ob *Observer) setLastScannedTX(tx *ton.Transaction) { + txHash := liteapi.TransactionHashToString(tx.Lt, ton.Bits256(tx.Hash())) + + ob.WithLastTxScanned(txHash) + + if err := ob.WriteLastTxScannedToDB(txHash); err != nil { + ob.Logger().Inbound.Error(). + Err(err). + Fields(txLogFields(tx)). + Msgf("setLastScannedTX: unable to WriteLastTxScannedToDB") + + return + } + + ob.Logger().Inbound.Info(). + Fields(txLogFields(tx)). + Msg("setLastScannedTX: WriteLastTxScannedToDB") +} + +func txLogFields(tx *ton.Transaction) map[string]any { + return map[string]any{ + "inbound.ton.lt": tx.Lt, + "inbound.ton.hash": tx.Hash().Hex(), + "inbound.ton.block_id": tx.BlockID.BlockID.String(), + } +} diff --git a/zetaclient/chains/ton/observer/inbound_test.go b/zetaclient/chains/ton/observer/inbound_test.go new file mode 100644 index 0000000000..281db44f79 --- /dev/null +++ b/zetaclient/chains/ton/observer/inbound_test.go @@ -0,0 +1,331 @@ +package observer + +import ( + "encoding/hex" + "testing" + + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tonkeeper/tongo/tlb" + "github.com/tonkeeper/tongo/ton" + toncontracts "github.com/zeta-chain/node/pkg/contracts/ton" + "github.com/zeta-chain/node/testutil/sample" + "github.com/zeta-chain/node/zetaclient/chains/ton/liteapi" +) + +func TestInbound(t *testing.T) { + gw := toncontracts.NewGateway( + ton.MustParseAccountID("0:997d889c815aeac21c47f86ae0e38383efc3c3463067582f6263ad48c5a1485b"), + ) + + t.Run("No gateway provided", func(t *testing.T) { + ts := newTestSuite(t) + + _, err := New(ts.baseObserver, ts.liteClient, nil) + require.Error(t, err) + }) + + t.Run("Ensure last scanned tx", func(t *testing.T) { + t.Run("Unable to get first tx", func(t *testing.T) { + // ARRANGE + ts := newTestSuite(t) + + // Given observer + ob, err := New(ts.baseObserver, ts.liteClient, gw) + require.NoError(t, err) + + // Given mocked lite client call + ts.OnGetFirstTransaction(gw.AccountID(), nil, 0, errors.New("oops")).Once() + + // ACT + // Observe inbounds once + err = ob.observeInbound(ts.ctx) + + // ASSERT + assert.ErrorContains(t, err, "unable to ensure last scanned tx") + assert.Empty(t, ob.LastTxScanned()) + }) + + t.Run("All good", func(t *testing.T) { + // ARRANGE + ts := newTestSuite(t) + + // Given mocked lite client calls + firstTX := sample.TONDonation(t, gw.AccountID(), toncontracts.Donation{ + Sender: sample.GenerateTONAccountID(), + Amount: tonCoins(t, "1"), + }) + + ts.OnGetFirstTransaction(gw.AccountID(), &firstTX, 0, nil).Once() + ts.OnGetTransactionsSince(gw.AccountID(), firstTX.Lt, txHash(firstTX), nil, nil).Once() + + // Given observer + ob, err := New(ts.baseObserver, ts.liteClient, gw) + require.NoError(t, err) + + // ACT + // Observe inbounds once + err = ob.observeInbound(ts.ctx) + + // ASSERT + assert.NoError(t, err) + + // Check that last scanned tx is set and is valid + lastScanned, err := ob.ReadLastTxScannedFromDB() + assert.NoError(t, err) + assert.Equal(t, ob.LastTxScanned(), lastScanned) + + lt, hash, err := liteapi.TransactionHashFromString(lastScanned) + assert.NoError(t, err) + assert.Equal(t, firstTX.Lt, lt) + assert.Equal(t, firstTX.Hash().Hex(), hash.Hex()) + }) + }) + + t.Run("Donation", func(t *testing.T) { + // ARRANGE + ts := newTestSuite(t) + + // Given observer + ob, err := New(ts.baseObserver, ts.liteClient, gw) + require.NoError(t, err) + + lastScanned := ts.SetupLastScannedTX(gw.AccountID()) + + // Given mocked lite client calls + donation := sample.TONDonation(t, gw.AccountID(), toncontracts.Donation{ + Sender: sample.GenerateTONAccountID(), + Amount: tonCoins(t, "12"), + }) + + txs := []ton.Transaction{donation} + + ts. + OnGetTransactionsSince(gw.AccountID(), lastScanned.Lt, txHash(lastScanned), txs, nil). + Once() + + // ACT + // Observe inbounds once + err = ob.observeInbound(ts.ctx) + + // ASSERT + assert.NoError(t, err) + + // nothing happened, but tx scanned + lt, hash, err := liteapi.TransactionHashFromString(ob.LastTxScanned()) + assert.NoError(t, err) + assert.Equal(t, donation.Lt, lt) + assert.Equal(t, donation.Hash().Hex(), hash.Hex()) + }) + + t.Run("Deposit", func(t *testing.T) { + // ARRANGE + ts := newTestSuite(t) + + // Given observer + ob, err := New(ts.baseObserver, ts.liteClient, gw) + require.NoError(t, err) + + lastScanned := ts.SetupLastScannedTX(gw.AccountID()) + + // Given mocked lite client calls + deposit := toncontracts.Deposit{ + Sender: sample.GenerateTONAccountID(), + Amount: tonCoins(t, "12"), + Recipient: sample.EthAddress(), + } + + depositTX := sample.TONDeposit(t, gw.AccountID(), deposit) + txs := []ton.Transaction{depositTX} + + ts. + OnGetTransactionsSince(gw.AccountID(), lastScanned.Lt, txHash(lastScanned), txs, nil). + Once() + + ts.MockGetBlockHeader(depositTX.BlockID) + + // ACT + // Observe inbounds once + err = ob.observeInbound(ts.ctx) + + // ASSERT + assert.NoError(t, err) + + // Check that cctx was sent to zetacore + require.Len(t, ts.votesBag, 1) + + // Check CCTX + cctx := ts.votesBag[0] + + assert.NotNil(t, cctx) + + assert.Equal(t, deposit.Sender.ToRaw(), cctx.Sender) + assert.Equal(t, ts.chain.ChainId, cctx.SenderChainId) + + assert.Equal(t, "", cctx.Asset) + assert.Equal(t, deposit.Amount.Uint64(), cctx.Amount.Uint64()) + assert.Equal(t, hex.EncodeToString(deposit.Recipient.Bytes()), cctx.Message) + + // Check hash & block height + expectedHash := liteapi.TransactionHashToString(depositTX.Lt, txHash(depositTX)) + assert.Equal(t, expectedHash, cctx.InboundHash) + + blockInfo, err := ts.liteClient.GetBlockHeader(ts.ctx, depositTX.BlockID, 0) + require.NoError(t, err) + + assert.Equal(t, uint64(blockInfo.MinRefMcSeqno), cctx.InboundBlockHeight) + }) + + t.Run("Deposit and call", func(t *testing.T) { + // ARRANGE + ts := newTestSuite(t) + + // Given observer + ob, err := New(ts.baseObserver, ts.liteClient, gw) + require.NoError(t, err) + + lastScanned := ts.SetupLastScannedTX(gw.AccountID()) + + // Given mocked lite client calls + const callData = "hey there" + depositAndCall := toncontracts.DepositAndCall{ + Deposit: toncontracts.Deposit{ + Sender: sample.GenerateTONAccountID(), + Amount: tonCoins(t, "4"), + Recipient: sample.EthAddress(), + }, + CallData: []byte(callData), + } + + depositAndCallTX := sample.TONDepositAndCall(t, gw.AccountID(), depositAndCall) + txs := []ton.Transaction{depositAndCallTX} + + ts. + OnGetTransactionsSince(gw.AccountID(), lastScanned.Lt, txHash(lastScanned), txs, nil). + Once() + + ts.MockGetBlockHeader(depositAndCallTX.BlockID) + + // ACT + // Observe inbounds once + err = ob.observeInbound(ts.ctx) + + // ASSERT + assert.NoError(t, err) + + // Check that cctx was sent to zetacore + require.Len(t, ts.votesBag, 1) + + // Check CCTX + cctx := ts.votesBag[0] + + assert.NotNil(t, cctx) + + assert.Equal(t, depositAndCall.Sender.ToRaw(), cctx.Sender) + assert.Equal(t, ts.chain.ChainId, cctx.SenderChainId) + + assert.Equal(t, "", cctx.Asset) + assert.Equal(t, depositAndCall.Amount.Uint64(), cctx.Amount.Uint64()) + + expectedMessage := hex.EncodeToString(append( + depositAndCall.Recipient.Bytes(), + []byte(callData)..., + )) + + assert.Equal(t, expectedMessage, cctx.Message) + + // Check hash & block height + expectedHash := liteapi.TransactionHashToString(depositAndCallTX.Lt, txHash(depositAndCallTX)) + assert.Equal(t, expectedHash, cctx.InboundHash) + + blockInfo, err := ts.liteClient.GetBlockHeader(ts.ctx, depositAndCallTX.BlockID, 0) + require.NoError(t, err) + + assert.Equal(t, uint64(blockInfo.MinRefMcSeqno), cctx.InboundBlockHeight) + }) + + t.Run("Multiple transactions", func(t *testing.T) { + // ARRANGE + ts := newTestSuite(t) + + // Given observer + ob, err := New(ts.baseObserver, ts.liteClient, gw) + require.NoError(t, err) + + lastScanned := ts.SetupLastScannedTX(gw.AccountID()) + + // Given several transactions + txs := []ton.Transaction{ + // should be skipped + sample.TONDonation(t, gw.AccountID(), toncontracts.Donation{ + Sender: sample.GenerateTONAccountID(), + Amount: tonCoins(t, "1"), + }), + // should be voted + sample.TONDeposit(t, gw.AccountID(), toncontracts.Deposit{ + Sender: sample.GenerateTONAccountID(), + Amount: tonCoins(t, "3"), + Recipient: sample.EthAddress(), + }), + // should be skipped (invalid inbound message) + sample.TONTransaction(t, sample.TONTransactionProps{ + Account: gw.AccountID(), + Input: &tlb.Message{}, + }), + // should be voted + sample.TONDeposit(t, gw.AccountID(), toncontracts.Deposit{ + Sender: sample.GenerateTONAccountID(), + Amount: tonCoins(t, "3"), + Recipient: sample.EthAddress(), + }), + // should be skipped (invalid inbound/outbound messages) + sample.TONTransaction(t, sample.TONTransactionProps{ + Account: gw.AccountID(), + Input: &tlb.Message{}, + Output: &tlb.Message{}, + }), + } + + ts. + OnGetTransactionsSince(gw.AccountID(), lastScanned.Lt, txHash(lastScanned), txs, nil). + Once() + + for _, tx := range txs { + ts.MockGetBlockHeader(tx.BlockID) + } + + // ACT + // Observe inbounds once + err = ob.observeInbound(ts.ctx) + + // ASSERT + assert.NoError(t, err) + + // Check that cctx was sent to zetacore + assert.Equal(t, 2, len(ts.votesBag)) + + var ( + hash1 = liteapi.TransactionHashToString(txs[1].Lt, txHash(txs[1])) + hash2 = liteapi.TransactionHashToString(txs[3].Lt, txHash(txs[3])) + ) + + assert.Equal(t, hash1, ts.votesBag[0].InboundHash) + assert.Equal(t, hash2, ts.votesBag[1].InboundHash) + + // Check that last scanned tx points to the last tx in a list (even if it was skipped) + var ( + lastTX = txs[len(txs)-1] + lastScannedHash = ob.LastTxScanned() + ) + + lastLT, lastHash, err := liteapi.TransactionHashFromString(lastScannedHash) + assert.NoError(t, err) + assert.Equal(t, lastTX.Lt, lastLT) + assert.Equal(t, lastTX.Hash().Hex(), lastHash.Hex()) + }) +} + +func txHash(tx ton.Transaction) ton.Bits256 { + return ton.Bits256(tx.Hash()) +} diff --git a/zetaclient/chains/ton/observer/observer.go b/zetaclient/chains/ton/observer/observer.go new file mode 100644 index 0000000000..e20742116a --- /dev/null +++ b/zetaclient/chains/ton/observer/observer.go @@ -0,0 +1,80 @@ +package observer + +import ( + "context" + "errors" + + "github.com/tonkeeper/tongo/tlb" + "github.com/tonkeeper/tongo/ton" + + "github.com/zeta-chain/node/pkg/bg" + toncontracts "github.com/zeta-chain/node/pkg/contracts/ton" + "github.com/zeta-chain/node/x/crosschain/types" + "github.com/zeta-chain/node/zetaclient/chains/base" + "github.com/zeta-chain/node/zetaclient/chains/interfaces" +) + +// Observer is a TON observer. +type Observer struct { + *base.Observer + + client LiteClient + gateway *toncontracts.Gateway +} + +// LiteClient represents a TON client +// +//go:generate mockery --name LiteClient --filename ton_liteclient.go --case underscore --output ../../../testutils/mocks +type LiteClient interface { + GetBlockHeader(ctx context.Context, blockID ton.BlockIDExt, mode uint32) (tlb.BlockInfo, error) + GetTransactionsSince(ctx context.Context, acc ton.AccountID, lt uint64, bits ton.Bits256) ([]ton.Transaction, error) + GetFirstTransaction(ctx context.Context, id ton.AccountID) (*ton.Transaction, int, error) +} + +var _ interfaces.ChainObserver = (*Observer)(nil) + +// New constructor for TON Observer. +func New(bo *base.Observer, client LiteClient, gateway *toncontracts.Gateway) (*Observer, error) { + switch { + case !bo.Chain().IsTONChain(): + return nil, errors.New("base observer chain is not TON") + case client == nil: + return nil, errors.New("liteapi client is nil") + case gateway == nil: + return nil, errors.New("gateway is nil") + } + + bo.LoadLastTxScanned() + + return &Observer{ + Observer: bo, + client: client, + gateway: gateway, + }, nil +} + +// Start starts the observer. This method is NOT blocking. +func (ob *Observer) Start(ctx context.Context) { + if ok := ob.Observer.Start(); !ok { + ob.Logger().Chain.Info().Msgf("observer is already started for chain %d", ob.Chain().ChainId) + return + } + + ob.Logger().Chain.Info().Msgf("observer is starting for chain %d", ob.Chain().ChainId) + + // Note that each `watch*` method has a ticker that will stop as soon as + // baseObserver.Stop() was called (ticker.WithStopChan) + + // watch for incoming txs and post votes to zetacore + bg.Work(ctx, ob.watchInbound, bg.WithName("WatchInbound"), bg.WithLogger(ob.Logger().Inbound)) + + // TODO: watchInboundTracker + // https://github.com/zeta-chain/node/issues/2935 + + // TODO: outbounds/withdrawals: (watchOutbound, watchGasPrice, watchRPCStatus) + // https://github.com/zeta-chain/node/issues/2807 +} + +func (ob *Observer) VoteOutboundIfConfirmed(_ context.Context, _ *types.CrossChainTx) (bool, error) { + return false, errors.New("not implemented") +} diff --git a/zetaclient/chains/ton/observer/observer_test.go b/zetaclient/chains/ton/observer/observer_test.go index e978a589b9..38c032eb4a 100644 --- a/zetaclient/chains/ton/observer/observer_test.go +++ b/zetaclient/chains/ton/observer/observer_test.go @@ -2,62 +2,171 @@ package observer import ( "context" - "encoding/json" - "strings" + "strconv" "testing" + "cosmossdk.io/math" + "github.com/rs/zerolog" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/tonkeeper/tongo/config" - "github.com/tonkeeper/tongo/liteapi" + "github.com/tonkeeper/tongo/tlb" + "github.com/tonkeeper/tongo/ton" + "github.com/zeta-chain/node/pkg/chains" + toncontracts "github.com/zeta-chain/node/pkg/contracts/ton" + "github.com/zeta-chain/node/testutil/sample" + cctxtypes "github.com/zeta-chain/node/x/crosschain/types" + observertypes "github.com/zeta-chain/node/x/observer/types" + "github.com/zeta-chain/node/zetaclient/chains/base" + "github.com/zeta-chain/node/zetaclient/chains/ton/liteapi" + "github.com/zeta-chain/node/zetaclient/db" + "github.com/zeta-chain/node/zetaclient/keys" + "github.com/zeta-chain/node/zetaclient/testutils/mocks" ) -// todo tmp (will be resolved automatically) -// taken from ton:8000/lite-client.json -const configRaw = `{"@type":"config.global","dht":{"@type":"dht.config.global","k":3,"a":3,"static_nodes": -{"@type":"dht.nodes","nodes":[]}},"liteservers":[{"id":{"key":"+DjLFqH/N5jO1ZO8PYVYU6a6e7EnnsF0GWFsteE+qy8=","@type": -"pub.ed25519"},"port":4443,"ip":2130706433}],"validator":{"@type":"validator.config.global","zero_state": -{"workchain":-1,"shard":-9223372036854775808,"seqno":0,"root_hash":"rR8EFZNlyj3rfYlMyQC8gT0A6ghDrbKe4aMmodiNw6I=", -"file_hash":"fT2hXGv1OF7XDhraoAELrYz6wX3ue16QpSoWTiPrUAE="},"init_block":{"workchain":-1,"shard":-9223372036854775808, -"seqno":0,"root_hash":"rR8EFZNlyj3rfYlMyQC8gT0A6ghDrbKe4aMmodiNw6I=", -"file_hash":"fT2hXGv1OF7XDhraoAELrYz6wX3ue16QpSoWTiPrUAE="}}}` +type testSuite struct { + ctx context.Context + t *testing.T -func TestObserver(t *testing.T) { - t.Skip("skip test") + chain chains.Chain + chainParams *observertypes.ChainParams - ctx := context.Background() + liteClient *mocks.LiteClient - cfg, err := config.ParseConfig(strings.NewReader(configRaw)) - require.NoError(t, err) + zetacore *mocks.ZetacoreClient + tss *mocks.TSS + database *db.DB + + baseObserver *base.Observer + + votesBag []*cctxtypes.MsgVoteInbound +} + +func newTestSuite(t *testing.T) *testSuite { + var ( + ctx = context.Background() - client, err := liteapi.NewClient(liteapi.WithConfigurationFile(*cfg)) + chain = chains.TONTestnet + chainParams = sample.ChainParams(chain.ChainId) + + liteClient = mocks.NewLiteClient(t) + + tss = mocks.NewTSSAthens3() + zetacore = mocks.NewZetacoreClient(t).WithKeys(&keys.Keys{}) + + testLogger = zerolog.New(zerolog.NewTestWriter(t)) + logger = base.Logger{Std: testLogger, Compliance: testLogger} + ) + + database, err := db.NewFromSqliteInMemory(true) require.NoError(t, err) - res, err := client.GetMasterchainInfo(ctx) + baseObserver, err := base.NewObserver( + chain, + *chainParams, + zetacore, + tss, + 1, + 1, + 60, + nil, + database, + logger, + ) + require.NoError(t, err) - // Outputs: - // { - // "Last": { - // "Workchain": 4294967295, - // "Shard": 9223372036854775808, - // "Seqno": 915, - // "RootHash": "2e9e312c5bd3b7b96d23ce1342ac76e5486012c9aac44781c2c25dbc55f5c8ad", - // "FileHash": "d3745319bfaeebb168d9db6bb5b4752b6b28ab9041735c81d4a02fc820040851" - // }, - // "StateRootHash": "02538fb9dc802004012285a90a7af9ba279706e2deea9ca635decd80e94a7045", - // "Init": { - // "Workchain": 4294967295, - // "RootHash": "ad1f04159365ca3deb7d894cc900bc813d00ea0843adb29ee1a326a1d88dc3a2", - // "FileHash": "7d3da15c6bf5385ed70e1adaa0010bad8cfac17dee7b5e90a52a164e23eb5001" - // } - // } - t.Logf("Masterchain info") - logJSON(t, res) -} - -func logJSON(t *testing.T, v any) { - b, err := json.MarshalIndent(v, "", " ") + ts := &testSuite{ + ctx: ctx, + t: t, + + chain: chain, + chainParams: chainParams, + + liteClient: liteClient, + + zetacore: zetacore, + tss: tss, + database: database, + + baseObserver: baseObserver, + } + + // Setup mocks + ts.zetacore.On("Chain").Return(chain).Maybe() + + setupVotesBag(ts) + + return ts +} + +func (ts *testSuite) SetupLastScannedTX(gw ton.AccountID) ton.Transaction { + lastScannedTX := sample.TONDonation(ts.t, gw, toncontracts.Donation{ + Sender: sample.GenerateTONAccountID(), + Amount: tonCoins(ts.t, "1"), + }) + + txHash := liteapi.TransactionHashToString(lastScannedTX.Lt, ton.Bits256(lastScannedTX.Hash())) + + ts.baseObserver.WithLastTxScanned(txHash) + require.NoError(ts.t, ts.baseObserver.WriteLastTxScannedToDB(txHash)) + + return lastScannedTX +} + +func (ts *testSuite) OnGetFirstTransaction(acc ton.AccountID, tx *ton.Transaction, scanned int, err error) *mock.Call { + return ts.liteClient. + On("GetFirstTransaction", ts.ctx, acc). + Return(tx, scanned, err) +} + +func (ts *testSuite) OnGetTransactionsSince( + acc ton.AccountID, + lt uint64, + hash ton.Bits256, + txs []ton.Transaction, + err error, +) *mock.Call { + return ts.liteClient. + On("GetTransactionsSince", mock.Anything, acc, lt, hash). + Return(txs, err) +} + +func (ts *testSuite) MockGetBlockHeader(id ton.BlockIDExt) *mock.Call { + // let's pretend that block's masterchain ref has the same seqno + blockInfo := tlb.BlockInfo{ + BlockInfoPart: tlb.BlockInfoPart{MinRefMcSeqno: id.Seqno}, + } + + return ts.liteClient. + On("GetBlockHeader", mock.Anything, id, uint32(0)). + Return(blockInfo, nil) +} + +// parses string to TON +func tonCoins(t *testing.T, raw string) math.Uint { + t.Helper() + + const oneTON = 1_000_000_000 + + f, err := strconv.ParseFloat(raw, 64) require.NoError(t, err) - t.Log(string(b)) + f *= oneTON + + return math.NewUint(uint64(f)) +} + +func setupVotesBag(ts *testSuite) { + catcher := func(args mock.Arguments) { + vote := args.Get(3) + cctx, ok := vote.(*cctxtypes.MsgVoteInbound) + require.True(ts.t, ok, "unexpected cctx type") + + ts.votesBag = append(ts.votesBag, cctx) + } + ts.zetacore. + On("PostVoteInbound", ts.ctx, mock.Anything, mock.Anything, mock.Anything). + Maybe(). + Run(catcher). + Return("", "", nil) // zeta hash, ballot index, error } diff --git a/zetaclient/compliance/compliance.go b/zetaclient/compliance/compliance.go index 163cca65e4..f0135c3ad9 100644 --- a/zetaclient/compliance/compliance.go +++ b/zetaclient/compliance/compliance.go @@ -7,7 +7,7 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" "github.com/rs/zerolog" - "github.com/zeta-chain/node/pkg/chains" + "github.com/zeta-chain/node/pkg/memo" crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" "github.com/zeta-chain/node/zetaclient/chains/base" "github.com/zeta-chain/node/zetaclient/config" @@ -66,7 +66,7 @@ func PrintComplianceLog( func DoesInboundContainsRestrictedAddress(event *clienttypes.InboundEvent, logger *base.ObserverLogger) bool { // parse memo-specified receiver receiver := "" - parsedAddress, _, err := chains.ParseAddressAndData(hex.EncodeToString(event.Memo)) + parsedAddress, _, err := memo.DecodeLegacyMemoHex(hex.EncodeToString(event.Memo)) if err == nil && parsedAddress != (ethcommon.Address{}) { receiver = parsedAddress.Hex() } diff --git a/zetaclient/config/config_chain.go b/zetaclient/config/config_chain.go index ca0234c126..6f17153b52 100644 --- a/zetaclient/config/config_chain.go +++ b/zetaclient/config/config_chain.go @@ -23,6 +23,7 @@ func New(setDefaults bool) Config { cfg.EVMChainConfigs = evmChainsConfigs() cfg.BTCChainConfigs = btcChainsConfigs() cfg.SolanaConfig = solanaConfigLocalnet() + cfg.TONConfig = tonConfigLocalnet() } return cfg @@ -47,6 +48,13 @@ func solanaConfigLocalnet() SolanaConfig { } } +func tonConfigLocalnet() TONConfig { + return TONConfig{ + LiteClientConfigURL: "http://ton:8000/lite-client.json", + RPCAlertLatency: 60, + } +} + // evmChainsConfigs contains EVM chain configs // it contains list of EVM chains with empty endpoint except for localnet func evmChainsConfigs() map[int64]EVMConfig { diff --git a/zetaclient/config/types.go b/zetaclient/config/types.go index bf225b97f4..a60875b5e8 100644 --- a/zetaclient/config/types.go +++ b/zetaclient/config/types.go @@ -60,6 +60,13 @@ type SolanaConfig struct { RPCAlertLatency int64 } +// TONConfig is the config for TON chain +type TONConfig struct { + // Can be either URL of local file path + LiteClientConfigURL string `json:"liteClientConfigURL"` + RPCAlertLatency int64 `json:"rpcAlertLatency"` +} + // ComplianceConfig is the config for compliance type ComplianceConfig struct { LogPath string `json:"LogPath"` @@ -97,6 +104,7 @@ type Config struct { // Deprecated: the 'BitcoinConfig' will be removed once the 'BTCChainConfigs' is fully adopted BitcoinConfig BTCConfig `json:"BitcoinConfig"` SolanaConfig SolanaConfig `json:"SolanaConfig"` + TONConfig TONConfig `json:"TONConfig"` // compliance config ComplianceConfig ComplianceConfig `json:"ComplianceConfig"` @@ -149,6 +157,14 @@ func (c Config) GetSolanaConfig() (SolanaConfig, bool) { return c.SolanaConfig, c.SolanaConfig != (SolanaConfig{}) } +// GetTONConfig returns the TONConfig and a bool indicating if it's present. +func (c Config) GetTONConfig() (TONConfig, bool) { + c.mu.RLock() + defer c.mu.RUnlock() + + return c.TONConfig, c.TONConfig != TONConfig{} +} + // StringMasked returns the string representation of the config with sensitive fields masked. // Currently only the endpoints and bitcoin credentials are masked. func (c Config) StringMasked() string { diff --git a/zetaclient/config/types_test.go b/zetaclient/config/types_test.go index c57fd002e0..02f7eb5a6f 100644 --- a/zetaclient/config/types_test.go +++ b/zetaclient/config/types_test.go @@ -128,6 +128,8 @@ func Test_StringMasked(t *testing.T) { // create config with defaults cfg := config.New(true) + cfg.SolanaConfig.Endpoint += "?api-key=123" + // mask the config JSON string masked := cfg.StringMasked() require.NotEmpty(t, masked) @@ -137,5 +139,5 @@ func Test_StringMasked(t *testing.T) { require.Contains(t, masked, "BTCChainConfigs") // should not contain endpoint - require.NotContains(t, masked, "http") + require.NotContains(t, masked, "?api-key=123") } diff --git a/zetaclient/context/app_test.go b/zetaclient/context/app_test.go index 7439aebc44..2297a3cdec 100644 --- a/zetaclient/context/app_test.go +++ b/zetaclient/context/app_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/zeta-chain/node/pkg/chains" + "github.com/zeta-chain/node/testutil/sample" "github.com/zeta-chain/node/x/observer/types" "github.com/zeta-chain/node/zetaclient/config" "golang.org/x/exp/maps" @@ -38,8 +39,7 @@ func TestAppContext(t *testing.T) { btcParams := types.GetDefaultBtcMainnetChainParams() btcParams.IsSupported = true - solParams := types.GetDefaultSolanaLocalnetChainParams() - solParams.IsSupported = true + solParams := sample.ChainParamsSupported(chains.SolanaLocalnet.ChainId) fancyL2 := chains.Chain{ ChainId: 123, diff --git a/zetaclient/context/chain.go b/zetaclient/context/chain.go index 117b921168..4f35953ce0 100644 --- a/zetaclient/context/chain.go +++ b/zetaclient/context/chain.go @@ -1,6 +1,7 @@ package context import ( + "cmp" "fmt" "sync" @@ -65,7 +66,9 @@ func (cr *ChainRegistry) Get(chainID int64) (Chain, error) { func (cr *ChainRegistry) All() []Chain { items := maps.Values(cr.chains) - slices.SortFunc(items, func(a, b Chain) bool { return a.ID() < b.ID() }) + slices.SortFunc(items, func(a, b Chain) int { + return cmp.Compare(a.ID(), b.ID()) + }) return items } @@ -165,6 +168,10 @@ func (c Chain) IsSolana() bool { return chains.IsSolanaChain(c.ID(), c.registry.additionalChains) } +func (c Chain) IsTON() bool { + return chains.IsTONChain(c.ID(), c.registry.additionalChains) +} + // RelayerKeyPassword returns the relayer key password for the chain func (c Chain) RelayerKeyPassword() string { network := c.RawChain().Network diff --git a/zetaclient/logs/fields.go b/zetaclient/logs/fields.go index 497690ffa4..78b95fc7e0 100644 --- a/zetaclient/logs/fields.go +++ b/zetaclient/logs/fields.go @@ -3,12 +3,13 @@ package logs // A group of predefined field keys and module names for zetaclient logs const ( // field keys - FieldModule = "module" - FieldMethod = "method" - FieldChain = "chain" - FieldNonce = "nonce" - FieldTx = "tx" - FieldCctx = "cctx" + FieldModule = "module" + FieldMethod = "method" + FieldChain = "chain" + FieldChainNetwork = "chain_network" + FieldNonce = "nonce" + FieldTx = "tx" + FieldCctx = "cctx" // module names ModNameInbound = "inbound" diff --git a/zetaclient/orchestrator/bootstap_test.go b/zetaclient/orchestrator/bootstap_test.go index 73f47d21cf..eaae3a8e6d 100644 --- a/zetaclient/orchestrator/bootstap_test.go +++ b/zetaclient/orchestrator/bootstap_test.go @@ -21,7 +21,11 @@ import ( "github.com/zeta-chain/node/zetaclient/testutils/testrpc" ) -const solanaGatewayAddress = "2kJndCL9NBR36ySiQ4bmArs4YgWQu67LmCDfLzk5Gb7s" +const ( + solanaGatewayAddress = "2kJndCL9NBR36ySiQ4bmArs4YgWQu67LmCDfLzk5Gb7s" + tonGatewayAddress = "0:997d889c815aeac21c47f86ae0e38383efc3c3463067582f6263ad48c5a1485b" + tonMainnet = "https://ton.org/global-config.json" +) func TestCreateSignerMap(t *testing.T) { var ( @@ -211,9 +215,12 @@ func TestCreateChainObserverMap(t *testing.T) { evmServer := testrpc.NewEVMServer(t) evmServer.SetBlockNumber(100) - // Given generic SOL RPC + // Given SOL config _, solConfig := testrpc.NewSolanaServer(t) + // Given TON config + tonConfig := config.TONConfig{LiteClientConfigURL: tonMainnet, RPCAlertLatency: 1} + // Given a zetaclient config with ETH, MATIC, and BTC chains cfg := config.New(false) @@ -229,6 +236,7 @@ func TestCreateChainObserverMap(t *testing.T) { cfg.BTCChainConfigs[chains.BitcoinMainnet.ChainId] = btcConfig cfg.SolanaConfig = solConfig + cfg.TONConfig = tonConfig // Given AppContext app := zctx.New(cfg, nil, log) @@ -239,6 +247,7 @@ func TestCreateChainObserverMap(t *testing.T) { mustUpdateAppContextChainParams(t, app, []chains.Chain{ chains.Ethereum, chains.BitcoinMainnet, + chains.TONMainnet, }) // ACT @@ -249,11 +258,12 @@ func TestCreateChainObserverMap(t *testing.T) { assert.NotEmpty(t, observers) // Okay, now we want to check that signers for EVM and BTC were created - assert.Equal(t, 2, len(observers)) + assert.Equal(t, 3, len(observers)) hasObserver(t, observers, chains.Ethereum.ChainId) hasObserver(t, observers, chains.BitcoinMainnet.ChainId) + hasObserver(t, observers, chains.TONMainnet.ChainId) - t.Run("Add polygon in the runtime", func(t *testing.T) { + t.Run("Add polygon and remove TON in the runtime", func(t *testing.T) { // ARRANGE mustUpdateAppContextChainParams(t, app, []chains.Chain{ chains.Ethereum, chains.BitcoinMainnet, chains.Polygon, @@ -265,7 +275,7 @@ func TestCreateChainObserverMap(t *testing.T) { // ASSERT assert.NoError(t, err) assert.Equal(t, 1, added) - assert.Equal(t, 0, removed) + assert.Equal(t, 1, removed) hasObserver(t, observers, chains.Ethereum.ChainId) hasObserver(t, observers, chains.Polygon.ChainId) @@ -400,6 +410,11 @@ func chainParams(supportedChains []chains.Chain) ([]chains.Chain, map[int64]*obs continue } + if chains.IsEVMChain(chainID, nil) { + params[chainID] = ptr.Ptr(mocks.MockChainParams(chainID, 100)) + continue + } + if chains.IsSolanaChain(chainID, nil) { p := mocks.MockChainParams(chainID, 100) p.GatewayAddress = solanaGatewayAddress @@ -407,10 +422,14 @@ func chainParams(supportedChains []chains.Chain) ([]chains.Chain, map[int64]*obs continue } - if chains.IsEVMChain(chainID, nil) { - params[chainID] = ptr.Ptr(mocks.MockChainParams(chainID, 100)) + if chains.IsTONChain(chainID, nil) { + p := mocks.MockChainParams(chainID, 100) + p.GatewayAddress = tonGatewayAddress + params[chainID] = &p continue } + + panic("unknown chain: " + chain.String()) } return supportedChains, params diff --git a/zetaclient/orchestrator/bootstrap.go b/zetaclient/orchestrator/bootstrap.go index 08d625548f..34c94bf77d 100644 --- a/zetaclient/orchestrator/bootstrap.go +++ b/zetaclient/orchestrator/bootstrap.go @@ -9,7 +9,9 @@ import ( solrpc "github.com/gagliardetto/solana-go/rpc" ethrpc2 "github.com/onrik/ethrpc" "github.com/pkg/errors" + "github.com/tonkeeper/tongo/ton" + toncontracts "github.com/zeta-chain/node/pkg/contracts/ton" "github.com/zeta-chain/node/zetaclient/chains/base" btcobserver "github.com/zeta-chain/node/zetaclient/chains/bitcoin/observer" "github.com/zeta-chain/node/zetaclient/chains/bitcoin/rpc" @@ -19,9 +21,12 @@ import ( "github.com/zeta-chain/node/zetaclient/chains/interfaces" solbserver "github.com/zeta-chain/node/zetaclient/chains/solana/observer" solanasigner "github.com/zeta-chain/node/zetaclient/chains/solana/signer" + "github.com/zeta-chain/node/zetaclient/chains/ton/liteapi" + tonobserver "github.com/zeta-chain/node/zetaclient/chains/ton/observer" zctx "github.com/zeta-chain/node/zetaclient/context" "github.com/zeta-chain/node/zetaclient/db" "github.com/zeta-chain/node/zetaclient/keys" + "github.com/zeta-chain/node/zetaclient/logs" "github.com/zeta-chain/node/zetaclient/metrics" ) @@ -71,7 +76,7 @@ func syncSignerMap( presentChainIDs = make([]int64, 0) onAfterAdd = func(chainID int64, _ interfaces.ChainSigner) { - logger.Std.Info().Msgf("Added signer for chain %d", chainID) + logger.Std.Info().Int64(logs.FieldChain, chainID).Msg("Added signer") added++ } @@ -80,7 +85,7 @@ func syncSignerMap( } onBeforeRemove = func(chainID int64, _ interfaces.ChainSigner) { - logger.Std.Info().Msgf("Removing signer for chain %d", chainID) + logger.Std.Info().Int64(logs.FieldChain, chainID).Msg("Removing signer") removed++ } ) @@ -181,6 +186,9 @@ func syncSignerMap( } addSigner(chainID, signer) + case chain.IsTON(): + logger.Std.Error().Err(err).Msgf("TON signer is not implemented yet for chain id %d", chainID) + continue default: logger.Std.Warn(). Int64("signer.chain_id", chain.ID()). @@ -238,7 +246,8 @@ func syncObserverMap( presentChainIDs = make([]int64, 0) - onAfterAdd = func(_ int64, ob interfaces.ChainObserver) { + onAfterAdd = func(chainID int64, ob interfaces.ChainObserver) { + logger.Std.Info().Int64(logs.FieldChain, chainID).Msg("Added observer") ob.Start(ctx) added++ } @@ -247,7 +256,8 @@ func syncObserverMap( mapSet[int64, interfaces.ChainObserver](observerMap, chainID, ob, onAfterAdd) } - onBeforeRemove = func(_ int64, ob interfaces.ChainObserver) { + onBeforeRemove = func(chainID int64, ob interfaces.ChainObserver) { + logger.Std.Info().Int64(logs.FieldChain, chainID).Msg("Removing observer") ob.Stop() removed++ } @@ -394,6 +404,58 @@ func syncObserverMap( } addObserver(chainID, solObserver) + case chain.IsTON(): + cfg, found := app.Config().GetTONConfig() + if !found { + logger.Std.Warn().Msgf("Unable to find chain params for TON chain %d", chainID) + continue + } + + database, err := db.NewFromSqlite(dbpath, chainName, true) + if err != nil { + logger.Std.Error().Err(err).Msgf("unable to open database for TON chain %d", chainID) + continue + } + + baseObserver, err := base.NewObserver( + *rawChain, + *params, + client, + tss, + base.DefaultBlockCacheSize, + base.DefaultHeaderCacheSize, + cfg.RPCAlertLatency, + ts, + database, + logger, + ) + + if err != nil { + logger.Std.Error().Err(err).Msgf("Unable to create base observer for TON chain %d", chainID) + continue + } + + tonClient, err := liteapi.NewFromSource(ctx, cfg.LiteClientConfigURL) + if err != nil { + logger.Std.Error().Err(err).Msgf("Unable to create TON liteapi for chain %d", chainID) + continue + } + + gatewayID, err := ton.ParseAccountID(params.GatewayAddress) + if err != nil { + logger.Std.Error().Err(err). + Msgf("Unable to parse gateway address %q for chain %d", params.GatewayAddress, chainID) + continue + } + + gw := toncontracts.NewGateway(gatewayID) + tonObserver, err := tonobserver.New(baseObserver, tonClient, gw) + if err != nil { + logger.Std.Error().Err(err).Msgf("Unable to create TON observer for chain %d", chainID) + continue + } + + addObserver(chainID, tonObserver) default: logger.Std.Warn(). Int64("observer.chain_id", chain.ID()). diff --git a/zetaclient/orchestrator/orchestrator.go b/zetaclient/orchestrator/orchestrator.go index dd0ef1eaab..698a520fcf 100644 --- a/zetaclient/orchestrator/orchestrator.go +++ b/zetaclient/orchestrator/orchestrator.go @@ -17,6 +17,7 @@ import ( "github.com/zeta-chain/node/pkg/bg" "github.com/zeta-chain/node/pkg/constant" zetamath "github.com/zeta-chain/node/pkg/math" + "github.com/zeta-chain/node/pkg/ticker" "github.com/zeta-chain/node/x/crosschain/types" observertypes "github.com/zeta-chain/node/x/observer/types" "github.com/zeta-chain/node/zetaclient/chains/base" @@ -228,7 +229,7 @@ func (oc *Orchestrator) resolveObserver(app *zctx.AppContext, chainID int64) (in // update chain observer chain parameters var ( - curParams = observer.GetChainParams() + curParams = observer.ChainParams() freshParams = chain.Params() ) @@ -447,11 +448,11 @@ func (oc *Orchestrator) ScheduleCctxEVM( for _, v := range res { trackerMap[v.Nonce] = true } - outboundScheduleLookahead := observer.GetChainParams().OutboundScheduleLookahead + outboundScheduleLookahead := observer.ChainParams().OutboundScheduleLookahead // #nosec G115 always in range outboundScheduleLookback := uint64(float64(outboundScheduleLookahead) * evmOutboundLookbackFactor) // #nosec G115 positive - outboundScheduleInterval := uint64(observer.GetChainParams().OutboundScheduleInterval) + outboundScheduleInterval := uint64(observer.ChainParams().OutboundScheduleInterval) criticalInterval := uint64(10) // for critical pending outbound we reduce re-try interval nonCriticalInterval := outboundScheduleInterval * 2 // for non-critical pending outbound we increase re-try interval @@ -546,8 +547,8 @@ func (oc *Orchestrator) ScheduleCctxBTC( return } // #nosec G115 positive - interval := uint64(observer.GetChainParams().OutboundScheduleInterval) - lookahead := observer.GetChainParams().OutboundScheduleLookahead + interval := uint64(observer.ChainParams().OutboundScheduleInterval) + lookahead := observer.ChainParams().OutboundScheduleLookahead // schedule at most one keysign per ticker for idx, cctx := range cctxList { @@ -618,7 +619,7 @@ func (oc *Orchestrator) ScheduleCctxSolana( return } // #nosec G701 positive - interval := uint64(observer.GetChainParams().OutboundScheduleInterval) + interval := uint64(observer.ChainParams().OutboundScheduleInterval) // schedule keysign for each pending cctx for _, cctx := range cctxList { @@ -666,28 +667,18 @@ func (oc *Orchestrator) ScheduleCctxSolana( // runObserverSignerSync runs a blocking ticker that observes chain changes from zetacore // and optionally (de)provisions respective observers and signers. func (oc *Orchestrator) runObserverSignerSync(ctx context.Context) error { - // sync observers and signers right away to speed up zetaclient startup - if err := oc.syncObserverSigner(ctx); err != nil { - oc.logger.Error().Err(err).Msg("runObserverSignerSync: syncObserverSigner failed for initial sync") - } - - // sync observer and signer every 10 blocks (approx. 1 minute) - const cadence = 10 * constant.ZetaBlockTime - - ticker := time.NewTicker(cadence) - defer ticker.Stop() + // every other block + const cadence = 2 * constant.ZetaBlockTime - for { - select { - case <-oc.stop: - oc.logger.Warn().Msg("runObserverSignerSync: stopped") - return nil - case <-ticker.C: - if err := oc.syncObserverSigner(ctx); err != nil { - oc.logger.Error().Err(err).Msg("runObserverSignerSync: syncObserverSigner failed") - } + task := func(ctx context.Context, _ *ticker.Ticker) error { + if err := oc.syncObserverSigner(ctx); err != nil { + oc.logger.Error().Err(err).Msg("syncObserverSigner failed") } + + return nil } + + return ticker.Run(ctx, cadence, task, ticker.WithLogger(oc.logger.Logger, "SyncObserverSigner")) } // syncs and provisions observers & signers. diff --git a/zetaclient/orchestrator/orchestrator_test.go b/zetaclient/orchestrator/orchestrator_test.go index 969dbbb393..2ab34b900e 100644 --- a/zetaclient/orchestrator/orchestrator_test.go +++ b/zetaclient/orchestrator/orchestrator_test.go @@ -196,7 +196,7 @@ func Test_GetUpdatedChainObserver(t *testing.T) { chainOb, err := orchestrator.resolveObserver(appContext, evmChain.ChainId) require.NoError(t, err) require.NotNil(t, chainOb) - require.True(t, observertypes.ChainParamsEqual(*evmChainParamsNew, chainOb.GetChainParams())) + require.True(t, observertypes.ChainParamsEqual(*evmChainParamsNew, chainOb.ChainParams())) }) t.Run("btc chain observer should not be found", func(t *testing.T) { @@ -244,7 +244,7 @@ func Test_GetUpdatedChainObserver(t *testing.T) { chainOb, err := orchestrator.resolveObserver(appContext, btcChain.ChainId) require.NoError(t, err) require.NotNil(t, chainOb) - require.True(t, observertypes.ChainParamsEqual(*btcChainParamsNew, chainOb.GetChainParams())) + require.True(t, observertypes.ChainParamsEqual(*btcChainParamsNew, chainOb.ChainParams())) }) t.Run("solana chain observer should not be found", func(t *testing.T) { orchestrator := mockOrchestrator( @@ -282,7 +282,7 @@ func Test_GetUpdatedChainObserver(t *testing.T) { chainOb, err := orchestrator.resolveObserver(appContext, solChain.ChainId) require.NoError(t, err) require.NotNil(t, chainOb) - require.True(t, observertypes.ChainParamsEqual(*solChainParamsNew, chainOb.GetChainParams())) + require.True(t, observertypes.ChainParamsEqual(*solChainParamsNew, chainOb.ChainParams())) }) } diff --git a/zetaclient/testutils/mocks/chain_clients.go b/zetaclient/testutils/mocks/chain_clients.go index 94f636bf4e..aa5e36889b 100644 --- a/zetaclient/testutils/mocks/chain_clients.go +++ b/zetaclient/testutils/mocks/chain_clients.go @@ -15,12 +15,12 @@ var _ interfaces.ChainObserver = (*EVMObserver)(nil) // EVMObserver is a mock of evm chain observer for testing type EVMObserver struct { - ChainParams observertypes.ChainParams + chainParams observertypes.ChainParams } func NewEVMObserver(chainParams *observertypes.ChainParams) *EVMObserver { return &EVMObserver{ - ChainParams: *chainParams, + chainParams: *chainParams, } } @@ -35,11 +35,11 @@ func (ob *EVMObserver) VoteOutboundIfConfirmed( } func (ob *EVMObserver) SetChainParams(chainParams observertypes.ChainParams) { - ob.ChainParams = chainParams + ob.chainParams = chainParams } -func (ob *EVMObserver) GetChainParams() observertypes.ChainParams { - return ob.ChainParams +func (ob *EVMObserver) ChainParams() observertypes.ChainParams { + return ob.chainParams } func (ob *EVMObserver) GetTxID(_ uint64) string { @@ -57,12 +57,12 @@ var _ interfaces.ChainObserver = (*BTCObserver)(nil) // BTCObserver is a mock of btc chain observer for testing type BTCObserver struct { - ChainParams observertypes.ChainParams + chainParams observertypes.ChainParams } func NewBTCObserver(chainParams *observertypes.ChainParams) *BTCObserver { return &BTCObserver{ - ChainParams: *chainParams, + chainParams: *chainParams, } } @@ -78,11 +78,11 @@ func (ob *BTCObserver) VoteOutboundIfConfirmed( } func (ob *BTCObserver) SetChainParams(chainParams observertypes.ChainParams) { - ob.ChainParams = chainParams + ob.chainParams = chainParams } -func (ob *BTCObserver) GetChainParams() observertypes.ChainParams { - return ob.ChainParams +func (ob *BTCObserver) ChainParams() observertypes.ChainParams { + return ob.chainParams } func (ob *BTCObserver) GetTxID(_ uint64) string { @@ -98,12 +98,12 @@ var _ interfaces.ChainObserver = (*SolanaObserver)(nil) // SolanaObserver is a mock of solana chain observer for testing type SolanaObserver struct { - ChainParams observertypes.ChainParams + chainParams observertypes.ChainParams } func NewSolanaObserver(chainParams *observertypes.ChainParams) *SolanaObserver { return &SolanaObserver{ - ChainParams: *chainParams, + chainParams: *chainParams, } } @@ -119,11 +119,11 @@ func (ob *SolanaObserver) VoteOutboundIfConfirmed( } func (ob *SolanaObserver) SetChainParams(chainParams observertypes.ChainParams) { - ob.ChainParams = chainParams + ob.chainParams = chainParams } -func (ob *SolanaObserver) GetChainParams() observertypes.ChainParams { - return ob.ChainParams +func (ob *SolanaObserver) ChainParams() observertypes.ChainParams { + return ob.chainParams } func (ob *SolanaObserver) GetTxID(_ uint64) string { diff --git a/zetaclient/testutils/mocks/ton_liteclient.go b/zetaclient/testutils/mocks/ton_liteclient.go new file mode 100644 index 0000000000..f11ccaf24c --- /dev/null +++ b/zetaclient/testutils/mocks/ton_liteclient.go @@ -0,0 +1,127 @@ +// Code generated by mockery v2.43.2. DO NOT EDIT. + +package mocks + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" + + tlb "github.com/tonkeeper/tongo/tlb" + + ton "github.com/tonkeeper/tongo/ton" +) + +// LiteClient is an autogenerated mock type for the LiteClient type +type LiteClient struct { + mock.Mock +} + +// GetBlockHeader provides a mock function with given fields: ctx, blockID, mode +func (_m *LiteClient) GetBlockHeader(ctx context.Context, blockID ton.BlockIDExt, mode uint32) (tlb.BlockInfo, error) { + ret := _m.Called(ctx, blockID, mode) + + if len(ret) == 0 { + panic("no return value specified for GetBlockHeader") + } + + var r0 tlb.BlockInfo + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, ton.BlockIDExt, uint32) (tlb.BlockInfo, error)); ok { + return rf(ctx, blockID, mode) + } + if rf, ok := ret.Get(0).(func(context.Context, ton.BlockIDExt, uint32) tlb.BlockInfo); ok { + r0 = rf(ctx, blockID, mode) + } else { + r0 = ret.Get(0).(tlb.BlockInfo) + } + + if rf, ok := ret.Get(1).(func(context.Context, ton.BlockIDExt, uint32) error); ok { + r1 = rf(ctx, blockID, mode) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetFirstTransaction provides a mock function with given fields: ctx, id +func (_m *LiteClient) GetFirstTransaction(ctx context.Context, id ton.AccountID) (*ton.Transaction, int, error) { + ret := _m.Called(ctx, id) + + if len(ret) == 0 { + panic("no return value specified for GetFirstTransaction") + } + + var r0 *ton.Transaction + var r1 int + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, ton.AccountID) (*ton.Transaction, int, error)); ok { + return rf(ctx, id) + } + if rf, ok := ret.Get(0).(func(context.Context, ton.AccountID) *ton.Transaction); ok { + r0 = rf(ctx, id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*ton.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, ton.AccountID) int); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Get(1).(int) + } + + if rf, ok := ret.Get(2).(func(context.Context, ton.AccountID) error); ok { + r2 = rf(ctx, id) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// GetTransactionsSince provides a mock function with given fields: ctx, acc, lt, bits +func (_m *LiteClient) GetTransactionsSince(ctx context.Context, acc ton.AccountID, lt uint64, bits ton.Bits256) ([]ton.Transaction, error) { + ret := _m.Called(ctx, acc, lt, bits) + + if len(ret) == 0 { + panic("no return value specified for GetTransactionsSince") + } + + var r0 []ton.Transaction + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, ton.AccountID, uint64, ton.Bits256) ([]ton.Transaction, error)); ok { + return rf(ctx, acc, lt, bits) + } + if rf, ok := ret.Get(0).(func(context.Context, ton.AccountID, uint64, ton.Bits256) []ton.Transaction); ok { + r0 = rf(ctx, acc, lt, bits) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]ton.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, ton.AccountID, uint64, ton.Bits256) error); ok { + r1 = rf(ctx, acc, lt, bits) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewLiteClient creates a new instance of LiteClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewLiteClient(t interface { + mock.TestingT + Cleanup(func()) +}) *LiteClient { + mock := &LiteClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/zetaclient/testutils/testrpc/rpc.go b/zetaclient/testutils/testrpc/rpc.go index f444631813..12f368fb3f 100644 --- a/zetaclient/testutils/testrpc/rpc.go +++ b/zetaclient/testutils/testrpc/rpc.go @@ -59,7 +59,7 @@ func (s *Server) httpHandler(w http.ResponseWriter, r *http.Request) { // Decode request raw, err := io.ReadAll(r.Body) require.NoError(s.t, err) - require.NoError(s.t, json.Unmarshal(raw, &req), "unable to unmarshal request") + require.NoError(s.t, json.Unmarshal(raw, &req), "unable to unmarshal request for %s", s.name) // Process request res := s.rpcHandler(req)