From d9715b935a19b4b5d7161a3899a204f54e756249 Mon Sep 17 00:00:00 2001 From: lumtis Date: Wed, 4 Sep 2024 11:11:06 +0200 Subject: [PATCH 1/4] backport v2 migration test --- .github/workflows/e2e.yml | 78 +++---- Makefile | 9 + changelog.md | 2 +- cmd/zetae2e/config/localnet.yml | 16 ++ cmd/zetae2e/local/local.go | 69 ++---- cmd/zetae2e/local/v2.go | 62 ++++- .../localnet/orchestrator/start-zetae2e.sh | 20 ++ contrib/localnet/scripts/start-zetacored.sh | 12 + e2e/config/config.go | 43 +++- e2e/runner/accounting.go | 14 +- e2e/runner/evm.go | 14 +- e2e/runner/v2_migration.go | 212 ++++++++++++++++++ e2e/runner/v2_setup_evm.go | 7 +- e2e/runner/v2_setup_zeta.go | 7 +- e2e/txserver/authority.go | 33 +++ .../msg_server_update_gateway_contract.go | 33 ++- ...msg_server_update_gateway_contract_test.go | 129 ++++++++--- x/fungible/keeper/v2_evm.go | 27 +++ zetaclient/chains/evm/signer/signer.go | 7 +- zetaclient/orchestrator/orchestrator.go | 32 +-- 20 files changed, 643 insertions(+), 183 deletions(-) create mode 100644 e2e/runner/v2_migration.go create mode 100644 e2e/txserver/authority.go diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index f5030b1152..b024d14573 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -13,48 +13,12 @@ on: - cron: "0 6 * * *" workflow_dispatch: inputs: - default-test: - type: boolean - required: false - default: false - upgrade-light-test: - type: boolean - required: false - default: false - upgrade-test: - type: boolean - required: false - default: false - admin-test: - type: boolean - required: false - default: false - upgrade-import-mainnet-test: - type: boolean - required: false - default: false - performance-test: - type: boolean - required: false - default: false - stateful-data-test: - type: boolean - required: false - default: false - tss-migration-test: - type: boolean - required: false - default: false - solana-test: - type: boolean - required: false - default: false - v2-test: - type: boolean - required: false - default: false + make-targets: + description: 'Comma separated list of make targets to run (without the start- prefix)' + required: true + default: '' -concurrency: +concurrency: group: e2e-${{ github.head_ref || github.sha }} cancel-in-progress: true @@ -74,6 +38,7 @@ jobs: TSS_MIGRATION_TESTS: ${{ steps.matrix-conditionals.outputs.TSS_MIGRATION_TESTS }} SOLANA_TESTS: ${{ steps.matrix-conditionals.outputs.SOLANA_TESTS }} V2_TESTS: ${{ steps.matrix-conditionals.outputs.V2_TESTS }} + V2_MIGRATION_TESTS: ${{ steps.matrix-conditionals.outputs.V2_MIGRATION_TESTS }} steps: # use api rather than event context to avoid race conditions (label added after push) - id: matrix-conditionals @@ -98,6 +63,7 @@ jobs: core.setOutput('TSS_MIGRATION_TESTS', labels.includes('TSS_MIGRATION_TESTS')); core.setOutput('SOLANA_TESTS', labels.includes('SOLANA_TESTS')); core.setOutput('V2_TESTS', labels.includes('V2_TESTS')); // 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', labels.includes('V2_MIGRATION_TESTS')); // for v2 tests, TODO: remove this once we fully migrate to v2 (https://github.com/zeta-chain/node/issues/2627) } else if (context.eventName === 'merge_group') { core.setOutput('DEFAULT_TESTS', true); core.setOutput('UPGRADE_LIGHT_TESTS', true); @@ -112,6 +78,7 @@ jobs: core.setOutput('PERFORMANCE_TESTS', true); core.setOutput('STATEFUL_DATA_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); @@ -123,17 +90,20 @@ jobs: core.setOutput('TSS_MIGRATION_TESTS', true); core.setOutput('SOLANA_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') { - core.setOutput('DEFAULT_TESTS', context.payload.inputs['default-test']); - core.setOutput('UPGRADE_TESTS', context.payload.inputs['upgrade-test']); - core.setOutput('UPGRADE_LIGHT_TESTS', context.payload.inputs['upgrade-light-test']); - core.setOutput('UPGRADE_IMPORT_MAINNET_TESTS', context.payload.inputs['upgrade-import-mainnet-test']); - core.setOutput('ADMIN_TESTS', context.payload.inputs['admin-test']); - core.setOutput('PERFORMANCE_TESTS', context.payload.inputs['performance-test']); - core.setOutput('STATEFUL_DATA_TESTS', context.payload.inputs['stateful-data-test']); - core.setOutput('TSS_MIGRATION_TESTS', context.payload.inputs['tss-migration-test']); - core.setOutput('SOLANA_TESTS', context.payload.inputs['solana-test']); - core.setOutput('V2_TESTS', context.payload.inputs['v2-test']); // for v2 tests, TODO: remove this once we fully migrate to v2 (https://github.com/zeta-chain/node/issues/2627) + const makeTargets = context.payload.inputs['make-targets'].split(','); + core.setOutput('DEFAULT_TESTS', makeTargets.includes('default-test')); + core.setOutput('UPGRADE_TESTS', makeTargets.includes('upgrade-test')); + core.setOutput('UPGRADE_LIGHT_TESTS', makeTargets.includes('upgrade-test-light')); + core.setOutput('UPGRADE_IMPORT_MAINNET_TESTS', makeTargets.includes('upgrade-import-mainnet-test')); + core.setOutput('ADMIN_TESTS', makeTargets.includes('admin-test')); + core.setOutput('PERFORMANCE_TESTS', makeTargets.includes('performance-test')); + core.setOutput('STATEFUL_DATA_TESTS', makeTargets.includes('import-mainnet-test')); + core.setOutput('TSS_MIGRATION_TESTS', makeTargets.includes('tss-migration-test')); + core.setOutput('SOLANA_TESTS', makeTargets.includes('solana-test')); + core.setOutput('V2_TESTS', makeTargets.includes('v2-test')); // 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', makeTargets.includes('v2-migration-test')); // for v2 tests, TODO: remove this once we fully migrate to v2 (https://github.com/zeta-chain/node/issues/2627) } e2e: @@ -168,12 +138,16 @@ jobs: - make-target: "start-tss-migration-test" runs-on: ubuntu-20.04 run: ${{ needs.matrix-conditionals.outputs.TSS_MIGRATION_TESTS == 'true' }} + timeout-minutes: 40 - make-target: "start-solana-test" runs-on: ubuntu-20.04 run: ${{ needs.matrix-conditionals.outputs.SOLANA_TESTS == 'true' }} - make-target: "start-v2-test" runs-on: ubuntu-20.04 run: ${{ needs.matrix-conditionals.outputs.V2_TESTS == 'true' }} + - make-target: "start-upgrade-v2-migration-test" + runs-on: ubuntu-20.04 + run: ${{ needs.matrix-conditionals.outputs.V2_MIGRATION_TESTS == 'true' }} name: ${{ matrix.make-target }} uses: ./.github/workflows/reusable-e2e.yml with: @@ -239,4 +213,4 @@ jobs: exit 0 else exit 1 - fi + fi \ No newline at end of file diff --git a/Makefile b/Makefile index 51931ea89b..94fdd9917a 100644 --- a/Makefile +++ b/Makefile @@ -246,6 +246,15 @@ start-e2e-admin-test: zetanode export E2E_ARGS="--skip-regular --test-admin" && \ cd contrib/localnet/ && $(DOCKER_COMPOSE) --profile eth2 -f docker-compose.yml up -d +# this test upgrades from v18 and execute the v2 contracts migration process +# this tests is part of upgrade test part because it should run the upgrade from v18 to fully replicate the upgrade process +start-upgrade-v2-migration-test: zetanode-upgrade + @echo "--> Starting v2 migration upgrade test" + export LOCALNET_MODE=upgrade && \ + export UPGRADE_HEIGHT=90 && \ + export E2E_ARGS="--test-v2-migration" && \ + cd contrib/localnet/ && $(DOCKER_COMPOSE) --profile upgrade -f docker-compose.yml -f docker-compose-upgrade.yml up -d + start-e2e-performance-test: zetanode @echo "--> Starting e2e performance test" export E2E_ARGS="--test-performance" && \ diff --git a/changelog.md b/changelog.md index d00252a146..a81fc297db 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # CHANGELOG -## Unreleased +## v20.0.0 ### Features diff --git a/cmd/zetae2e/config/localnet.yml b/cmd/zetae2e/config/localnet.yml index 98410bc368..6b1b9f20da 100644 --- a/cmd/zetae2e/config/localnet.yml +++ b/cmd/zetae2e/config/localnet.yml @@ -41,6 +41,22 @@ additional_accounts: bech32_address: "zeta1pvtxa708yvdmszn687nne6nl8qn704daf420xz" evm_address: "0x0B166ef9e7231Bb80A7A3FA73CEA7F3827E7D5BD" private_key: "0bcc2fa28b526f90e1d54648d612db901e860bf68248555593f91ea801c6b482" + user_v2_ether: + bech32_address: "zeta1erlqlpl5da7a9r3emzw60kax9fxc3h0r3z7c5e" + evm_address: "0xC8fe0F87f46F7Dd28e39D89Da7Dba62A4D88dde3" + private_key: "11c25af71c82602a681ce622bf76f4f0fbc3b7f23ce935db6249d1517322f436" + user_v2_erc20: + bech32_address: "zeta12wp6syndml6jd32m7f9mn2wscsxz6cff8nczl4" + evm_address: "0x5383A8126ddff526C55bF24Bb9a9D0c40c2d6129" + private_key: "77b0e4dcc29c5c47b6999dabd42abcfdf7750ccc86d6659c1373ec1ea3b4af6c" + user_v2_ether_revert: + bech32_address: "zeta1m7m5xd79x9qmlyfpqxcwuac04r3dewfpdcfw5e" + evm_address: "0xdFb74337c53141bf912101b0Ee770FA8e2DCB921" + private_key: "be7098604cc40f95d68298a3b4ae13972ac8a3df271ba19ddf169070d30e8ba8" + user_v2_erc20_revert: + bech32_address: "zeta1nry9yeg6njhjrp2ctppa8558vqxal9fxk69zxg" + evm_address: "0x98c852651A9CAF2185585843d3D287600Ddf9526" + private_key: "bf9456c679bb5a952a9a137fcfc920e0413efdb97c36de1e57455763084230cb" policy_accounts: emergency_policy_account: bech32_address: "zeta16m2cnrdwtgweq4njc6t470vl325gw4kp6s7tap" diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index 3d923b32a8..59f1499531 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -42,6 +42,7 @@ const ( flagSkipBitcoinSetup = "skip-bitcoin-setup" flagSkipHeaderProof = "skip-header-proof" flagTestV2 = "test-v2" + flagTestV2Migration = "test-v2-migration" flagSkipTrackerCheck = "skip-tracker-check" ) @@ -76,6 +77,7 @@ func NewLocalCmd() *cobra.Command { cmd.Flags().Bool(flagSkipHeaderProof, false, "set to true to skip header proof tests") cmd.Flags().Bool(flagTestTSSMigration, false, "set to true to include a migration test at the end") cmd.Flags().Bool(flagTestV2, false, "set to true to run tests for v2 contracts") + 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") return cmd @@ -101,6 +103,7 @@ func localE2ETest(cmd *cobra.Command, _ []string) { skipTrackerCheck = must(cmd.Flags().GetBool(flagSkipTrackerCheck)) testTSSMigration = must(cmd.Flags().GetBool(flagTestTSSMigration)) testV2 = must(cmd.Flags().GetBool(flagTestV2)) + testV2Migration = must(cmd.Flags().GetBool(flagTestV2Migration)) ) logger := runner.NewLogger(verbose, color.FgWhite, "setup") @@ -233,6 +236,11 @@ func localE2ETest(cmd *cobra.Command, _ []string) { os.Exit(0) } + // run the v2 migration + if testV2Migration { + deployerRunner.RunV2Migration() + } + // run tests var eg errgroup.Group @@ -355,63 +363,12 @@ func localE2ETest(cmd *cobra.Command, _ []string) { } if testV2 { // update the ERC20 custody contract for v2 tests - deployerRunner.UpdateChainParamsERC20CustodyContract() - - //// Test happy paths for gas token workflow - eg.Go(v2TestRoutine(conf, "eth", conf.AdditionalAccounts.UserEther, color.FgHiGreen, deployerRunner, verbose, - e2etests.TestV2ETHDepositName, - e2etests.TestV2ETHDepositAndCallName, - e2etests.TestV2ETHWithdrawName, - e2etests.TestV2ETHWithdrawAndCallName, - e2etests.TestV2ZEVMToEVMCallName, - e2etests.TestV2EVMToZEVMCallName, - )) - - //// Test happy paths for erc20 token workflow - eg.Go(v2TestRoutine(conf, "erc20", conf.AdditionalAccounts.UserERC20, color.FgHiBlue, deployerRunner, verbose, - e2etests.TestV2ETHDepositName, // necessary to pay fees on ZEVM - e2etests.TestV2ERC20DepositName, - e2etests.TestV2ERC20DepositAndCallName, - e2etests.TestV2ERC20WithdrawName, - e2etests.TestV2ERC20WithdrawAndCallName, - )) + // note: not run in testV2Migration because it is already run in the migration process + deployerRunner.UpdateChainParamsV2Contracts() + } - // Test revert cases for gas token workflow - eg.Go( - v2TestRoutine( - conf, - "eth-revert", - conf.AdditionalAccounts.UserZetaTest, - color.FgHiYellow, - deployerRunner, - verbose, - e2etests.TestV2ETHDepositName, // necessary to pay fees on ZEVM and withdraw - e2etests.TestV2ETHDepositAndCallRevertName, - e2etests.TestV2ETHDepositAndCallRevertWithCallName, - e2etests.TestV2ETHWithdrawAndCallRevertName, - e2etests.TestV2ETHWithdrawAndCallRevertWithCallName, - ), - ) - - // Test revert cases for erc20 token workflow - eg.Go( - v2TestRoutine( - conf, - "erc20-revert", - conf.AdditionalAccounts.UserBitcoin, - color.FgHiRed, - deployerRunner, - verbose, - e2etests.TestV2ETHDepositName, // necessary to pay fees on ZEVM - e2etests.TestV2ERC20DepositName, // necessary to have assets to withdraw - e2etests.TestOperationAddLiquidityETHName, // liquidity with gas and ERC20 are necessary for reverts - e2etests.TestOperationAddLiquidityERC20Name, - e2etests.TestV2ERC20DepositAndCallRevertName, - e2etests.TestV2ERC20DepositAndCallRevertWithCallName, - e2etests.TestV2ERC20WithdrawAndCallRevertName, - e2etests.TestV2ERC20WithdrawAndCallRevertWithCallName, - ), - ) + if testV2 || testV2Migration { + startV2Tests(&eg, conf, deployerRunner, verbose) } // while tests are executed, monitor blocks in parallel to check if system txs are on top and they have biggest priority diff --git a/cmd/zetae2e/local/v2.go b/cmd/zetae2e/local/v2.go index e87785de31..b38be32868 100644 --- a/cmd/zetae2e/local/v2.go +++ b/cmd/zetae2e/local/v2.go @@ -5,13 +5,73 @@ import ( "time" "github.com/fatih/color" + "golang.org/x/sync/errgroup" "github.com/zeta-chain/zetacore/e2e/config" "github.com/zeta-chain/zetacore/e2e/e2etests" "github.com/zeta-chain/zetacore/e2e/runner" ) -// erc20TestRoutine runs v2 related e2e tests +// startV2Tests starts v2 related tests in parallel +func startV2Tests(eg *errgroup.Group, conf config.Config, deployerRunner *runner.E2ERunner, verbose bool) { + // Test happy paths for gas token workflow + eg.Go(v2TestRoutine(conf, "eth", conf.AdditionalAccounts.UserV2Ether, color.FgHiGreen, deployerRunner, verbose, + e2etests.TestV2ETHDepositName, + e2etests.TestV2ETHDepositAndCallName, + e2etests.TestV2ETHWithdrawName, + e2etests.TestV2ETHWithdrawAndCallName, + e2etests.TestV2ZEVMToEVMCallName, + e2etests.TestV2EVMToZEVMCallName, + )) + + // Test happy paths for erc20 token workflow + eg.Go(v2TestRoutine(conf, "erc20", conf.AdditionalAccounts.UserV2ERC20, color.FgHiBlue, deployerRunner, verbose, + e2etests.TestV2ETHDepositName, // necessary to pay fees on ZEVM + e2etests.TestV2ERC20DepositName, + e2etests.TestV2ERC20DepositAndCallName, + e2etests.TestV2ERC20WithdrawName, + e2etests.TestV2ERC20WithdrawAndCallName, + )) + + // Test revert cases for gas token workflow + eg.Go( + v2TestRoutine( + conf, + "eth-revert", + conf.AdditionalAccounts.UserV2EtherRevert, + color.FgHiYellow, + deployerRunner, + verbose, + e2etests.TestV2ETHDepositName, // necessary to pay fees on ZEVM and withdraw + e2etests.TestV2ETHDepositAndCallRevertName, + e2etests.TestV2ETHDepositAndCallRevertWithCallName, + e2etests.TestV2ETHWithdrawAndCallRevertName, + e2etests.TestV2ETHWithdrawAndCallRevertWithCallName, + ), + ) + + // Test revert cases for erc20 token workflow + eg.Go( + v2TestRoutine( + conf, + "erc20-revert", + conf.AdditionalAccounts.UserV2ERC20Revert, + color.FgHiRed, + deployerRunner, + verbose, + e2etests.TestV2ETHDepositName, // necessary to pay fees on ZEVM + e2etests.TestV2ERC20DepositName, // necessary to have assets to withdraw + e2etests.TestOperationAddLiquidityETHName, // liquidity with gas and ERC20 are necessary for reverts + e2etests.TestOperationAddLiquidityERC20Name, + e2etests.TestV2ERC20DepositAndCallRevertName, + e2etests.TestV2ERC20DepositAndCallRevertWithCallName, + e2etests.TestV2ERC20WithdrawAndCallRevertName, + e2etests.TestV2ERC20WithdrawAndCallRevertWithCallName, + ), + ) +} + +// v2TestRoutine runs v2 related e2e tests // TODO: this routine will be broken down in the future and will replace most current tests // we keep a single routine for v2 for simplicity // https://github.com/zeta-chain/node/issues/2554 diff --git a/contrib/localnet/orchestrator/start-zetae2e.sh b/contrib/localnet/orchestrator/start-zetae2e.sh index 4ee8192c6d..d4a8fd75ad 100644 --- a/contrib/localnet/orchestrator/start-zetae2e.sh +++ b/contrib/localnet/orchestrator/start-zetae2e.sh @@ -91,6 +91,26 @@ address=$(yq -r '.additional_accounts.user_migration.evm_address' config.yml) echo "funding migration tester address ${address} with 10000 Ether" geth --exec "eth.sendTransaction({from: eth.coinbase, to: '${address}', value: web3.toWei(10000,'ether')})" attach http://eth:8545 > /dev/null +# unlock v2 ethers tests accounts +address=$(yq -r '.additional_accounts.user_v2_ether.evm_address' config.yml) +echo "funding v2 ethers tester address ${address} with 10000 Ether" +geth --exec "eth.sendTransaction({from: eth.coinbase, to: '${address}', value: web3.toWei(10000,'ether')})" attach http://eth:8545 > /dev/null + +# unlock v2 erc20 tests accounts +address=$(yq -r '.additional_accounts.user_v2_erc20.evm_address' config.yml) +echo "funding v2 erc20 tester address ${address} with 10000 Ether" +geth --exec "eth.sendTransaction({from: eth.coinbase, to: '${address}', value: web3.toWei(10000,'ether')})" attach http://eth:8545 > /dev/null + +# unlock v2 ethers revert tests accounts +address=$(yq -r '.additional_accounts.user_v2_ether_revert.evm_address' config.yml) +echo "funding v2 ethers revert tester address ${address} with 10000 Ether" +geth --exec "eth.sendTransaction({from: eth.coinbase, to: '${address}', value: web3.toWei(10000,'ether')})" attach http://eth:8545 > /dev/null + +# unlock v2 erc20 revert tests accounts +address=$(yq -r '.additional_accounts.user_v2_erc20_revert.evm_address' config.yml) +echo "funding v2 erc20 revert tester address ${address} with 10000 Ether" +geth --exec "eth.sendTransaction({from: eth.coinbase, to: '${address}', value: web3.toWei(10000,'ether')})" attach http://eth:8545 > /dev/null + # unlock local solana relayer accounts if host solana > /dev/null; then solana_url=$(yq -r '.rpcs.solana' config.yml) diff --git a/contrib/localnet/scripts/start-zetacored.sh b/contrib/localnet/scripts/start-zetacored.sh index 14980d195f..f1d3e11872 100755 --- a/contrib/localnet/scripts/start-zetacored.sh +++ b/contrib/localnet/scripts/start-zetacored.sh @@ -254,6 +254,18 @@ then # migration tester address=$(yq -r '.additional_accounts.user_migration.bech32_address' /root/config.yml) zetacored add-genesis-account "$address" 100000000000000000000000000azeta +# v2 ether tester + address=$(yq -r '.additional_accounts.user_v2_ether.bech32_address' /root/config.yml) + zetacored add-genesis-account "$address" 100000000000000000000000000azeta +# v2 erc20 tester + address=$(yq -r '.additional_accounts.user_v2_erc20.bech32_address' /root/config.yml) + zetacored add-genesis-account "$address" 100000000000000000000000000azeta +# v2 ether revert tester + address=$(yq -r '.additional_accounts.user_v2_ether_revert.bech32_address' /root/config.yml) + zetacored add-genesis-account "$address" 100000000000000000000000000azeta +# v2 erc20 revert tester + address=$(yq -r '.additional_accounts.user_v2_erc20_revert.bech32_address' /root/config.yml) + zetacored add-genesis-account "$address" 100000000000000000000000000azeta # 3. Copy the genesis.json to all the nodes .And use it to create a gentx for every node zetacored gentx operator 1000000000000000000000azeta --chain-id=$CHAINID --keyring-backend=$KEYRING --gas-prices 20000000000azeta diff --git a/e2e/config/config.go b/e2e/config/config.go index 7e721bcdd3..4995bcd7c7 100644 --- a/e2e/config/config.go +++ b/e2e/config/config.go @@ -61,15 +61,20 @@ type Account struct { // AdditionalAccounts are extra accounts required to run specific tests type AdditionalAccounts struct { - UserERC20 Account `yaml:"user_erc20"` - UserZetaTest Account `yaml:"user_zeta_test"` - UserZEVMMPTest Account `yaml:"user_zevm_mp_test"` - UserBitcoin Account `yaml:"user_bitcoin"` - UserSolana Account `yaml:"user_solana"` - UserEther Account `yaml:"user_ether"` - UserMisc Account `yaml:"user_misc"` - UserAdmin Account `yaml:"user_admin"` - UserMigration Account `yaml:"user_migration"` + UserERC20 Account `yaml:"user_erc20"` + UserZetaTest Account `yaml:"user_zeta_test"` + UserZEVMMPTest Account `yaml:"user_zevm_mp_test"` + UserBitcoin Account `yaml:"user_bitcoin"` + UserSolana Account `yaml:"user_solana"` + UserEther Account `yaml:"user_ether"` + UserMisc Account `yaml:"user_misc"` + UserAdmin Account `yaml:"user_admin"` + UserMigration Account `yaml:"user_migration"` + UserPrecompile Account `yaml:"user_precompile"` + UserV2Ether Account `yaml:"user_v2_ether"` + UserV2ERC20 Account `yaml:"user_v2_erc20"` + UserV2EtherRevert Account `yaml:"user_v2_ether_revert"` + UserV2ERC20Revert Account `yaml:"user_v2_erc20_revert"` } type PolicyAccounts struct { @@ -225,6 +230,10 @@ func (a AdditionalAccounts) AsSlice() []Account { a.UserMisc, a.UserAdmin, a.UserMigration, + a.UserV2Ether, + a.UserV2ERC20, + a.UserV2EtherRevert, + a.UserV2ERC20Revert, } } @@ -317,6 +326,22 @@ func (c *Config) GenerateKeys() error { if err != nil { return err } + c.AdditionalAccounts.UserV2Ether, err = generateAccount() + if err != nil { + return err + } + c.AdditionalAccounts.UserV2ERC20, err = generateAccount() + if err != nil { + return err + } + c.AdditionalAccounts.UserV2EtherRevert, err = generateAccount() + if err != nil { + return err + } + c.AdditionalAccounts.UserV2ERC20Revert, err = generateAccount() + if err != nil { + return err + } c.PolicyAccounts.EmergencyPolicyAccount, err = generateAccount() if err != nil { diff --git a/e2e/runner/accounting.go b/e2e/runner/accounting.go index 7f357a69ab..2da7c2b46d 100644 --- a/e2e/runner/accounting.go +++ b/e2e/runner/accounting.go @@ -170,11 +170,17 @@ func (r *E2ERunner) checkERC20TSSBalance() error { if err != nil { return err } - custodyV2Balance, err := r.ERC20.BalanceOf(&bind.CallOpts{}, r.ERC20CustodyV2Addr) - if err != nil { - return err + custodyFullBalance := custodyBalance + + // take into account the balance of the new ERC20 custody contract as v2 test use this contract + // if both addresses are equal, then there is no need to check the balance of the new contract + if r.ERC20CustodyAddr.Hex() != r.ERC20CustodyV2Addr.Hex() { + custodyV2Balance, err := r.ERC20.BalanceOf(&bind.CallOpts{}, r.ERC20CustodyV2Addr) + if err != nil { + return err + } + custodyFullBalance = big.NewInt(0).Add(custodyBalance, custodyV2Balance) } - custodyFullBalance := big.NewInt(0).Add(custodyBalance, custodyV2Balance) erc20zrc20Supply, err := r.ERC20ZRC20.TotalSupply(&bind.CallOpts{}) if err != nil { diff --git a/e2e/runner/evm.go b/e2e/runner/evm.go index 3b5e7f8d90..9bb1bb9082 100644 --- a/e2e/runner/evm.go +++ b/e2e/runner/evm.go @@ -185,11 +185,12 @@ func (r *E2ERunner) ApproveERC20OnEVM(allowed ethcommon.Address) { // check if allowance is zero before calling this method // allow a high amount to avoid multiple approvals func (r *E2ERunner) ApproveETHZRC20(allowed ethcommon.Address) { - allowance, err := r.ETHZRC20.Allowance(&bind.CallOpts{}, r.Account.EVMAddress(), r.GatewayEVMAddr) + allowance, err := r.ETHZRC20.Allowance(&bind.CallOpts{}, r.Account.EVMAddress(), allowed) require.NoError(r, err) - // approve 1M*1e18 if allowance is zero - if allowance.Cmp(big.NewInt(0)) == 0 { + // approve 1M*1e18 if allowance is below 1k + thousand := big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(1000)) + if allowance.Cmp(thousand) < 0 { tx, err := r.ETHZRC20.Approve(r.ZEVMAuth, allowed, big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(1000000))) require.NoError(r, err) receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) @@ -201,11 +202,12 @@ func (r *E2ERunner) ApproveETHZRC20(allowed ethcommon.Address) { // check if allowance is zero before calling this method // allow a high amount to avoid multiple approvals func (r *E2ERunner) ApproveERC20ZRC20(allowed ethcommon.Address) { - allowance, err := r.ERC20ZRC20.Allowance(&bind.CallOpts{}, r.Account.EVMAddress(), r.GatewayEVMAddr) + allowance, err := r.ERC20ZRC20.Allowance(&bind.CallOpts{}, r.Account.EVMAddress(), allowed) require.NoError(r, err) - // approve 1M*1e18 if allowance is zero - if allowance.Cmp(big.NewInt(0)) == 0 { + // approve 1M*1e18 if allowance is below 1k + thousand := big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(1000)) + if allowance.Cmp(thousand) < 0 { tx, err := r.ERC20ZRC20.Approve(r.ZEVMAuth, allowed, big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(1000000))) require.NoError(r, err) receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) diff --git a/e2e/runner/v2_migration.go b/e2e/runner/v2_migration.go new file mode 100644 index 0000000000..abfb114409 --- /dev/null +++ b/e2e/runner/v2_migration.go @@ -0,0 +1,212 @@ +package runner + +import ( + "math/big" + + sdkmath "cosmossdk.io/math" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" + + "github.com/zeta-chain/zetacore/e2e/txserver" + "github.com/zeta-chain/zetacore/e2e/utils" + "github.com/zeta-chain/zetacore/pkg/coin" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" + fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" +) + +// RunV2Migration runs the process for the v2 migration +func (r *E2ERunner) RunV2Migration() { + // prepare for v2 migration: deposit erc20 to ensure that the custody contract has funds to migrate + oneThousand := big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(1000)) + erc20Deposit := r.DepositERC20WithAmountAndMessage( + r.EVMAddress(), + oneThousand, + []byte{}, + ) + r.WaitForMinedCCTX(erc20Deposit) + + // Part 1: add new admin authorization + r.Logger.Info("Part 1: Adding authorization for new v2 contracts") + err := r.ZetaTxServer.AddAuthorization("/zetachain.zetacore.crosschain.MsgUpdateERC20CustodyPauseStatus") + require.NoError(r, err) + + err = r.ZetaTxServer.AddAuthorization("/zetachain.zetacore.crosschain.MsgMigrateERC20CustodyFunds") + require.NoError(r, err) + + err = r.ZetaTxServer.AddAuthorization("/zetachain.zetacore.fungible.MsgUpdateGatewayContract") + require.NoError(r, err) + + // Part 2: deploy v2 contracts on EVM chain + r.Logger.Info("Part 2: Deploying v2 contracts on EVM chain") + r.SetupEVMV2() + + // Part 3: upgrade all ZRC20s + r.Logger.Info("Part 3: Upgrading ZRC20s") + r.upgradeZRC20s() + + // Part 4: deploy gateway on ZetaChain + r.Logger.Info("Part 4: Deploying Gateway ZEVM") + r.SetZEVMContractsV2() + + // Part 5: migrate ERC20 custody funds + r.Logger.Info("Part 5: Migrating ERC20 custody funds") + r.migrateERC20CustodyFunds() +} + +// upgradeZRC20s upgrades all ZRC20s to the new version +func (r *E2ERunner) upgradeZRC20s() { + // get chain IDs + evmChainID, err := r.EVMClient.ChainID(r.Ctx) + require.NoError(r, err) + btcChainID := r.GetBitcoinChainID() + + // upgrade ETH ZRC20 + r.Logger.Info("Upgrading ETH ZRC20") + r.upgradeZRC20(r.ETHZRC20Addr, r.ETHZRC20, evmChainID, uint8(coin.CoinType_Gas)) + + // upgrade ERC20 ZRC20 + r.Logger.Info("Upgrading ERC20 ZRC20") + r.upgradeZRC20(r.ERC20ZRC20Addr, r.ERC20ZRC20, evmChainID, uint8(coin.CoinType_ERC20)) + + // upgrade BTC ZRC20 + r.Logger.Info("Upgrading BTC ZRC20") + r.upgradeZRC20(r.BTCZRC20Addr, r.BTCZRC20, big.NewInt(btcChainID), uint8(coin.CoinType_Gas)) +} + +// zrc20Caller is an interface to call ZRC20 functions +type zrc20Caller interface { + Name(opts *bind.CallOpts) (string, error) + Symbol(opts *bind.CallOpts) (string, error) + Decimals(opts *bind.CallOpts) (uint8, error) +} + +// upgradeZRC20 upgrades a ZRC20 to the new version +func (r *E2ERunner) upgradeZRC20( + zrc20Addr common.Address, + zrc20Caller zrc20Caller, + chainID *big.Int, + coinType uint8, +) { + // deploy new ZRC20 version + name, err := zrc20Caller.Name(&bind.CallOpts{}) + require.NoError(r, err) + symbol, err := zrc20Caller.Symbol(&bind.CallOpts{}) + require.NoError(r, err) + decimal, err := zrc20Caller.Decimals(&bind.CallOpts{}) + require.NoError(r, err) + + newZRC20Addr, newZRC20Tx, _, err := zrc20.DeployZRC20( + r.ZEVMAuth, + r.ZEVMClient, + name, + symbol, + decimal, + chainID, + coinType, + big.NewInt(100_000), + r.SystemContractAddr, + r.SystemContractAddr, // gateway is not deployed yet, gateway will be set during MsgUpdateGatewayContract phase by the protocol + ) + require.NoError(r, err) + + // wait tx to be mined + receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, newZRC20Tx, r.Logger, r.ReceiptTimeout) + require.EqualValues(r, ethtypes.ReceiptStatusSuccessful, receipt.Status) + + // upgrade ZRC20 bytecode with the one of the new ZRC20 + codeHashRes, err := r.FungibleClient.CodeHash(r.Ctx, &fungibletypes.QueryCodeHashRequest{ + Address: newZRC20Addr.String(), + }) + require.NoError(r, err) + + msg := fungibletypes.NewMsgUpdateContractBytecode( + r.ZetaTxServer.MustGetAccountAddressFromName(utils.AdminPolicyName), + zrc20Addr.Hex(), + codeHashRes.CodeHash, + ) + _, err = r.ZetaTxServer.BroadcastTx(utils.AdminPolicyName, msg) + require.NoError(r, err) +} + +func (r *E2ERunner) migrateERC20CustodyFunds() { + evmChainID, err := r.EVMClient.ChainID(r.Ctx) + require.NoError(r, err) + + // Part 1: pause the ERC20Custody v1 + r.Logger.Info("Pausing ERC20 custody v1 contract") + msgPausing := crosschaintypes.NewMsgUpdateERC20CustodyPauseStatus( + r.ZetaTxServer.MustGetAccountAddressFromName(utils.AdminPolicyName), + evmChainID.Int64(), + true, + ) + res, err := r.ZetaTxServer.BroadcastTx(utils.AdminPolicyName, msgPausing) + require.NoError(r, err) + + // fetch cctx index from tx response + cctxIndex, err := txserver.FetchAttributeFromTxResponse(res, "cctx_index") + require.NoError(r, err) + + cctxRes, err := r.CctxClient.Cctx(r.Ctx, &crosschaintypes.QueryGetCctxRequest{Index: cctxIndex}) + require.NoError(r, err) + + cctx := cctxRes.CrossChainTx + r.Logger.CCTX(*cctx, "pausing") + + // wait for the cctx to be mined + r.WaitForMinedCCTXFromIndex(cctxIndex) + + // Part 2: pause the ZRC20 ERC20 + msgPause := fungibletypes.NewMsgPauseZRC20( + r.ZetaTxServer.MustGetAccountAddressFromName(utils.EmergencyPolicyName), + []string{r.ERC20ZRC20Addr.Hex()}, + ) + _, err = r.ZetaTxServer.BroadcastTx(utils.EmergencyPolicyName, msgPause) + require.NoError(r, err) + + // Part 3: migrate all funds of the ERC20 + balance, err := r.ERC20.BalanceOf(&bind.CallOpts{}, r.ERC20CustodyAddr) + require.NoError(r, err) + + // ensure balance is not zero to ensure the test tests actual migration + require.NotEqual(r, int64(0), balance.Int64()) + + // send MigrateERC20CustodyFunds command + msgMigration := crosschaintypes.NewMsgMigrateERC20CustodyFunds( + r.ZetaTxServer.MustGetAccountAddressFromName(utils.AdminPolicyName), + evmChainID.Int64(), + r.ERC20CustodyV2Addr.Hex(), + r.ERC20Addr.Hex(), + sdkmath.NewUintFromBigInt(balance), + ) + res, err = r.ZetaTxServer.BroadcastTx(utils.AdminPolicyName, msgMigration) + require.NoError(r, err) + + // fetch cctx index from tx response + cctxIndex, err = txserver.FetchAttributeFromTxResponse(res, "cctx_index") + require.NoError(r, err) + + cctxRes, err = r.CctxClient.Cctx(r.Ctx, &crosschaintypes.QueryGetCctxRequest{Index: cctxIndex}) + require.NoError(r, err) + + cctx = cctxRes.CrossChainTx + r.Logger.CCTX(*cctx, "migration") + + // wait for the cctx to be mined + r.WaitForMinedCCTXFromIndex(cctxIndex) + + // Part 4: unpause the ZRC20 + msgUnpause := fungibletypes.NewMsgUnpauseZRC20( + r.ZetaTxServer.MustGetAccountAddressFromName(utils.OperationalPolicyName), + []string{r.ERC20ZRC20Addr.Hex()}, + ) + _, err = r.ZetaTxServer.BroadcastTx(utils.OperationalPolicyName, msgUnpause) + require.NoError(r, err) + + // Part 5: update the ERC20 custody contract in the chain params and in the runner + r.UpdateChainParamsV2Contracts() + + r.ERC20CustodyAddr = r.ERC20CustodyV2Addr +} diff --git a/e2e/runner/v2_setup_evm.go b/e2e/runner/v2_setup_evm.go index 001da12e04..cbacc59550 100644 --- a/e2e/runner/v2_setup_evm.go +++ b/e2e/runner/v2_setup_evm.go @@ -22,7 +22,7 @@ func (r *E2ERunner) SetupEVMV2() { r.requireTxSuccessful(receipt, failMessage) } - r.Logger.Print("⚙️ setting up EVM v2 network") + r.Logger.Info("⚙️ setting up EVM v2 network") startTime := time.Now() defer func() { r.Logger.Info("EVM v2 setup took %s\n", time.Since(startTime)) @@ -104,5 +104,10 @@ func (r *E2ERunner) SetupEVMV2() { txWhitelist, err := r.ERC20CustodyV2.Whitelist(r.EVMAuth, r.ERC20Addr) require.NoError(r, err) + // set legacy supported (calling deposit directly in ERC20Custody) + txSetLegacySupported, err := r.ERC20CustodyV2.SetSupportsLegacy(r.EVMAuth, true) + require.NoError(r, err) + ensureTxReceipt(txWhitelist, "ERC20 whitelist failed") + ensureTxReceipt(txSetLegacySupported, "Set legacy support failed") } diff --git a/e2e/runner/v2_setup_zeta.go b/e2e/runner/v2_setup_zeta.go index c82e8fa741..bac11aaae2 100644 --- a/e2e/runner/v2_setup_zeta.go +++ b/e2e/runner/v2_setup_zeta.go @@ -75,9 +75,9 @@ func (r *E2ERunner) SetZEVMContractsV2() { ensureTxReceipt(txTestDAppV2, "TestDAppV2 deployment failed") } -// UpdateChainParamsERC20CustodyContract update the erc20 custody contract in the chain params +// UpdateChainParamsV2Contracts update the erc20 custody contract and gateway address in the chain params // this operation is used when transitioning to new smart contract architecture where a new ERC20 custody contract is deployed -func (r *E2ERunner) UpdateChainParamsERC20CustodyContract() { +func (r *E2ERunner) UpdateChainParamsV2Contracts() { res, err := r.ObserverClient.GetChainParams(r.Ctx, &observertypes.QueryGetChainParamsRequest{}) require.NoError(r, err) @@ -101,6 +101,9 @@ func (r *E2ERunner) UpdateChainParamsERC20CustodyContract() { // update with the new ERC20 custody contract address chainParams.Erc20CustodyContractAddress = r.ERC20CustodyV2Addr.Hex() + // update with the new gateway address + chainParams.GatewayAddress = r.GatewayEVMAddr.Hex() + // update the chain params _, err = r.ZetaTxServer.BroadcastTx(utils.OperationalPolicyName, observertypes.NewMsgUpdateChainParams( r.ZetaTxServer.MustGetAccountAddressFromName(utils.OperationalPolicyName), diff --git a/e2e/txserver/authority.go b/e2e/txserver/authority.go new file mode 100644 index 0000000000..a6c4dfb9dd --- /dev/null +++ b/e2e/txserver/authority.go @@ -0,0 +1,33 @@ +package txserver + +import ( + "fmt" + + e2eutils "github.com/zeta-chain/zetacore/e2e/utils" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" +) + +// AddAuthorization adds a new authorization in the authority module for admin message +func (zts ZetaTxServer) AddAuthorization(msgURL string) error { + // retrieve account + accAdmin, err := zts.clientCtx.Keyring.Key(e2eutils.AdminPolicyName) + if err != nil { + return err + } + addrAdmin, err := accAdmin.GetAddress() + if err != nil { + return err + } + + // add new authorization + _, err = zts.BroadcastTx(e2eutils.AdminPolicyName, authoritytypes.NewMsgAddAuthorization( + addrAdmin.String(), + msgURL, + authoritytypes.PolicyType_groupAdmin, + )) + if err != nil { + return fmt.Errorf("failed to add authorization: %w", err) + } + + return nil +} diff --git a/x/fungible/keeper/msg_server_update_gateway_contract.go b/x/fungible/keeper/msg_server_update_gateway_contract.go index 1fd3b95021..499ab23b13 100644 --- a/x/fungible/keeper/msg_server_update_gateway_contract.go +++ b/x/fungible/keeper/msg_server_update_gateway_contract.go @@ -5,6 +5,8 @@ import ( cosmoserrors "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + ethcommon "github.com/ethereum/go-ethereum/common" authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/fungible/types" @@ -21,6 +23,16 @@ func (k msgServer) UpdateGatewayContract( return nil, cosmoserrors.Wrap(authoritytypes.ErrUnauthorized, err.Error()) } + // parse the new gateway address + gatewayAddr := ethcommon.HexToAddress(msg.NewGatewayContractAddress) + if gatewayAddr == (ethcommon.Address{}) { + return nil, cosmoserrors.Wrapf( + sdkerrors.ErrInvalidAddress, + "invalid gateway contract address (%s)", + msg.NewGatewayContractAddress, + ) + } + // The SystemContract state variable tracks the contract addresses used by the protocol // This variable is planned to be renamed ProtocolContracts in the future: // https://github.com/zeta-chain/node/issues/2576 @@ -32,7 +44,26 @@ func (k msgServer) UpdateGatewayContract( } oldGateway := protocolContracts.Gateway - // update address and save + // update all ZRC20 contracts with the new gateway address + foreignCoins := k.GetAllForeignCoins(ctx) + for _, fcoin := range foreignCoins { + zrc20Addr := ethcommon.HexToAddress(fcoin.Zrc20ContractAddress) + if zrc20Addr == (ethcommon.Address{}) { + k.Logger(ctx).Error("invalid zrc20 contract address", "address", fcoin.Zrc20ContractAddress) + continue + } + + _, err := k.CallUpdateGatewayAddress(ctx, zrc20Addr, gatewayAddr) + if err != nil { + return nil, cosmoserrors.Wrapf( + err, + "failed to call updateSystemContractAddress for ZRC20 (%s)", + fcoin.Zrc20ContractAddress, + ) + } + } + + // update in the store address and save protocolContracts.Gateway = msg.NewGatewayContractAddress k.SetSystemContract(ctx, protocolContracts) diff --git a/x/fungible/keeper/msg_server_update_gateway_contract_test.go b/x/fungible/keeper/msg_server_update_gateway_contract_test.go index 70b0a1144c..6d9015aa0d 100644 --- a/x/fungible/keeper/msg_server_update_gateway_contract_test.go +++ b/x/fungible/keeper/msg_server_update_gateway_contract_test.go @@ -1,6 +1,9 @@ package keeper_test import ( + "github.com/ethereum/go-ethereum/common" + "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" + "github.com/zeta-chain/zetacore/pkg/chains" "testing" "github.com/stretchr/testify/require" @@ -12,46 +15,81 @@ import ( ) func TestKeeper_UpdateGatewayContract(t *testing.T) { - t.Run("can update the gateway contract address stored in the module", func(t *testing.T) { - // ARRANGE - k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ - UseAuthorityMock: true, - }) - - msgServer := keeper.NewMsgServerImpl(*k) - k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) - admin := sample.AccAddress() + t.Run( + "can update the gateway contract address stored in the module and update address in ZRC20s", + func(t *testing.T) { + // ARRANGE + k, ctx, sdkk, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) - authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + msgServer := keeper.NewMsgServerImpl(*k) + k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + admin := sample.AccAddress() - systemContractAddr := sample.EthAddress() - connectorAddr := sample.EthAddress() - k.SetSystemContract(ctx, types.SystemContract{ - SystemContract: systemContractAddr.Hex(), - ConnectorZevm: connectorAddr.Hex(), - Gateway: sample.EthAddress().Hex(), - }) + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + authorityMock.On("GetAdditionalChainList", ctx).Return([]chains.Chain{}) + + // setup gas coins for two chains + defaultChains := chains.DefaultChainsList() + require.True(t, len(defaultChains) > 1) + require.NotNil(t, defaultChains[0]) + require.NotNil(t, defaultChains[1]) + chainID1 := defaultChains[0].ChainId + chainID2 := defaultChains[1].ChainId + _, _, _, connectorAddr, systemContractAddr := deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) + gas1 := setupGasCoin(t, ctx, k, sdkk.EvmKeeper, chainID1, "foo", "foo") + gas2 := setupGasCoin(t, ctx, k, sdkk.EvmKeeper, chainID2, "bar", "bar") + queryZRC20Gateway := func(contract common.Address) string { + abi, err := zrc20.ZRC20MetaData.GetAbi() + require.NoError(t, err) + res, err := k.CallEVM( + ctx, + *abi, + types.ModuleAddressEVM, + contract, + keeper.BigIntZero, + nil, + false, + false, + "gatewayAddress", + ) + require.NoError(t, err) + unpacked, err := abi.Unpack("gatewayAddress", res.Ret) + require.NoError(t, err) + address, ok := unpacked[0].(common.Address) + require.True(t, ok) + return address.Hex() + } + + // new gateway address + newGatewayAddr := sample.EthAddress() + require.NotEqual(t, newGatewayAddr.Hex(), queryZRC20Gateway(gas1)) + require.NotEqual(t, newGatewayAddr.Hex(), queryZRC20Gateway(gas2)) - newGatewayAddr := sample.EthAddress() + msg := types.NewMsgUpdateGatewayContract(admin, newGatewayAddr.Hex()) + keepertest.MockCheckAuthorization(&authorityMock.Mock, msg, nil) - msg := types.NewMsgUpdateGatewayContract(admin, newGatewayAddr.Hex()) - keepertest.MockCheckAuthorization(&authorityMock.Mock, msg, nil) + // ACT + _, err := msgServer.UpdateGatewayContract(ctx, msg) - // ACT - _, err := msgServer.UpdateGatewayContract(ctx, msg) + // ASSERT + require.NoError(t, err) + sc, found := k.GetSystemContract(ctx) + require.True(t, found) - // ASSERT - require.NoError(t, err) - sc, found := k.GetSystemContract(ctx) - require.True(t, found) + // gateway is updated + require.EqualValues(t, newGatewayAddr.Hex(), sc.Gateway) - // gateway is updated - require.EqualValues(t, newGatewayAddr.Hex(), sc.Gateway) + // system contract and connector remain the same + require.EqualValues(t, systemContractAddr.Hex(), sc.SystemContract) + require.EqualValues(t, connectorAddr.Hex(), sc.ConnectorZevm) - // system contract and connector remain the same - require.EqualValues(t, systemContractAddr.Hex(), sc.SystemContract) - require.EqualValues(t, connectorAddr.Hex(), sc.ConnectorZevm) - }) + // gateway address in ZRC20s is updated + require.EqualValues(t, newGatewayAddr.Hex(), queryZRC20Gateway(gas1)) + require.EqualValues(t, newGatewayAddr.Hex(), queryZRC20Gateway(gas2)) + }, + ) t.Run( "can update and overwrite the gateway contract if system contract state variable not found", @@ -104,15 +142,38 @@ func TestKeeper_UpdateGatewayContract(t *testing.T) { admin := sample.AccAddress() authorityMock := keepertest.GetFungibleAuthorityMock(t, k) - msg := types.NewMsgUpdateSystemContract(admin, sample.EthAddress().Hex()) + msg := types.NewMsgUpdateGatewayContract(admin, sample.EthAddress().Hex()) keepertest.MockCheckAuthorization(&authorityMock.Mock, msg, authoritytypes.ErrUnauthorized) // ACT - _, err := msgServer.UpdateSystemContract(ctx, msg) + _, err := msgServer.UpdateGatewayContract(ctx, msg) // ASSERT require.Error(t, err) require.ErrorIs(t, err, authoritytypes.ErrUnauthorized) }) + t.Run("should prevent update the gateway contract if invalid gateway address", func(t *testing.T) { + // ARRANGE + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) + + msgServer := keeper.NewMsgServerImpl(*k) + k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + admin := sample.AccAddress() + + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + + msg := types.NewMsgUpdateGatewayContract(admin, "invalid") + keepertest.MockCheckAuthorization(&authorityMock.Mock, msg, nil) + + // ACT + _, err := msgServer.UpdateGatewayContract(ctx, msg) + + // ASSERT + require.Error(t, err) + require.Contains(t, err.Error(), "invalid gateway contract address") + }) + } diff --git a/x/fungible/keeper/v2_evm.go b/x/fungible/keeper/v2_evm.go index e77c04b580..4480cd2184 100644 --- a/x/fungible/keeper/v2_evm.go +++ b/x/fungible/keeper/v2_evm.go @@ -9,11 +9,38 @@ import ( "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" "github.com/zeta-chain/protocol-contracts/v2/pkg/revert.sol" "github.com/zeta-chain/protocol-contracts/v2/pkg/systemcontract.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" "github.com/zeta-chain/zetacore/pkg/crypto" "github.com/zeta-chain/zetacore/x/fungible/types" ) +// CallUpdateGatewayAddress calls the updateGatewayAddress function on the ZRC20 contract +// function updateGatewayAddress(address addr) +func (k Keeper) CallUpdateGatewayAddress( + ctx sdk.Context, + zrc20Address common.Address, + newGatewayAddress common.Address, +) (*evmtypes.MsgEthereumTxResponse, error) { + zrc20ABI, err := zrc20.ZRC20MetaData.GetAbi() + if err != nil { + return nil, err + } + + return k.CallEVM( + ctx, + *zrc20ABI, + types.ModuleAddressEVM, + zrc20Address, + BigIntZero, + nil, + true, + false, + "updateGatewayAddress", + newGatewayAddress, + ) +} + // CallDepositAndCallZRC20 calls the depositAndCall (ZRC20 version) function on the gateway contract // Callable only by the fungible module account // returns directly CallEVM() diff --git a/zetaclient/chains/evm/signer/signer.go b/zetaclient/chains/evm/signer/signer.go index c77d4e36e6..a25c3962f5 100644 --- a/zetaclient/chains/evm/signer/signer.go +++ b/zetaclient/chains/evm/signer/signer.go @@ -115,9 +115,10 @@ func (signer *Signer) SetERC20CustodyAddress(addr ethcommon.Address) { } // SetGatewayAddress sets the gateway address -func (signer *Signer) SetGatewayAddress(_ string) { - // Note: do nothing for now - // gateway address will be needed in the future contract architecture +func (signer *Signer) SetGatewayAddress(addr string) { + signer.Lock() + defer signer.Unlock() + signer.gatewayAddress = ethcommon.HexToAddress(addr) } // GetZetaConnectorAddress returns the zeta connector address diff --git a/zetaclient/orchestrator/orchestrator.go b/zetaclient/orchestrator/orchestrator.go index d8a8aeb6a0..9b2ced55bb 100644 --- a/zetaclient/orchestrator/orchestrator.go +++ b/zetaclient/orchestrator/orchestrator.go @@ -161,7 +161,7 @@ func (oc *Orchestrator) resolveSigner(app *zctx.AppContext, chainID int64) (inte case chain.IsEVM(): params := chain.Params() - // update zeta connector and ERC20 custody addresses + // update zeta connector, ERC20 custody, and gateway addresses zetaConnectorAddress := ethcommon.HexToAddress(params.GetConnectorContractAddress()) if zetaConnectorAddress != signer.GetZetaConnectorAddress() { signer.SetZetaConnectorAddress(zetaConnectorAddress) @@ -175,7 +175,13 @@ func (oc *Orchestrator) resolveSigner(app *zctx.AppContext, chainID int64) (inte signer.SetERC20CustodyAddress(erc20CustodyAddress) oc.logger.Info(). Str("signer.erc20_custody", erc20CustodyAddress.String()). - Msgf("updated zeta connector address for chain %d", chainID) + Msgf("updated erc20 custody address for chain %d", chainID) + } + if params.GatewayAddress != signer.GetGatewayAddress() { + signer.SetGatewayAddress(params.GatewayAddress) + oc.logger.Info(). + Str("signer.gateway_address", params.GatewayAddress). + Msgf("updated gateway address for chain %d", chainID) } case chain.IsSolana(): params := chain.Params() @@ -368,17 +374,6 @@ func (oc *Orchestrator) runScheduler(ctx context.Context) error { chainID := chain.ID() - // get cctxs from map and set pending transactions prometheus gauge - cctxList := cctxMap[chainID] - - metrics.PendingTxsPerChain. - WithLabelValues(chain.Name()). - Set(float64(len(cctxList))) - - if len(cctxList) == 0 { - continue - } - // update chain parameters for signer and chain observer signer, err := oc.resolveSigner(app, chainID) if err != nil { @@ -394,6 +389,17 @@ func (oc *Orchestrator) runScheduler(ctx context.Context) error { continue } + // get cctxs from map and set pending transactions prometheus gauge + cctxList := cctxMap[chainID] + + metrics.PendingTxsPerChain. + WithLabelValues(chain.Name()). + Set(float64(len(cctxList))) + + if len(cctxList) == 0 { + continue + } + if !app.IsOutboundObservationEnabled() { continue } From 9938c46c5c84ae44e709294a797145f37f777cae Mon Sep 17 00:00:00 2001 From: lumtis Date: Wed, 4 Sep 2024 11:11:20 +0200 Subject: [PATCH 2/4] go mod --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 97c95d67c5..fa4a5c0e95 100644 --- a/go.mod +++ b/go.mod @@ -43,7 +43,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/rakyll/statik v0.1.7 github.com/zeta-chain/keystone/keys v0.0.0-20231105174229-903bc9405da2 - github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20240816144801-7eb673cf8890 + github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20240819143729-b8229cd7b410 google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index bb816367cf..46c12bf27e 100644 --- a/go.sum +++ b/go.sum @@ -1673,8 +1673,8 @@ github.com/zeta-chain/go-tss v0.0.0-20240729195411-9f5ae8189449 h1:4U+4g2QQjbrme github.com/zeta-chain/go-tss v0.0.0-20240729195411-9f5ae8189449/go.mod h1:LN1IBRN8xQkKgdgLhl5BDGZyPm70QOTbVLejdS2FVpo= github.com/zeta-chain/keystone/keys v0.0.0-20231105174229-903bc9405da2 h1:gd2uE0X+ZbdFJ8DubxNqLbOVlCB12EgWdzSNRAR82tM= github.com/zeta-chain/keystone/keys v0.0.0-20231105174229-903bc9405da2/go.mod h1:x7Bkwbzt2W2lQfjOirnff0Dj+tykdbTG1FMJPVPZsvE= -github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20240816144801-7eb673cf8890 h1:y2TNtm9ZF/GjJIg40wiZ/IqoeDouFfqi27Uu3xSQaVE= -github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20240816144801-7eb673cf8890/go.mod h1:SjT7QirtJE8stnAe1SlNOanxtfSfijJm3MGJ+Ax7w7w= +github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20240819143729-b8229cd7b410 h1:sBeVX63s/qmfT1KnIKj1Y2SK3PsFpAM/P49ODcD1CN8= +github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20240819143729-b8229cd7b410/go.mod h1:SjT7QirtJE8stnAe1SlNOanxtfSfijJm3MGJ+Ax7w7w= github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= From 293ac49b0291f19d51a04198b108e6812c4f8efd Mon Sep 17 00:00:00 2001 From: lumtis Date: Wed, 4 Sep 2024 12:22:38 +0200 Subject: [PATCH 3/4] changelog --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index a81fc297db..eb4ee974a0 100644 --- a/changelog.md +++ b/changelog.md @@ -22,6 +22,7 @@ ### Tests * [2726](https://github.com/zeta-chain/node/pull/2726) - add e2e tests for deposit and call, deposit and revert +* [2821](https://github.com/zeta-chain/node/pull/2821) - V2 protocol contracts migration e2e tests ### Fixes From f62923f47b58bea35189ba757546f9dddba3ad1d Mon Sep 17 00:00:00 2001 From: lumtis Date: Wed, 4 Sep 2024 17:56:03 +0200 Subject: [PATCH 4/4] fix unit test --- e2e/config/config.go | 1 - 1 file changed, 1 deletion(-) diff --git a/e2e/config/config.go b/e2e/config/config.go index 4995bcd7c7..76c16120c0 100644 --- a/e2e/config/config.go +++ b/e2e/config/config.go @@ -70,7 +70,6 @@ type AdditionalAccounts struct { UserMisc Account `yaml:"user_misc"` UserAdmin Account `yaml:"user_admin"` UserMigration Account `yaml:"user_migration"` - UserPrecompile Account `yaml:"user_precompile"` UserV2Ether Account `yaml:"user_v2_ether"` UserV2ERC20 Account `yaml:"user_v2_erc20"` UserV2EtherRevert Account `yaml:"user_v2_ether_revert"`