From edebb7ac073a409d3625683f753e523619520ebc Mon Sep 17 00:00:00 2001 From: Alex Gartner Date: Tue, 12 Nov 2024 00:38:07 -0800 Subject: [PATCH 01/14] chore(e2e): move block production monitor before any transactions (#3132) --- cmd/zetae2e/local/local.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index 9858ac6382..6017313fa8 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -182,6 +182,11 @@ func localE2ETest(cmd *cobra.Command, _ []string) { ) noError(err) + // monitor block production to ensure we fail fast if there are consensus failures + // this is not run in an errgroup since only returning an error will not exit immedately + // this needs to be early to quickly detect consensus failure during genesis + go monitorBlockProductionExit(ctx, conf) + // set the authority client to the zeta tx server to be able to query message permissions deployerRunner.ZetaTxServer.SetAuthorityClient(deployerRunner.AuthorityClient) @@ -190,10 +195,6 @@ func localE2ETest(cmd *cobra.Command, _ []string) { noError(deployerRunner.FundEmissionsPool()) } - // monitor block production to ensure we fail fast if there are consensus failures - // this is not run in an errgroup since only returning an error will not exit immedately - go monitorBlockProductionExit(ctx, conf) - // wait for keygen to be completed // if setup is skipped, we assume that the keygen is already completed if !skipSetup { From dede0493f44743b273e20f1578532b60d3ca52cb Mon Sep 17 00:00:00 2001 From: Dmitry S <11892559+swift1337@users.noreply.github.com> Date: Tue, 12 Nov 2024 11:48:26 +0100 Subject: [PATCH 02/14] Remove chain.Chain from zetaclientd config (#3137) --- changelog.md | 1 + cmd/zetaclientd/start.go | 2 +- .../chains/evm/observer/observer_test.go | 4 --- zetaclient/config/config_chain.go | 27 +++---------------- zetaclient/config/types.go | 5 +--- zetaclient/config/types_test.go | 17 ------------ zetaclient/orchestrator/bootstap_test.go | 4 --- zetaclient/orchestrator/orchestrator_test.go | 2 +- 8 files changed, 7 insertions(+), 55 deletions(-) diff --git a/changelog.md b/changelog.md index 59e544fad6..b17c06c7c2 100644 --- a/changelog.md +++ b/changelog.md @@ -16,6 +16,7 @@ * [3118](https://github.com/zeta-chain/node/pull/3118) - zetaclient: remove hsm signer * [3122](https://github.com/zeta-chain/node/pull/3122) - improve & refactor zetaclientd cli * [3125](https://github.com/zeta-chain/node/pull/3125) - drop support for header proofs +* [3137](https://github.com/zeta-chain/node/pull/3137) - remove chain.Chain from zetaclientd config ### Fixes * [3041](https://github.com/zeta-chain/node/pull/3041) - replace libp2p public DHT with private gossip peer discovery and connection gater for inbound connections diff --git a/cmd/zetaclientd/start.go b/cmd/zetaclientd/start.go index 903e6d1735..6c3493ad06 100644 --- a/cmd/zetaclientd/start.go +++ b/cmd/zetaclientd/start.go @@ -55,7 +55,7 @@ func Start(_ *cobra.Command, _ []string) error { chains.Network_solana.String(): solanaKeyPass, } - //Load Config file given path + // Load Config file given path cfg, err := config.Load(globalOpts.ZetacoreHome) if err != nil { return err diff --git a/zetaclient/chains/evm/observer/observer_test.go b/zetaclient/chains/evm/observer/observer_test.go index d30be608e5..3be38cd86c 100644 --- a/zetaclient/chains/evm/observer/observer_test.go +++ b/zetaclient/chains/evm/observer/observer_test.go @@ -53,7 +53,6 @@ func getAppContext( // create config cfg := config.New(false) cfg.EVMChainConfigs[evmChain.ChainId] = config.EVMConfig{ - Chain: evmChain, Endpoint: endpoint, } @@ -178,7 +177,6 @@ func Test_NewObserver(t *testing.T) { { name: "should be able to create observer", evmCfg: config.EVMConfig{ - Chain: chain, Endpoint: "http://localhost:8545", }, chainParams: params, @@ -192,7 +190,6 @@ func Test_NewObserver(t *testing.T) { { name: "should fail if RPC call fails", evmCfg: config.EVMConfig{ - Chain: chain, Endpoint: "http://localhost:8545", }, chainParams: params, @@ -212,7 +209,6 @@ func Test_NewObserver(t *testing.T) { { name: "should fail on invalid ENV var", evmCfg: config.EVMConfig{ - Chain: chain, Endpoint: "http://localhost:8545", }, chainParams: params, diff --git a/zetaclient/config/config_chain.go b/zetaclient/config/config_chain.go index 6f17153b52..81cd3b2e1c 100644 --- a/zetaclient/config/config_chain.go +++ b/zetaclient/config/config_chain.go @@ -32,7 +32,9 @@ func New(setDefaults bool) Config { // bitcoinConfigRegnet contains Bitcoin config for regnet func bitcoinConfigRegnet() BTCConfig { return BTCConfig{ - RPCUsername: "smoketest", // smoketest is the previous name for E2E test, we keep this name for compatibility between client versions in upgrade test + // `smoketest` is the previous name for E2E test, + // we keep this name for compatibility between client versions in upgrade test + RPCUsername: "smoketest", RPCPassword: "123", RPCHost: "bitcoin:18443", RPCParams: "regtest", @@ -59,30 +61,7 @@ func tonConfigLocalnet() TONConfig { // it contains list of EVM chains with empty endpoint except for localnet func evmChainsConfigs() map[int64]EVMConfig { return map[int64]EVMConfig{ - chains.Ethereum.ChainId: { - Chain: chains.Ethereum, - }, - chains.BscMainnet.ChainId: { - Chain: chains.BscMainnet, - }, - chains.Goerli.ChainId: { - Chain: chains.Goerli, - Endpoint: "", - }, - chains.Sepolia.ChainId: { - Chain: chains.Sepolia, - Endpoint: "", - }, - chains.BscTestnet.ChainId: { - Chain: chains.BscTestnet, - Endpoint: "", - }, - chains.Mumbai.ChainId: { - Chain: chains.Mumbai, - Endpoint: "", - }, chains.GoerliLocalnet.ChainId: { - Chain: chains.GoerliLocalnet, Endpoint: "http://eth:8545", RPCAlertLatency: 60, }, diff --git a/zetaclient/config/types.go b/zetaclient/config/types.go index d3a1e4b3a0..e843397e0c 100644 --- a/zetaclient/config/types.go +++ b/zetaclient/config/types.go @@ -6,8 +6,6 @@ import ( "sync" "github.com/showa-93/go-mask" - - "github.com/zeta-chain/node/pkg/chains" ) // KeyringBackend is the type of keyring backend to use for the hotkey @@ -40,7 +38,6 @@ type ClientConfiguration struct { // EVMConfig is the config for EVM chain type EVMConfig struct { - Chain chains.Chain Endpoint string `mask:"filled"` RPCAlertLatency int64 } @@ -216,7 +213,7 @@ func (c Config) GetRelayerKeyPath() string { } func (c EVMConfig) Empty() bool { - return c.Endpoint == "" || c.Chain.IsEmpty() + return c.Endpoint == "" } func (c BTCConfig) Empty() bool { diff --git a/zetaclient/config/types_test.go b/zetaclient/config/types_test.go index 02f7eb5a6f..0b8884a30a 100644 --- a/zetaclient/config/types_test.go +++ b/zetaclient/config/types_test.go @@ -17,7 +17,6 @@ func Test_GetRelayerKeyPath(t *testing.T) { } func Test_GetEVMConfig(t *testing.T) { - chain := chains.Sepolia chainID := chains.Sepolia.ChainId t.Run("should find non-empty evm config", func(t *testing.T) { @@ -26,7 +25,6 @@ func Test_GetEVMConfig(t *testing.T) { // set valid evm endpoint cfg.EVMChainConfigs[chainID] = config.EVMConfig{ - Chain: chain, Endpoint: "localhost", } @@ -44,21 +42,6 @@ func Test_GetEVMConfig(t *testing.T) { _, found := cfg.GetEVMConfig(chainID) require.False(t, found) }) - - t.Run("should not find evm config if chain is empty", func(t *testing.T) { - // create config with defaults - cfg := config.New(true) - - // set empty chain - cfg.EVMChainConfigs[chainID] = config.EVMConfig{ - Chain: chains.Chain{}, - Endpoint: "localhost", - } - - // should not find evm config because chain is empty - _, found := cfg.GetEVMConfig(chainID) - require.False(t, found) - }) } func Test_GetBTCConfig(t *testing.T) { diff --git a/zetaclient/orchestrator/bootstap_test.go b/zetaclient/orchestrator/bootstap_test.go index eaae3a8e6d..c93efada00 100644 --- a/zetaclient/orchestrator/bootstap_test.go +++ b/zetaclient/orchestrator/bootstap_test.go @@ -44,12 +44,10 @@ func TestCreateSignerMap(t *testing.T) { cfg := config.New(false) cfg.EVMChainConfigs[chains.Ethereum.ChainId] = config.EVMConfig{ - Chain: chains.Ethereum, Endpoint: testutils.MockEVMRPCEndpoint, } cfg.EVMChainConfigs[chains.Polygon.ChainId] = config.EVMConfig{ - Chain: chains.Polygon, Endpoint: testutils.MockEVMRPCEndpoint, } @@ -225,12 +223,10 @@ func TestCreateChainObserverMap(t *testing.T) { cfg := config.New(false) cfg.EVMChainConfigs[chains.Ethereum.ChainId] = config.EVMConfig{ - Chain: chains.Ethereum, Endpoint: evmServer.Endpoint, } cfg.EVMChainConfigs[chains.Polygon.ChainId] = config.EVMConfig{ - Chain: chains.Polygon, Endpoint: evmServer.Endpoint, } diff --git a/zetaclient/orchestrator/orchestrator_test.go b/zetaclient/orchestrator/orchestrator_test.go index d88006920f..b86e85cc15 100644 --- a/zetaclient/orchestrator/orchestrator_test.go +++ b/zetaclient/orchestrator/orchestrator_test.go @@ -602,7 +602,7 @@ func createAppContext(t *testing.T, chainsOrParams ...any) *zctx.AppContext { for _, c := range supportedChains { switch { case chains.IsEVMChain(c.ChainId, nil): - cfg.EVMChainConfigs[c.ChainId] = config.EVMConfig{Chain: c} + cfg.EVMChainConfigs[c.ChainId] = config.EVMConfig{Endpoint: "localhost"} case chains.IsBitcoinChain(c.ChainId, nil): cfg.BTCChainConfigs[c.ChainId] = config.BTCConfig{RPCHost: "localhost"} case chains.IsSolanaChain(c.ChainId, nil): From 1738618456589706762f379d0633fd70194b0ca8 Mon Sep 17 00:00:00 2001 From: Lucas Bertrand Date: Tue, 12 Nov 2024 13:32:30 +0100 Subject: [PATCH 03/14] fix: allow BTC revert with dust amount (#3142) * add deposit and call with dust test * fix test * smaller amount * finish test * fix test * add gas consumption on revert * comment tests * format * Revert "add gas consumption on revert" This reverts commit b28903bb7f7018f211bfd53adbdedc032ec0f6e5. * Revert "comment tests" This reverts commit fd0d824d779fc2a2c48015d7ab3cb787679208dd. * Revert "format" This reverts commit 92acb224ceebc40e354e1ce6ed1208313e7b6b69. * coderabbit * format * lint --- cmd/zetae2e/local/local.go | 1 + e2e/e2etests/e2etests.go | 6 +++ ...tcoin_deposit_and_call_revert_with_dust.go | 53 +++++++++++++++++++ .../chains/bitcoin/observer/outbound.go | 5 +- zetaclient/chains/bitcoin/signer/signer.go | 21 ++++++-- 5 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 e2e/e2etests/test_bitcoin_deposit_and_call_revert_with_dust.go diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index 6017313fa8..a170929cbb 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -320,6 +320,7 @@ func localE2ETest(cmd *cobra.Command, _ []string) { e2etests.TestBitcoinDepositName, e2etests.TestBitcoinDepositAndCallName, e2etests.TestBitcoinDepositAndCallRevertName, + e2etests.TestBitcoinDepositAndCallRevertWithDustName, e2etests.TestBitcoinStdMemoDepositName, e2etests.TestBitcoinStdMemoDepositAndCallName, e2etests.TestBitcoinStdMemoDepositAndCallRevertName, diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index a880172830..3b5ef2ed00 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -80,6 +80,7 @@ const ( TestBitcoinDepositName = "bitcoin_deposit" TestBitcoinDepositAndCallName = "bitcoin_deposit_and_call" TestBitcoinDepositAndCallRevertName = "bitcoin_deposit_and_call_revert" + TestBitcoinDepositAndCallRevertWithDustName = "bitcoin_deposit_and_call_revert_with_dust" TestBitcoinDonationName = "bitcoin_donation" TestBitcoinStdMemoDepositName = "bitcoin_std_memo_deposit" TestBitcoinStdMemoDepositAndCallName = "bitcoin_std_memo_deposit_and_call" @@ -555,6 +556,11 @@ var AllE2ETests = []runner.E2ETest{ }, TestBitcoinDepositAndCallRevert, ), + runner.NewE2ETest( + TestBitcoinDepositAndCallRevertWithDustName, + "deposit Bitcoin into ZEVM; revert with dust amount that aborts the CCTX", []runner.ArgDefinition{}, + TestBitcoinDepositAndCallRevertWithDust, + ), runner.NewE2ETest( TestBitcoinStdMemoDepositName, "deposit Bitcoin into ZEVM with standard memo", diff --git a/e2e/e2etests/test_bitcoin_deposit_and_call_revert_with_dust.go b/e2e/e2etests/test_bitcoin_deposit_and_call_revert_with_dust.go new file mode 100644 index 0000000000..1dec399a9b --- /dev/null +++ b/e2e/e2etests/test_bitcoin_deposit_and_call_revert_with_dust.go @@ -0,0 +1,53 @@ +package e2etests + +import ( + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/node/e2e/runner" + "github.com/zeta-chain/node/e2e/utils" + "github.com/zeta-chain/node/pkg/constant" + "github.com/zeta-chain/node/testutil/sample" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" + zetabitcoin "github.com/zeta-chain/node/zetaclient/chains/bitcoin" +) + +// TestBitcoinDepositAndCallRevertWithDust sends a Bitcoin deposit that reverts with a dust amount in the revert outbound. +func TestBitcoinDepositAndCallRevertWithDust(r *runner.E2ERunner, args []string) { + // Given "Live" BTC network + stop := r.MineBlocksIfLocalBitcoin() + defer stop() + + require.Len(r, args, 0) + + // 0.002 BTC is consumed in a deposit and revert, the dust is set to 1000 satoshis in the protocol + // Therefore the deposit amount should be 0.002 + 0.000001 = 0.00200100 should trigger the condition + // As only 100 satoshis are left after the deposit + const ( + // depositAmount is 0.002001 BTC, chosen to result in 100 satoshis after deposit + // which is below the dust threshold of 1000 satoshis + depositAmount = 0.00200100 + ) + amount := depositAmount + zetabitcoin.DefaultDepositorFee + + // Given a list of UTXOs + utxos, err := r.ListDeployerUTXOs() + require.NoError(r, err) + require.NotEmpty(r, utxos) + + // ACT + // Send BTC to TSS address with a dummy memo + // zetacore should revert cctx if call is made on a non-existing address + nonExistReceiver := sample.EthAddress() + badMemo := append(nonExistReceiver.Bytes(), []byte("gibberish-memo")...) + txHash, err := r.SendToTSSFromDeployerWithMemo(amount, utxos, badMemo) + require.NoError(r, err) + require.NotEmpty(r, txHash) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, txHash.String(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "deposit_and_revert") + utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_Reverted) + + // check the test was effective: the revert outbound amount is less than the dust amount + require.Less(r, cctx.GetCurrentOutboundParam().Amount.Uint64(), uint64(constant.BTCWithdrawalDustAmount)) +} diff --git a/zetaclient/chains/bitcoin/observer/outbound.go b/zetaclient/chains/bitcoin/observer/outbound.go index 16c8d24b81..a66559b62e 100644 --- a/zetaclient/chains/bitcoin/observer/outbound.go +++ b/zetaclient/chains/bitcoin/observer/outbound.go @@ -13,6 +13,7 @@ import ( "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/coin" + "github.com/zeta-chain/node/pkg/constant" crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" "github.com/zeta-chain/node/zetaclient/chains/bitcoin" "github.com/zeta-chain/node/zetaclient/chains/bitcoin/rpc" @@ -531,8 +532,8 @@ func (ob *Observer) checkTssOutboundResult( return errors.Wrapf(err, "checkTssOutboundResult: invalid TSS Vin in outbound %s nonce %d", hash, nonce) } - // differentiate between normal and restricted cctx - if compliance.IsCctxRestricted(cctx) { + // differentiate between normal and cancelled cctx + if compliance.IsCctxRestricted(cctx) || params.Amount.Uint64() < constant.BTCWithdrawalDustAmount { err = ob.checkTSSVoutCancelled(params, rawResult.Vout) if err != nil { return errors.Wrapf( diff --git a/zetaclient/chains/bitcoin/signer/signer.go b/zetaclient/chains/bitcoin/signer/signer.go index 1c08dcdcd6..c239627f22 100644 --- a/zetaclient/chains/bitcoin/signer/signer.go +++ b/zetaclient/chains/bitcoin/signer/signer.go @@ -21,6 +21,7 @@ import ( "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/x/crosschain/types" observertypes "github.com/zeta-chain/node/x/observer/types" "github.com/zeta-chain/node/zetaclient/chains/base" @@ -413,13 +414,25 @@ func (signer *Signer) TryProcessOutbound( gasprice.Add(gasprice, satPerByte) // compliance check - cancelTx := compliance.IsCctxRestricted(cctx) - if cancelTx { + restrictedCCTX := compliance.IsCctxRestricted(cctx) + if restrictedCCTX { compliance.PrintComplianceLog(logger, signer.Logger().Compliance, true, chain.ChainId, cctx.Index, cctx.InboundParams.Sender, params.Receiver, "BTC") - amount = 0.0 // zero out the amount to cancel the tx } - logger.Info().Msgf("SignGasWithdraw: to %s, value %d sats", to.EncodeAddress(), params.Amount.Uint64()) + + // check dust amount + dustAmount := params.Amount.Uint64() < constant.BTCWithdrawalDustAmount + if dustAmount { + logger.Warn().Msgf("dust amount %d sats, canceling tx", params.Amount.Uint64()) + } + + // set the amount to 0 when the tx should be cancelled + cancelTx := restrictedCCTX || dustAmount + if cancelTx { + amount = 0.0 + } else { + logger.Info().Msgf("SignGasWithdraw: to %s, value %d sats", to.EncodeAddress(), params.Amount.Uint64()) + } // sign withdraw tx tx, err := signer.SignWithdrawTx( From f8df512505f3ff78b60e1869a756411176ad0ccd Mon Sep 17 00:00:00 2001 From: Dmitry S <11892559+swift1337@users.noreply.github.com> Date: Tue, 12 Nov 2024 14:31:39 +0100 Subject: [PATCH 04/14] fix(zetaclient): orchestrator: signer resolution (#3139) * Fix signer resolution * Address PR comments --- changelog.md | 1 + zetaclient/chains/evm/signer/signer.go | 48 +++++++++++++++++------ zetaclient/chains/solana/signer/signer.go | 23 ++++++++--- zetaclient/chains/ton/signer/signer.go | 7 +++- zetaclient/orchestrator/orchestrator.go | 46 +++------------------- 5 files changed, 67 insertions(+), 58 deletions(-) diff --git a/changelog.md b/changelog.md index b17c06c7c2..528b4db956 100644 --- a/changelog.md +++ b/changelog.md @@ -21,6 +21,7 @@ ### Fixes * [3041](https://github.com/zeta-chain/node/pull/3041) - replace libp2p public DHT with private gossip peer discovery and connection gater for inbound connections * [3106](https://github.com/zeta-chain/node/pull/3106) - prevent blocked CCTX on out of gas during omnichain calls +* [3139](https://github.com/zeta-chain/node/pull/3139) - fix config resolution in orchestrator ## v21.0.0 diff --git a/zetaclient/chains/evm/signer/signer.go b/zetaclient/chains/evm/signer/signer.go index 5e046107fb..5cebb323c1 100644 --- a/zetaclient/chains/evm/signer/signer.go +++ b/zetaclient/chains/evm/signer/signer.go @@ -108,43 +108,69 @@ func (signer *Signer) WithEvmClient(client interfaces.EVMRPCClient) { // SetZetaConnectorAddress sets the zeta connector address func (signer *Signer) SetZetaConnectorAddress(addr ethcommon.Address) { + // noop + if (addr == ethcommon.Address{}) || signer.zetaConnectorAddress == addr { + return + } + + signer.Logger().Std.Info(). + Str("signer.old_zeta_connector_address", signer.zetaConnectorAddress.String()). + Str("signer.new_zeta_connector_address", addr.String()). + Msg("Updated zeta connector address") + signer.Lock() - defer signer.Unlock() signer.zetaConnectorAddress = addr + signer.Unlock() } // SetERC20CustodyAddress sets the erc20 custody address func (signer *Signer) SetERC20CustodyAddress(addr ethcommon.Address) { + // noop + if (addr == ethcommon.Address{}) || signer.er20CustodyAddress == addr { + return + } + + signer.Logger().Std.Info(). + Str("signer.old_erc20_custody_address", signer.er20CustodyAddress.String()). + Str("signer.new_erc20_custody_address", addr.String()). + Msg("Updated erc20 custody address") + signer.Lock() - defer signer.Unlock() signer.er20CustodyAddress = addr + signer.Unlock() } // SetGatewayAddress sets the gateway address -func (signer *Signer) SetGatewayAddress(addr string) { +func (signer *Signer) SetGatewayAddress(addrRaw string) { + addr := ethcommon.HexToAddress(addrRaw) + + // noop + if (addr == ethcommon.Address{}) || signer.gatewayAddress == addr { + return + } + + signer.Logger().Std.Info(). + Str("signer.old_gateway_address", signer.gatewayAddress.String()). + Str("signer.new_gateway_address", addr.String()). + Msg("Updated gateway address") + signer.Lock() - defer signer.Unlock() - signer.gatewayAddress = ethcommon.HexToAddress(addr) + signer.gatewayAddress = addr + signer.Unlock() } // GetZetaConnectorAddress returns the zeta connector address func (signer *Signer) GetZetaConnectorAddress() ethcommon.Address { - signer.Lock() - defer signer.Unlock() return signer.zetaConnectorAddress } // GetERC20CustodyAddress returns the erc20 custody address func (signer *Signer) GetERC20CustodyAddress() ethcommon.Address { - signer.Lock() - defer signer.Unlock() return signer.er20CustodyAddress } // GetGatewayAddress returns the gateway address func (signer *Signer) GetGatewayAddress() string { - signer.Lock() - defer signer.Unlock() return signer.gatewayAddress.String() } diff --git a/zetaclient/chains/solana/signer/signer.go b/zetaclient/chains/solana/signer/signer.go index 8e180f8c7f..9100f5c628 100644 --- a/zetaclient/chains/solana/signer/signer.go +++ b/zetaclient/chains/solana/signer/signer.go @@ -258,25 +258,36 @@ func (signer *Signer) prepareWhitelistTx( // SetGatewayAddress sets the gateway address func (signer *Signer) SetGatewayAddress(address string) { + // noop + if address == "" || signer.gatewayID.String() == address { + return + } + // parse gateway ID and PDA gatewayID, pda, err := contracts.ParseGatewayIDAndPda(address) if err != nil { - signer.Logger().Std.Error().Err(err).Msgf("cannot parse gateway address: %s", address) + signer.Logger().Std.Error().Err(err).Str("address", address).Msgf("Unable to parse gateway address") return } - // update gateway ID and PDA - signer.Lock() - defer signer.Unlock() + // noop + if signer.gatewayID.Equals(gatewayID) { + return + } + + signer.Logger().Std.Info(). + Str("signer.old_gateway_address", signer.gatewayID.String()). + Str("signer.new_gateway_address", gatewayID.String()). + Msg("Updated gateway address") + signer.Lock() signer.gatewayID = gatewayID signer.pda = pda + signer.Unlock() } // GetGatewayAddress returns the gateway address func (signer *Signer) GetGatewayAddress() string { - signer.Lock() - defer signer.Unlock() return signer.gatewayID.String() } diff --git a/zetaclient/chains/ton/signer/signer.go b/zetaclient/chains/ton/signer/signer.go index bdd25c0c18..689692eece 100644 --- a/zetaclient/chains/ton/signer/signer.go +++ b/zetaclient/chains/ton/signer/signer.go @@ -278,7 +278,7 @@ func (s *Signer) GetGatewayAddress() string { // SetGatewayAddress sets gateway address. Has a check for noop. func (s *Signer) SetGatewayAddress(addr string) { // noop - if s.gateway.AccountID().ToRaw() == addr { + if addr == "" || s.gateway.AccountID().ToRaw() == addr { return } @@ -288,6 +288,11 @@ func (s *Signer) SetGatewayAddress(addr string) { return } + s.Logger().Std.Info(). + Str("signer.old_gateway_address", s.gateway.AccountID().ToRaw()). + Str("signer.new_gateway_address", acc.ToRaw()). + Msg("Updated gateway address") + s.Lock() s.gateway = toncontracts.NewGateway(acc) s.Unlock() diff --git a/zetaclient/orchestrator/orchestrator.go b/zetaclient/orchestrator/orchestrator.go index fe1c26b76a..986b799f72 100644 --- a/zetaclient/orchestrator/orchestrator.go +++ b/zetaclient/orchestrator/orchestrator.go @@ -9,7 +9,7 @@ import ( "time" sdkmath "cosmossdk.io/math" - ethcommon "github.com/ethereum/go-ethereum/common" + eth "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/samber/lo" @@ -164,47 +164,13 @@ func (oc *Orchestrator) resolveSigner(app *zctx.AppContext, chainID int64) (inte params := chain.Params() // update zeta connector, ERC20 custody, and gateway addresses - zetaConnectorAddress := ethcommon.HexToAddress(params.GetConnectorContractAddress()) - if zetaConnectorAddress != signer.GetZetaConnectorAddress() { - signer.SetZetaConnectorAddress(zetaConnectorAddress) - oc.logger.Info(). - Str("signer.connector_address", zetaConnectorAddress.String()). - Msgf("updated zeta connector address for chain %d", chainID) - } - erc20CustodyAddress := ethcommon.HexToAddress(params.GetErc20CustodyContractAddress()) - if erc20CustodyAddress != signer.GetERC20CustodyAddress() { - signer.SetERC20CustodyAddress(erc20CustodyAddress) - oc.logger.Info(). - Str("signer.erc20_custody", erc20CustodyAddress.String()). - 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) - } - + signer.SetZetaConnectorAddress(eth.HexToAddress(params.ConnectorContractAddress)) + signer.SetERC20CustodyAddress(eth.HexToAddress(params.Erc20CustodyContractAddress)) + signer.SetGatewayAddress(params.GatewayAddress) case chain.IsSolana(): - params := chain.Params() - - // update gateway address - 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) - } + signer.SetGatewayAddress(chain.Params().GatewayAddress) case chain.IsTON(): - newAddress := chain.Params().GatewayAddress - - if newAddress != signer.GetGatewayAddress() { - signer.SetGatewayAddress(newAddress) - oc.logger.Info(). - Str("signer.new_gateway_address", newAddress). - Int64("signer.chain_id", chainID). - Msg("set gateway address") - } + signer.SetGatewayAddress(chain.Params().GatewayAddress) } return signer, nil From 47b0323ffda0b39dce308907dca7cb538a6ccfb9 Mon Sep 17 00:00:00 2001 From: Dmitry S <11892559+swift1337@users.noreply.github.com> Date: Tue, 12 Nov 2024 15:17:55 +0100 Subject: [PATCH 05/14] refactor(zetaclient): move app context update from zetacore client (#3131) * Refactor context update worker * Drop zetaclient.GetBlockHeaderChainState * Move waitForZetacoreToCreateBlocks from zetacore client * Update changelog * Rollback upgrade plan return values semantics * Rearrange start() sequence to fix TSS * Address PR comments --- changelog.md | 1 + cmd/zetaclientd/inbound.go | 4 +- cmd/zetaclientd/start.go | 82 +++++---- cmd/zetaclientd/utils.go | 30 +++ pkg/rpc/clients.go | 2 +- pkg/rpc/clients_cosmos.go | 3 +- zetaclient/chains/interfaces/interfaces.go | 10 +- zetaclient/orchestrator/contextupdater.go | 172 ++++++++++++++++++ .../orchestrator/contextupdater_test.go | 82 +++++++++ zetaclient/orchestrator/orchestrator.go | 21 ++- zetaclient/testutils/mocks/zetacore_client.go | 132 +++++++++++++- zetaclient/tss/generate.go | 41 +++-- zetaclient/tss/tss_signer.go | 12 +- zetaclient/zetacore/client.go | 145 +-------------- zetaclient/zetacore/client_worker.go | 44 ----- zetaclient/zetacore/constant.go | 9 - zetaclient/zetacore/tx_test.go | 157 +--------------- 17 files changed, 510 insertions(+), 437 deletions(-) create mode 100644 zetaclient/orchestrator/contextupdater.go create mode 100644 zetaclient/orchestrator/contextupdater_test.go delete mode 100644 zetaclient/zetacore/client_worker.go diff --git a/changelog.md b/changelog.md index 528b4db956..e3c0336caa 100644 --- a/changelog.md +++ b/changelog.md @@ -16,6 +16,7 @@ * [3118](https://github.com/zeta-chain/node/pull/3118) - zetaclient: remove hsm signer * [3122](https://github.com/zeta-chain/node/pull/3122) - improve & refactor zetaclientd cli * [3125](https://github.com/zeta-chain/node/pull/3125) - drop support for header proofs +* [3131](https://github.com/zeta-chain/node/pull/3131) - move app context update from zetacore client * [3137](https://github.com/zeta-chain/node/pull/3137) - remove chain.Chain from zetaclientd config ### Fixes diff --git a/cmd/zetaclientd/inbound.go b/cmd/zetaclientd/inbound.go index bac3c96725..20a3422a37 100644 --- a/cmd/zetaclientd/inbound.go +++ b/cmd/zetaclientd/inbound.go @@ -22,6 +22,7 @@ import ( "github.com/zeta-chain/node/zetaclient/config" zctx "github.com/zeta-chain/node/zetaclient/context" "github.com/zeta-chain/node/zetaclient/keys" + "github.com/zeta-chain/node/zetaclient/orchestrator" "github.com/zeta-chain/node/zetaclient/zetacore" ) @@ -69,7 +70,8 @@ func InboundGetBallot(_ *cobra.Command, args []string) error { appContext := zctx.New(cfg, nil, zerolog.Nop()) ctx := zctx.WithAppContext(context.Background(), appContext) - if err := client.UpdateAppContext(ctx, appContext, zerolog.Nop()); err != nil { + err = orchestrator.UpdateAppContext(ctx, appContext, client, zerolog.Nop()) + if err != nil { return errors.Wrap(err, "failed to update app context") } diff --git a/cmd/zetaclientd/start.go b/cmd/zetaclientd/start.go index 6c3493ad06..46d204fa34 100644 --- a/cmd/zetaclientd/start.go +++ b/cmd/zetaclientd/start.go @@ -27,6 +27,7 @@ import ( "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/constant" zetaos "github.com/zeta-chain/node/pkg/os" + "github.com/zeta-chain/node/pkg/ticker" observerTypes "github.com/zeta-chain/node/x/observer/types" "github.com/zeta-chain/node/zetaclient/chains/base" "github.com/zeta-chain/node/zetaclient/config" @@ -100,7 +101,7 @@ func Start(_ *cobra.Command, _ []string) error { } // Wait until zetacore is ready to create blocks - if err = zetacoreClient.WaitForZetacoreToCreateBlocks(ctx); err != nil { + if err = waitForZetacoreToCreateBlocks(ctx, zetacoreClient, startLogger); err != nil { startLogger.Error().Err(err).Msg("WaitForZetacoreToCreateBlocks error") return err } @@ -141,14 +142,12 @@ func Start(_ *cobra.Command, _ []string) error { startLogger.Debug().Msgf("createAuthzSigner is ready") // Initialize core parameters from zetacore - if err = zetacoreClient.UpdateAppContext(ctx, appContext, startLogger); err != nil { + if err = orchestrator.UpdateAppContext(ctx, appContext, zetacoreClient, startLogger); err != nil { return errors.Wrap(err, "unable to update app context") } startLogger.Info().Msgf("Config is updated from zetacore\n %s", cfg.StringMasked()) - go zetacoreClient.UpdateAppContextWorker(ctx, appContext) - // Generate TSS address . The Tss address is generated through Keygen ceremony. The TSS key is used to sign all outbound transactions . // The hotkeyPk is private key for the Hotkey. The Hotkey is used to sign all inbound transactions // Each node processes a portion of the key stored in ~/.tss by default . Custom location can be specified in config file during init. @@ -201,33 +200,36 @@ func Start(_ *cobra.Command, _ []string) error { } // Create TSS server - server, err := mc.SetupTSSServer(peers, priKey, preParams, appContext.Config(), tssKeyPass, true, whitelistedPeers) + tssServer, err := mc.SetupTSSServer( + peers, + priKey, + preParams, + appContext.Config(), + tssKeyPass, + true, + whitelistedPeers, + ) if err != nil { return fmt.Errorf("SetupTSSServer error: %w", err) } + // Set P2P ID for telemetry - telemetryServer.SetP2PID(server.GetLocalPeerID()) + telemetryServer.SetP2PID(tssServer.GetLocalPeerID()) // Creating a channel to listen for os signals (or other signals) signalChannel := make(chan os.Signal, 1) signal.Notify(signalChannel, syscall.SIGINT, syscall.SIGTERM) - // Maintenance workers ============ - maintenance.NewTSSListener(zetacoreClient, masterLogger).Listen(ctx, func() { - masterLogger.Info().Msg("TSS listener received an action to shutdown zetaclientd.") - signalChannel <- syscall.SIGTERM - }) - go func() { for { time.Sleep(30 * time.Second) - ps := server.GetKnownPeers() + ps := tssServer.GetKnownPeers() metrics.NumConnectedPeers.Set(float64(len(ps))) telemetryServer.SetConnectedPeers(ps) } }() go func() { - host := server.GetP2PHost() + host := tssServer.GetP2PHost() pingRTT := make(map[peer.ID]int64) for { var wg sync.WaitGroup @@ -254,7 +256,7 @@ func Start(_ *cobra.Command, _ []string) error { // Generate a new TSS if keygen is set and add it into the tss server // If TSS has already been generated, and keygen was successful ; we use the existing TSS - err = mc.Generate(ctx, masterLogger, zetacoreClient, server) + err = mc.Generate(ctx, zetacoreClient, tssServer, masterLogger) if err != nil { return err } @@ -264,7 +266,7 @@ func Start(_ *cobra.Command, _ []string) error { zetacoreClient, tssHistoricalList, hotkeyPass, - server, + tssServer, ) if err != nil { startLogger.Error().Err(err).Msg("NewTSS error") @@ -279,23 +281,26 @@ func Start(_ *cobra.Command, _ []string) error { // Wait for TSS keygen to be successful before proceeding, This is a blocking thread only for a new keygen. // For existing keygen, this should directly proceed to the next step - ticker := time.NewTicker(time.Second * 1) - for range ticker.C { - keyGen := appContext.GetKeygen() - if keyGen.Status != observerTypes.KeygenStatus_KeyGenSuccess { - startLogger.Info().Msgf("Waiting for TSS Keygen to be a success, current status %s", keyGen.Status) - continue + _ = ticker.Run(ctx, time.Second, func(ctx context.Context, t *ticker.Ticker) error { + keygen, err = zetacoreClient.GetKeyGen(ctx) + switch { + case err != nil: + startLogger.Warn().Err(err).Msg("Waiting for TSS Keygen to be a success, got error") + case keygen.Status != observerTypes.KeygenStatus_KeyGenSuccess: + startLogger.Warn().Msgf("Waiting for TSS Keygen to be a success, current status %s", keygen.Status) + default: + t.Stop() } - break - } + + return nil + }) // Update Current TSS value from zetacore, if TSS keygen is successful, the TSS address is set on zeta-core // Returns err if the RPC call fails as zeta client needs the current TSS address to be set // This is only needed in case of a new Keygen , as the TSS address is set on zetacore only after the keygen is successful i.e enough votes have been broadcast currentTss, err := zetacoreClient.GetTSS(ctx) if err != nil { - startLogger.Error().Err(err).Msg("GetCurrentTSS error") - return err + return errors.Wrap(err, "unable to get current TSS") } // Filter supported BTC chain IDs @@ -314,6 +319,13 @@ func Start(_ *cobra.Command, _ []string) error { return err } + // Starts various background TSS listeners. + // Shuts down zetaclientd if any is triggered. + maintenance.NewTSSListener(zetacoreClient, masterLogger).Listen(ctx, func() { + masterLogger.Info().Msg("TSS listener received an action to shutdown zetaclientd.") + signalChannel <- syscall.SIGTERM + }) + if len(appContext.ListChainIDs()) == 0 { startLogger.Error().Interface("config", cfg).Msgf("No chains in updated config") } @@ -348,12 +360,12 @@ func Start(_ *cobra.Command, _ []string) error { // Each chain observer is responsible for observing events on the chain and processing them. observerMap, err := orchestrator.CreateChainObserverMap(ctx, zetacoreClient, tss, dbpath, logger, telemetryServer) if err != nil { - startLogger.Err(err).Msg("CreateChainObserverMap") - return err + return errors.Wrap(err, "unable to create chain observer map") } // Orchestrator wraps the zetacore client and adds the observers and signer maps to it. // This is the high level object used for CCTX interactions + // It also handles background configuration updates from zetacore maestro, err := orchestrator.New( ctx, zetacoreClient, @@ -365,14 +377,12 @@ func Start(_ *cobra.Command, _ []string) error { telemetryServer, ) if err != nil { - startLogger.Error().Err(err).Msg("Unable to create orchestrator") - return err + return errors.Wrap(err, "unable to create orchestrator") } // Start orchestrator with all observers and signers - if err := maestro.Start(ctx); err != nil { - startLogger.Error().Err(err).Msg("Unable to start orchestrator") - return err + if err = maestro.Start(ctx); err != nil { + return errors.Wrap(err, "unable to start orchestrator") } // start zeta supply checker @@ -389,12 +399,12 @@ func Start(_ *cobra.Command, _ []string) error { // defer zetaSupplyChecker.Stop() //} - startLogger.Info().Msgf("Zetaclientd is running") + startLogger.Info().Msg("zetaclientd is running") sig := <-signalChannel - startLogger.Info().Msgf("Stop signal received: %q", sig) + startLogger.Info().Msgf("Stop signal received: %q. Stopping zetaclientd", sig) - zetacoreClient.Stop() + maestro.Stop() return nil } diff --git a/cmd/zetaclientd/utils.go b/cmd/zetaclientd/utils.go index 011a1bc0ab..53f2f208bc 100644 --- a/cmd/zetaclientd/utils.go +++ b/cmd/zetaclientd/utils.go @@ -1,6 +1,7 @@ package main import ( + "context" "fmt" "net" "strings" @@ -13,6 +14,7 @@ import ( "google.golang.org/grpc/credentials/insecure" "github.com/zeta-chain/node/zetaclient/authz" + "github.com/zeta-chain/node/zetaclient/chains/interfaces" "github.com/zeta-chain/node/zetaclient/config" "github.com/zeta-chain/node/zetaclient/keys" "github.com/zeta-chain/node/zetaclient/zetacore" @@ -71,6 +73,34 @@ func waitForZetaCore(config config.Config, logger zerolog.Logger) { } } +func waitForZetacoreToCreateBlocks(ctx context.Context, zc interfaces.ZetacoreClient, logger zerolog.Logger) error { + const ( + interval = 5 * time.Second + attempts = 15 + ) + + var ( + retryCount = 0 + start = time.Now() + ) + + for { + blockHeight, err := zc.GetBlockHeight(ctx) + if err == nil && blockHeight > 1 { + logger.Info().Msgf("Zeta block height: %d", blockHeight) + return nil + } + + retryCount++ + if retryCount > attempts { + return fmt.Errorf("zetacore is not ready, timeout %s", time.Since(start).String()) + } + + logger.Info().Msgf("Failed to get block number, retry : %d/%d", retryCount, attempts) + time.Sleep(interval) + } +} + func validatePeer(seedPeer string) error { parsedPeer := strings.Split(seedPeer, "/") diff --git a/pkg/rpc/clients.go b/pkg/rpc/clients.go index 1dc0d7314e..4d94b317e4 100644 --- a/pkg/rpc/clients.go +++ b/pkg/rpc/clients.go @@ -98,7 +98,7 @@ func NewCometBFTClients(url string) (Clients, error) { return newClients(clientCtx) } -// NewGRPCClient creates a Clients which uses gRPC as the transport +// NewGRPCClients creates a Clients which uses gRPC as the transport func NewGRPCClients(url string, opts ...grpc.DialOption) (Clients, error) { grpcConn, err := grpc.Dial(url, opts...) if err != nil { diff --git a/pkg/rpc/clients_cosmos.go b/pkg/rpc/clients_cosmos.go index 329b5c2e20..59be82bfb5 100644 --- a/pkg/rpc/clients_cosmos.go +++ b/pkg/rpc/clients_cosmos.go @@ -11,8 +11,7 @@ import ( "github.com/zeta-chain/node/cmd/zetacored/config" ) -// GetUpgradePlan returns the current upgrade plan. -// if there is no active upgrade plan, plan will be nil, err will be nil as well. +// GetUpgradePlan returns the current upgrade plan or nil if there is no plan. func (c *Clients) GetUpgradePlan(ctx context.Context) (*upgradetypes.Plan, error) { in := &upgradetypes.QueryCurrentPlanRequest{} diff --git a/zetaclient/chains/interfaces/interfaces.go b/zetaclient/chains/interfaces/interfaces.go index 0043d0a7a9..4ed7c0797e 100644 --- a/zetaclient/chains/interfaces/interfaces.go +++ b/zetaclient/chains/interfaces/interfaces.go @@ -11,6 +11,7 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcd/wire" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" "github.com/ethereum/go-ethereum/accounts/abi/bind" ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" @@ -102,6 +103,10 @@ type ZetacoreClient interface { GetLogger() *zerolog.Logger GetKeys() keyinterfaces.ObserverKeys + GetSupportedChains(ctx context.Context) ([]chains.Chain, error) + GetAdditionalChains(ctx context.Context) ([]chains.Chain, error) + GetChainParams(ctx context.Context) ([]*observertypes.ChainParams, error) + GetKeyGen(ctx context.Context) (observertypes.Keygen, error) GetTSS(ctx context.Context) (observertypes.TSS, error) GetTSSHistory(ctx context.Context) ([]observertypes.TSS, error) @@ -130,10 +135,9 @@ type ZetacoreClient interface { GetZetaHotKeyBalance(ctx context.Context) (sdkmath.Int, error) GetInboundTrackersForChain(ctx context.Context, chainID int64) ([]crosschaintypes.InboundTracker, error) - PostOutboundTracker(ctx context.Context, chainID int64, nonce uint64, txHash string) (string, error) + GetUpgradePlan(ctx context.Context) (*upgradetypes.Plan, error) - Stop() - OnBeforeStop(callback func()) + PostOutboundTracker(ctx context.Context, chainID int64, nonce uint64, txHash string) (string, error) } // BTCRPCClient is the interface for BTC RPC client diff --git a/zetaclient/orchestrator/contextupdater.go b/zetaclient/orchestrator/contextupdater.go new file mode 100644 index 0000000000..02e4b275e0 --- /dev/null +++ b/zetaclient/orchestrator/contextupdater.go @@ -0,0 +1,172 @@ +package orchestrator + +import ( + "context" + + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + "github.com/pkg/errors" + "github.com/rs/zerolog" + + "github.com/zeta-chain/node/pkg/chains" + "github.com/zeta-chain/node/pkg/ticker" + observertypes "github.com/zeta-chain/node/x/observer/types" + zctx "github.com/zeta-chain/node/zetaclient/context" +) + +type Zetacore interface { + GetBlockHeight(ctx context.Context) (int64, error) + GetUpgradePlan(ctx context.Context) (*upgradetypes.Plan, error) + GetSupportedChains(ctx context.Context) ([]chains.Chain, error) + GetAdditionalChains(ctx context.Context) ([]chains.Chain, error) + GetCrosschainFlags(ctx context.Context) (observertypes.CrosschainFlags, error) + GetChainParams(ctx context.Context) ([]*observertypes.ChainParams, error) + GetTSS(ctx context.Context) (observertypes.TSS, error) + GetKeyGen(ctx context.Context) (observertypes.Keygen, error) +} + +var ErrUpgradeRequired = errors.New("upgrade required") + +func (oc *Orchestrator) runAppContextUpdater(ctx context.Context) error { + app, err := zctx.FromContext(ctx) + if err != nil { + return err + } + + interval := ticker.DurationFromUint64Seconds(app.Config().ConfigUpdateTicker) + + oc.logger.Info().Msg("UpdateAppContext worker started") + + task := func(ctx context.Context, t *ticker.Ticker) error { + err := UpdateAppContext(ctx, app, oc.zetacoreClient, oc.logger.Sampled) + switch { + case errors.Is(err, ErrUpgradeRequired): + oc.onUpgradeDetected(err) + t.Stop() + return nil + case err != nil: + oc.logger.Err(err).Msg("UpdateAppContext failed") + } + + return nil + } + + return ticker.Run( + ctx, + interval, + task, + ticker.WithLogger(oc.logger.Logger, "UpdateAppContext"), + ticker.WithStopChan(oc.stop), + ) +} + +// UpdateAppContext fetches latest data from Zetacore and updates the AppContext. +// Also detects if an upgrade is required. If an upgrade is required, it returns ErrUpgradeRequired. +func UpdateAppContext(ctx context.Context, app *zctx.AppContext, zc Zetacore, logger zerolog.Logger) error { + bn, err := zc.GetBlockHeight(ctx) + if err != nil { + return errors.Wrap(err, "unable to get zeta block height") + } + + if err = checkForZetacoreUpgrade(ctx, bn, zc); err != nil { + return err + } + + supportedChains, err := zc.GetSupportedChains(ctx) + if err != nil { + return errors.Wrap(err, "unable to fetch supported chains") + } + + additionalChains, err := zc.GetAdditionalChains(ctx) + if err != nil { + return errors.Wrap(err, "unable to fetch additional chains") + } + + chainParams, err := zc.GetChainParams(ctx) + if err != nil { + return errors.Wrap(err, "unable to fetch chain params") + } + + keyGen, err := zc.GetKeyGen(ctx) + if err != nil { + return errors.Wrap(err, "unable to fetch keygen from zetacore") + } + + crosschainFlags, err := zc.GetCrosschainFlags(ctx) + if err != nil { + return errors.Wrap(err, "unable to fetch crosschain flags from zetacore") + } + + tss, err := zc.GetTSS(ctx) + if err != nil { + return errors.Wrap(err, "unable to fetch current TSS") + } + + freshParams := make(map[int64]*observertypes.ChainParams, len(chainParams)) + + // check and update chain params for each chain + // Note that we are EXCLUDING ZetaChain from the chainParams if it's present + for i := range chainParams { + cp := chainParams[i] + + if !cp.IsSupported { + logger.Warn().Int64("chain.id", cp.ChainId).Msg("Skipping unsupported chain") + continue + } + + if chains.IsZetaChain(cp.ChainId, nil) { + continue + } + + if err := observertypes.ValidateChainParams(cp); err != nil { + logger.Warn().Err(err).Int64("chain.id", cp.ChainId).Msg("Skipping invalid chain params") + continue + } + + freshParams[cp.ChainId] = cp + } + + return app.Update( + keyGen, + supportedChains, + additionalChains, + freshParams, + tss.GetTssPubkey(), + crosschainFlags, + ) +} + +// returns an error if an upgrade is required +func checkForZetacoreUpgrade(ctx context.Context, zetaHeight int64, zc Zetacore) error { + plan, err := zc.GetUpgradePlan(ctx) + switch { + case err != nil: + return errors.Wrap(err, "unable to get upgrade plan") + case plan == nil: + // no upgrade planned + return nil + } + + upgradeHeight := plan.Height + + // We can return an error in a few blocks ahead. + // It's okay because the ticker might have an interval longer than 1 block (~5s). + // + // Example: if an upgrade is on block #102, we can return an error on block #100, #101, #102, ... + // Note that tha plan is deleted from zetacore after the upgrade block. + const upgradeBlockBuffer = 2 + + if (upgradeHeight - zetaHeight) <= upgradeBlockBuffer { + return errors.Wrapf(ErrUpgradeRequired, "current height: %d, upgrade height: %d", zetaHeight, upgradeHeight) + } + + return nil +} + +// onUpgradeDetected is called when an upgrade is detected. +func (oc *Orchestrator) onUpgradeDetected(errDetected error) { + const msg = "Upgrade detected." + + " Kill the process, replace the binary with upgraded version, and restart zetaclientd" + + oc.logger.Warn().Str("upgrade", errDetected.Error()).Msg(msg) + oc.Stop() +} diff --git a/zetaclient/orchestrator/contextupdater_test.go b/zetaclient/orchestrator/contextupdater_test.go new file mode 100644 index 0000000000..cc28d5ad9e --- /dev/null +++ b/zetaclient/orchestrator/contextupdater_test.go @@ -0,0 +1,82 @@ +package orchestrator + +import ( + "context" + "testing" + + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + "github.com/rs/zerolog" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/node/pkg/chains" + observertypes "github.com/zeta-chain/node/x/observer/types" + "github.com/zeta-chain/node/zetaclient/testutils/mocks" +) + +func Test_UpdateAppContext(t *testing.T) { + var ( + eth = chains.Ethereum + ethParams = mocks.MockChainParams(eth.ChainId, 100) + + btc = chains.BitcoinMainnet + btcParams = mocks.MockChainParams(btc.ChainId, 100) + ) + + t.Run("Updates app context", func(t *testing.T) { + var ( + ctx = context.Background() + app = createAppContext(t, eth, ethParams) + zetacore = mocks.NewZetacoreClient(t) + logger = zerolog.New(zerolog.NewTestWriter(t)) + ) + + // Given zetacore client that has eth and btc chains + newChains := []chains.Chain{eth, btc} + newParams := []*observertypes.ChainParams{ðParams, &btcParams} + ccFlags := observertypes.CrosschainFlags{ + IsInboundEnabled: true, + IsOutboundEnabled: true, + } + + zetacore.On("GetBlockHeight", mock.Anything).Return(int64(123), nil) + zetacore.On("GetUpgradePlan", mock.Anything).Return(nil, nil) + zetacore.On("GetSupportedChains", mock.Anything).Return(newChains, nil) + zetacore.On("GetAdditionalChains", mock.Anything).Return(nil, nil) + zetacore.On("GetChainParams", mock.Anything).Return(newParams, nil) + zetacore.On("GetKeyGen", mock.Anything).Return(observertypes.Keygen{}, nil) + zetacore.On("GetCrosschainFlags", mock.Anything).Return(ccFlags, nil) + zetacore.On("GetTSS", mock.Anything).Return(observertypes.TSS{TssPubkey: "0x123"}, nil) + + // ACT + err := UpdateAppContext(ctx, app, zetacore, logger) + + // ASSERT + require.NoError(t, err) + + // New chains should be added + _, err = app.GetChain(btc.ChainId) + require.NoError(t, err) + }) + + t.Run("Upgrade plan detected", func(t *testing.T) { + // ARRANGE + var ( + ctx = context.Background() + app = createAppContext(t, eth, ethParams) + zetacore = mocks.NewZetacoreClient(t) + logger = zerolog.New(zerolog.NewTestWriter(t)) + ) + + zetacore.On("GetBlockHeight", mock.Anything).Return(int64(123), nil) + zetacore.On("GetUpgradePlan", mock.Anything).Return(&upgradetypes.Plan{ + Name: "hello", + Height: 124, + }, nil) + + // ACT + err := UpdateAppContext(ctx, app, zetacore, logger) + + // ASSERT + require.ErrorIs(t, err, ErrUpgradeRequired) + }) +} diff --git a/zetaclient/orchestrator/orchestrator.go b/zetaclient/orchestrator/orchestrator.go index 986b799f72..c29b41466a 100644 --- a/zetaclient/orchestrator/orchestrator.go +++ b/zetaclient/orchestrator/orchestrator.go @@ -132,20 +132,17 @@ func (oc *Orchestrator) Start(ctx context.Context) error { oc.logger.Info().Str("signer", signerAddress.String()).Msg("Starting orchestrator") - // start cctx scheduler bg.Work(ctx, oc.runScheduler, bg.WithName("runScheduler"), bg.WithLogger(oc.logger.Logger)) bg.Work(ctx, oc.runObserverSignerSync, bg.WithName("runObserverSignerSync"), bg.WithLogger(oc.logger.Logger)) - - shutdownOrchestrator := func() { - // now stop orchestrator and all observers - close(oc.stop) - } - - oc.zetacoreClient.OnBeforeStop(shutdownOrchestrator) + bg.Work(ctx, oc.runAppContextUpdater, bg.WithName("runAppContextUpdater"), bg.WithLogger(oc.logger.Logger)) return nil } +func (oc *Orchestrator) Stop() { + close(oc.stop) +} + // returns signer with updated chain parameters. func (oc *Orchestrator) resolveSigner(app *zctx.AppContext, chainID int64) (interfaces.ChainSigner, error) { signer, err := oc.getSigner(chainID) @@ -746,7 +743,13 @@ func (oc *Orchestrator) runObserverSignerSync(ctx context.Context) error { return nil } - return ticker.Run(ctx, cadence, task, ticker.WithLogger(oc.logger.Logger, "SyncObserverSigner")) + return ticker.Run( + ctx, + cadence, + task, + ticker.WithLogger(oc.logger.Logger, "SyncObserverSigner"), + ticker.WithStopChan(oc.stop), + ) } // syncs and provisions observers & signers. diff --git a/zetaclient/testutils/mocks/zetacore_client.go b/zetaclient/testutils/mocks/zetacore_client.go index 5bd9b685f9..6bbaee022c 100644 --- a/zetaclient/testutils/mocks/zetacore_client.go +++ b/zetaclient/testutils/mocks/zetacore_client.go @@ -20,6 +20,8 @@ import ( types "github.com/zeta-chain/node/x/crosschain/types" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + zerolog "github.com/rs/zerolog" ) @@ -46,6 +48,36 @@ func (_m *ZetacoreClient) Chain() chains.Chain { return r0 } +// GetAdditionalChains provides a mock function with given fields: ctx +func (_m *ZetacoreClient) GetAdditionalChains(ctx context.Context) ([]chains.Chain, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetAdditionalChains") + } + + var r0 []chains.Chain + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) ([]chains.Chain, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) []chains.Chain); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]chains.Chain) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetAllOutboundTrackerByChain provides a mock function with given fields: ctx, chainID, order func (_m *ZetacoreClient) GetAllOutboundTrackerByChain(ctx context.Context, chainID int64, order interfaces.Order) ([]types.OutboundTracker, error) { ret := _m.Called(ctx, chainID, order) @@ -162,6 +194,36 @@ func (_m *ZetacoreClient) GetCctxByNonce(ctx context.Context, chainID int64, non return r0, r1 } +// GetChainParams provides a mock function with given fields: ctx +func (_m *ZetacoreClient) GetChainParams(ctx context.Context) ([]*observertypes.ChainParams, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetChainParams") + } + + var r0 []*observertypes.ChainParams + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) ([]*observertypes.ChainParams, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) []*observertypes.ChainParams); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*observertypes.ChainParams) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetCrosschainFlags provides a mock function with given fields: ctx func (_m *ZetacoreClient) GetCrosschainFlags(ctx context.Context) (observertypes.CrosschainFlags, error) { ret := _m.Called(ctx) @@ -434,6 +496,36 @@ func (_m *ZetacoreClient) GetRateLimiterInput(ctx context.Context, window int64) return r0, r1 } +// GetSupportedChains provides a mock function with given fields: ctx +func (_m *ZetacoreClient) GetSupportedChains(ctx context.Context) ([]chains.Chain, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetSupportedChains") + } + + var r0 []chains.Chain + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) ([]chains.Chain, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) []chains.Chain); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]chains.Chain) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetTSS provides a mock function with given fields: ctx func (_m *ZetacoreClient) GetTSS(ctx context.Context) (observertypes.TSS, error) { ret := _m.Called(ctx) @@ -492,6 +584,36 @@ func (_m *ZetacoreClient) GetTSSHistory(ctx context.Context) ([]observertypes.TS return r0, r1 } +// GetUpgradePlan provides a mock function with given fields: ctx +func (_m *ZetacoreClient) GetUpgradePlan(ctx context.Context) (*upgradetypes.Plan, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetUpgradePlan") + } + + var r0 *upgradetypes.Plan + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*upgradetypes.Plan, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) *upgradetypes.Plan); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*upgradetypes.Plan) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetZetaHotKeyBalance provides a mock function with given fields: ctx func (_m *ZetacoreClient) GetZetaHotKeyBalance(ctx context.Context) (math.Int, error) { ret := _m.Called(ctx) @@ -587,11 +709,6 @@ func (_m *ZetacoreClient) ListPendingCCTXWithinRateLimit(ctx context.Context) (* return r0, r1 } -// OnBeforeStop provides a mock function with given fields: callback -func (_m *ZetacoreClient) OnBeforeStop(callback func()) { - _m.Called(callback) -} - // PostOutboundTracker provides a mock function with given fields: ctx, chainID, nonce, txHash func (_m *ZetacoreClient) PostOutboundTracker(ctx context.Context, chainID int64, nonce uint64, txHash string) (string, error) { ret := _m.Called(ctx, chainID, nonce, txHash) @@ -746,11 +863,6 @@ func (_m *ZetacoreClient) PostVoteOutbound(ctx context.Context, gasLimit uint64, return r0, r1, r2 } -// Stop provides a mock function with given fields: -func (_m *ZetacoreClient) Stop() { - _m.Called() -} - // NewZetacoreClient creates a new instance of ZetacoreClient. 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 NewZetacoreClient(t interface { diff --git a/zetaclient/tss/generate.go b/zetaclient/tss/generate.go index 5334212332..1adaad8c5c 100644 --- a/zetaclient/tss/generate.go +++ b/zetaclient/tss/generate.go @@ -17,7 +17,7 @@ import ( "github.com/zeta-chain/node/pkg/chains" observertypes "github.com/zeta-chain/node/x/observer/types" "github.com/zeta-chain/node/zetaclient/chains/interfaces" - zctx "github.com/zeta-chain/node/zetaclient/context" + "github.com/zeta-chain/node/zetaclient/logs" "github.com/zeta-chain/node/zetaclient/metrics" "github.com/zeta-chain/node/zetaclient/zetacore" ) @@ -29,15 +29,11 @@ import ( // In case of a failed keygen a TSS failed vote is broadcasted to zetacore. func Generate( ctx context.Context, - logger zerolog.Logger, - zetaCoreClient *zetacore.Client, + zc *zetacore.Client, keygenTssServer *tss.TssServer, + logger zerolog.Logger, ) error { - keygenLogger := logger.With().Str("module", "keygen").Logger() - app, err := zctx.FromContext(ctx) - if err != nil { - return err - } + keygenLogger := logger.With().Str(logs.FieldModule, "tss_keygen").Logger() // If Keygen block is set it will try to generate new TSS at the block // This is a blocking thread and will wait until the ceremony is complete successfully // If the TSS generation is unsuccessful , it will loop indefinitely until a new TSS is generated @@ -52,19 +48,24 @@ func Generate( // Break out of loop only when TSS is generated successfully, either at the keygenBlock or if it has been generated already , Block set as zero in genesis file // This loop will try keygen at the keygen block and then wait for keygen to be successfully reported by all nodes before breaking out of the loop. // If keygen is unsuccessful, it will reset the triedKeygenAtBlock flag and try again at a new keygen block. - keyGen := app.GetKeygen() - if keyGen.Status == observertypes.KeygenStatus_KeyGenSuccess { + keyGen, err := zc.GetKeyGen(ctx) + switch { + case err != nil: + keygenLogger.Error().Err(err).Msg("GetKeyGen RPC error") + continue + case keyGen.Status == observertypes.KeygenStatus_KeyGenSuccess: return nil - } - // Arrive at this stage only if keygen is unsuccessfully reported by every node . This will reset the flag and to try again at a new keygen block - if keyGen.Status == observertypes.KeygenStatus_KeyGenFailed { + case keyGen.Status == observertypes.KeygenStatus_KeyGenFailed: + // Arrive at this stage only if keygen is unsuccessfully reported by every node. + // This will reset the flag and to try again at a new keygen block triedKeygenAtBlock = false continue } + // Try generating TSS at keygen block , only when status is pending keygen and generation has not been tried at the block if keyGen.Status == observertypes.KeygenStatus_PendingKeygen { // Return error if RPC is not working - currentBlock, err := zetaCoreClient.GetBlockHeight(ctx) + currentBlock, err := zc.GetBlockHeight(ctx) if err != nil { keygenLogger.Error().Err(err).Msg("GetBlockHeight RPC error") continue @@ -79,16 +80,16 @@ func Generate( if currentBlock > lastBlock { lastBlock = currentBlock keygenLogger.Info(). - Msgf("Waiting For Keygen Block to arrive or new keygen block to be set. Keygen Block : %d Current Block : %d ChainID %s ", keyGen.BlockNumber, currentBlock, app.Config().ChainID) + Msgf("Waiting For Keygen Block to arrive or new keygen block to be set. Keygen Block: %d; Current Block: %d", keyGen.BlockNumber, currentBlock) } continue } // Try keygen only once at a particular block, irrespective of whether it is successful or failure triedKeygenAtBlock = true - newPubkey, err := keygenTSS(ctx, keyGen, *keygenTssServer, zetaCoreClient, keygenLogger) + newPubkey, err := keygenTSS(ctx, keyGen, *keygenTssServer, zc, keygenLogger) if err != nil { keygenLogger.Error().Err(err).Msg("keygenTSS error") - tssFailedVoteHash, err := zetaCoreClient.PostVoteTSS(ctx, + tssFailedVoteHash, err := zc.PostVoteTSS(ctx, "", keyGen.BlockNumber, chains.ReceiveStatus_failed) if err != nil { keygenLogger.Error().Err(err).Msg("Failed to broadcast Failed TSS Vote to zetacore") @@ -98,7 +99,7 @@ func Generate( continue } // If TSS is successful , broadcast the vote to zetacore and also set the Pubkey - tssSuccessVoteHash, err := zetaCoreClient.PostVoteTSS(ctx, + tssSuccessVoteHash, err := zc.PostVoteTSS(ctx, newPubkey, keyGen.BlockNumber, chains.ReceiveStatus_success, @@ -117,7 +118,7 @@ func Generate( } } keygenLogger.Debug(). - Msgf("Waiting for TSS to be generated or Current Keygen to be be finalized. Keygen Block : %d ", keyGen.BlockNumber) + Msgf("Waiting for TSS to be generated or Current Keygen to be be finalized. Keygen Block: %d", keyGen.BlockNumber) } return errors.New("unexpected state for TSS generation") } @@ -173,7 +174,7 @@ func keygenTSS( // TestTSS tests the TSS keygen by signing a sample message with the TSS key. func TestTSS(pubkey string, tssServer tss.TssServer, logger zerolog.Logger) error { - keygenLogger := logger.With().Str("module", "test-keygen").Logger() + keygenLogger := logger.With().Str(logs.FieldModule, "tss_test_keygen").Logger() keygenLogger.Info().Msgf("KeyGen success ! Doing a Key-sign test") // KeySign can fail even if TSS keygen is successful, just logging the error here to break out of outer loop and report TSS err := TestKeysign(pubkey, tssServer) diff --git a/zetaclient/tss/tss_signer.go b/zetaclient/tss/tss_signer.go index 83ff502f4a..bb887d9380 100644 --- a/zetaclient/tss/tss_signer.go +++ b/zetaclient/tss/tss_signer.go @@ -277,32 +277,30 @@ func (tss *TSS) Sign( log.Info().Msgf("signature of digest is... %v", signature) if len(signature) == 0 { - log.Warn().Err(err).Msgf("signature has length 0") - return [65]byte{}, fmt.Errorf("keysign fail: %s", err) + return [65]byte{}, fmt.Errorf("keysign fail: signature list is empty") } if !verifySignature(tssPubkey, signature, H) { - log.Error().Err(err).Msgf("signature verification failure") - return [65]byte{}, fmt.Errorf("signuature verification fail") + return [65]byte{}, fmt.Errorf("signuature verification failue") } var sigbyte [65]byte _, err = base64.StdEncoding.Decode(sigbyte[:32], []byte(signature[0].R)) if err != nil { log.Error().Err(err).Msg("decoding signature R") - return [65]byte{}, fmt.Errorf("signuature verification fail") + return [65]byte{}, fmt.Errorf("signuature verification failure (R) %w", err) } _, err = base64.StdEncoding.Decode(sigbyte[32:64], []byte(signature[0].S)) if err != nil { log.Error().Err(err).Msg("decoding signature S") - return [65]byte{}, fmt.Errorf("signuature verification fail") + return [65]byte{}, fmt.Errorf("signuature verification failue (S): %w", err) } _, err = base64.StdEncoding.Decode(sigbyte[64:65], []byte(signature[0].RecoveryID)) if err != nil { log.Error().Err(err).Msg("decoding signature RecoveryID") - return [65]byte{}, fmt.Errorf("signuature verification fail") + return [65]byte{}, fmt.Errorf("signuature verification failue (V) %w", err) } return sigbyte, nil diff --git a/zetaclient/zetacore/client.go b/zetaclient/zetacore/client.go index dac6d400fb..65078a72cb 100644 --- a/zetaclient/zetacore/client.go +++ b/zetaclient/zetacore/client.go @@ -2,11 +2,9 @@ package zetacore import ( - "context" "fmt" "strings" "sync" - "time" rpchttp "github.com/cometbft/cometbft/rpc/client/http" cosmosclient "github.com/cosmos/cosmos-sdk/client" @@ -21,11 +19,10 @@ import ( "github.com/zeta-chain/node/pkg/authz" "github.com/zeta-chain/node/pkg/chains" zetacore_rpc "github.com/zeta-chain/node/pkg/rpc" - observertypes "github.com/zeta-chain/node/x/observer/types" "github.com/zeta-chain/node/zetaclient/chains/interfaces" "github.com/zeta-chain/node/zetaclient/config" - zctx "github.com/zeta-chain/node/zetaclient/context" keyinterfaces "github.com/zeta-chain/node/zetaclient/keys/interfaces" + "github.com/zeta-chain/node/zetaclient/logs" ) var _ interfaces.ZetacoreClient = &Client{} @@ -43,12 +40,10 @@ type Client struct { accountNumber map[authz.KeyType]uint64 seqNumber map[authz.KeyType]uint64 - encodingCfg etherminttypes.EncodingConfig - keys keyinterfaces.ObserverKeys - chainID string - chain chains.Chain - stop chan struct{} - onBeforeStopCallback []func() + encodingCfg etherminttypes.EncodingConfig + keys keyinterfaces.ObserverKeys + chainID string + chain chains.Chain mu sync.RWMutex } @@ -100,7 +95,7 @@ func NewClient( return nil, errors.Wrapf(err, "invalid chain id %q", chainID) } - log := logger.With().Str("module", "zetacoreClient").Logger() + log := logger.With().Str(logs.FieldModule, "zetacoreClient").Logger() cfg := config.ClientConfiguration{ ChainHost: cosmosREST(chainIP), @@ -140,7 +135,6 @@ func NewClient( encodingCfg: encodingCfg, keys: keys, - stop: make(chan struct{}), chainID: chainID, chain: zetaChain, }, nil @@ -248,23 +242,6 @@ func (c *Client) GetKeys() keyinterfaces.ObserverKeys { return c.keys } -// OnBeforeStop adds a callback to be called before the client stops. -func (c *Client) OnBeforeStop(callback func()) { - c.onBeforeStopCallback = append(c.onBeforeStopCallback, callback) -} - -// Stop stops the client and optionally calls the onBeforeStop callbacks. -func (c *Client) Stop() { - c.logger.Info().Msgf("Stopping zetacore client") - - for i := len(c.onBeforeStopCallback) - 1; i >= 0; i-- { - c.logger.Info().Int("callback.index", i).Msgf("calling onBeforeStopCallback") - c.onBeforeStopCallback[i]() - } - - close(c.stop) -} - // GetAccountNumberAndSequenceNumber We do not use multiple KeyType for now , but this can be optionally used in the future to seprate TSS signer from Zetaclient GRantee func (c *Client) GetAccountNumberAndSequenceNumber(_ authz.KeyType) (uint64, uint64, error) { address, err := c.keys.GetAddress() @@ -293,116 +270,6 @@ func (c *Client) SetAccountNumber(keyType authz.KeyType) error { return nil } -// WaitForZetacoreToCreateBlocks waits for zetacore to create blocks -func (c *Client) WaitForZetacoreToCreateBlocks(ctx context.Context) error { - retryCount := 0 - for { - block, err := c.GetLatestZetaBlock(ctx) - if err == nil && block.Header.Height > 1 { - c.logger.Info().Msgf("Zetacore height: %d", block.Header.Height) - break - } - retryCount++ - c.logger.Debug().Msgf("Failed to get latest Block , Retry : %d/%d", retryCount, DefaultRetryCount) - if retryCount > ExtendedRetryCount { - return fmt.Errorf("zetacore is not ready, waited for %d seconds", DefaultRetryCount*DefaultRetryInterval) - } - time.Sleep(DefaultRetryInterval * time.Second) - } - return nil -} - -// UpdateAppContext updates zctx.AppContext -// zetacore stores AppContext for all clients -func (c *Client) UpdateAppContext(ctx context.Context, appContext *zctx.AppContext, logger zerolog.Logger) error { - bn, err := c.GetBlockHeight(ctx) - if err != nil { - return errors.Wrap(err, "unable to get zetablock height") - } - - plan, err := c.GetUpgradePlan(ctx) - if err != nil { - return errors.Wrap(err, "unable to get upgrade plan") - } - - // Stop client and notify dependant services to stop (Orchestrator, Observers, and Signers) - if plan != nil && bn == plan.Height-1 { - c.logger.Warn().Msgf( - "Active upgrade plan detected and upgrade height reached: %s at height %d; Stopping ZetaClient;"+ - " please kill this process, replace zetaclientd binary with upgraded version, and restart zetaclientd", - plan.Name, - plan.Height, - ) - - c.Stop() - - return nil - } - - supportedChains, err := c.GetSupportedChains(ctx) - if err != nil { - return errors.Wrap(err, "unable to fetch supported chains") - } - - additionalChains, err := c.GetAdditionalChains(ctx) - if err != nil { - return errors.Wrap(err, "unable to fetch additional chains") - } - - chainParams, err := c.GetChainParams(ctx) - if err != nil { - return errors.Wrap(err, "unable to fetch chain params") - } - - keyGen, err := c.GetKeyGen(ctx) - if err != nil { - return errors.Wrap(err, "unable to fetch keygen from zetacore") - } - - crosschainFlags, err := c.GetCrosschainFlags(ctx) - if err != nil { - return errors.Wrap(err, "unable to fetch crosschain flags from zetacore") - } - - tss, err := c.GetTSS(ctx) - if err != nil { - return errors.Wrap(err, "unable to fetch current TSS") - } - - freshParams := make(map[int64]*observertypes.ChainParams, len(chainParams)) - - // check and update chain params for each chain - // Note that we are EXCLUDING ZetaChain from the chainParams if it's present - for i := range chainParams { - cp := chainParams[i] - - if !cp.IsSupported { - logger.Warn().Int64("chain.id", cp.ChainId).Msg("Skipping unsupported chain") - continue - } - - if chains.IsZetaChain(cp.ChainId, nil) { - continue - } - - if err := observertypes.ValidateChainParams(cp); err != nil { - logger.Warn().Err(err).Int64("chain.id", cp.ChainId).Msg("Skipping invalid chain params") - continue - } - - freshParams[cp.ChainId] = cp - } - - return appContext.Update( - keyGen, - supportedChains, - additionalChains, - freshParams, - tss.GetTssPubkey(), - crosschainFlags, - ) -} - func cosmosREST(host string) string { return fmt.Sprintf("%s:1317", host) } diff --git a/zetaclient/zetacore/client_worker.go b/zetaclient/zetacore/client_worker.go deleted file mode 100644 index b1fb4a6074..0000000000 --- a/zetaclient/zetacore/client_worker.go +++ /dev/null @@ -1,44 +0,0 @@ -package zetacore - -import ( - "context" - "time" - - "github.com/rs/zerolog" - - appcontext "github.com/zeta-chain/node/zetaclient/context" -) - -var logSampler = &zerolog.BasicSampler{N: 10} - -// UpdateAppContextWorker is a polling goroutine that checks and updates AppContext at every height. -// todo implement graceful shutdown and work group -func (c *Client) UpdateAppContextWorker(ctx context.Context, app *appcontext.AppContext) { - defer func() { - if r := recover(); r != nil { - c.logger.Error().Interface("panic", r).Msg("UpdateAppContextWorker: recovered from panic") - } - }() - - var ( - // #nosec G115 interval is in range and not user controlled - updateEvery = time.Duration(app.Config().ConfigUpdateTicker) * time.Second - ticker = time.NewTicker(updateEvery) - logger = c.logger.Sample(logSampler) - ) - - c.logger.Info().Msg("UpdateAppContextWorker started") - - for { - select { - case <-ticker.C: - c.logger.Debug().Msg("UpdateAppContextWorker invocation") - if err := c.UpdateAppContext(ctx, app, logger); err != nil { - c.logger.Err(err).Msg("UpdateAppContextWorker failed to update config") - } - case <-c.stop: - c.logger.Info().Msg("UpdateAppContextWorker stopped") - return - } - } -} diff --git a/zetaclient/zetacore/constant.go b/zetaclient/zetacore/constant.go index ab13e741d0..c2185a08e1 100644 --- a/zetaclient/zetacore/constant.go +++ b/zetaclient/zetacore/constant.go @@ -33,15 +33,6 @@ const ( // PostBlameDataGasLimit is the gas limit for voting on blames PostBlameDataGasLimit = 200_000 - // DefaultRetryCount is the number of retries for broadcasting a tx - DefaultRetryCount = 5 - - // ExtendedRetryCount is an extended number of retries for broadcasting a tx, used in keygen operations - ExtendedRetryCount = 15 - - // DefaultRetryInterval is the interval between retries in seconds - DefaultRetryInterval = 5 - // PostVoteOutboundGasLimit is the gas limit for voting on observed outbound tx (for zetachain itself) PostVoteOutboundGasLimit = 500_000 diff --git a/zetaclient/zetacore/tx_test.go b/zetaclient/zetacore/tx_test.go index f1cac183f1..f12d7fc0e9 100644 --- a/zetaclient/zetacore/tx_test.go +++ b/zetaclient/zetacore/tx_test.go @@ -2,32 +2,20 @@ package zetacore import ( "context" - "net" "testing" - "github.com/zeta-chain/node/testutil/sample" - authoritytypes "github.com/zeta-chain/node/x/authority/types" - "cosmossdk.io/math" sdktypes "github.com/cosmos/cosmos-sdk/types" - upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" "github.com/pkg/errors" - "github.com/rs/zerolog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - zctx "github.com/zeta-chain/node/zetaclient/context" - "gitlab.com/thorchain/tss/go-tss/blame" - "go.nhat.io/grpcmock" - "go.nhat.io/grpcmock/planner" - "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/coin" crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" - lightclienttypes "github.com/zeta-chain/node/x/lightclient/types" observertypes "github.com/zeta-chain/node/x/observer/types" - "github.com/zeta-chain/node/zetaclient/config" "github.com/zeta-chain/node/zetaclient/keys" "github.com/zeta-chain/node/zetaclient/testutils/mocks" + "gitlab.com/thorchain/tss/go-tss/blame" ) const ( @@ -199,149 +187,6 @@ func TestZetacore_SetTSS(t *testing.T) { }) } -func TestZetacore_UpdateAppContext(t *testing.T) { - ctx := context.Background() - - //Setup server for multiple grpc calls - listener, err := net.Listen("tcp", "127.0.0.1:9090") - require.NoError(t, err) - - ethChainParams := mocks.MockChainParams(chains.Ethereum.ChainId, 100) - - server := grpcmock.MockUnstartedServer( - grpcmock.RegisterService(crosschaintypes.RegisterQueryServer), - grpcmock.RegisterService(upgradetypes.RegisterQueryServer), - grpcmock.RegisterService(observertypes.RegisterQueryServer), - grpcmock.RegisterService(lightclienttypes.RegisterQueryServer), - grpcmock.RegisterService(authoritytypes.RegisterQueryServer), - grpcmock.WithPlanner(planner.FirstMatch()), - grpcmock.WithListener(listener), - func(s *grpcmock.Server) { - method := "/zetachain.zetacore.crosschain.Query/LastZetaHeight" - s.ExpectUnary(method). - UnlimitedTimes(). - WithPayload(crosschaintypes.QueryLastZetaHeightRequest{}). - Return(crosschaintypes.QueryLastZetaHeightResponse{Height: 12345}) - - method = "/cosmos.upgrade.v1beta1.Query/CurrentPlan" - s.ExpectUnary(method). - UnlimitedTimes(). - WithPayload(upgradetypes.QueryCurrentPlanRequest{}). - Return(upgradetypes.QueryCurrentPlanResponse{ - Plan: &upgradetypes.Plan{ - Name: "big upgrade", - Height: 100, - }, - }) - - method = "/zetachain.zetacore.observer.Query/GetChainParams" - s.ExpectUnary(method). - UnlimitedTimes(). - WithPayload(observertypes.QueryGetChainParamsRequest{}). - Return(observertypes.QueryGetChainParamsResponse{ChainParams: &observertypes.ChainParamsList{ - ChainParams: []*observertypes.ChainParams{ - {ChainId: 7000}, // ZetaChain - ðChainParams, - }, - }}) - - method = "/zetachain.zetacore.observer.Query/SupportedChains" - s.ExpectUnary(method). - UnlimitedTimes(). - WithPayload(observertypes.QuerySupportedChains{}). - Return(observertypes.QuerySupportedChainsResponse{ - Chains: []chains.Chain{ - { - ChainId: chains.BitcoinMainnet.ChainId, - Network: chains.BscMainnet.Network, - NetworkType: chains.BscMainnet.NetworkType, - Vm: chains.BscMainnet.Vm, - Consensus: chains.BscMainnet.Consensus, - IsExternal: chains.BscMainnet.IsExternal, - CctxGateway: chains.BscMainnet.CctxGateway, - Name: chains.BscMainnet.Name, - }, - { - ChainId: chains.Ethereum.ChainId, - Network: chains.Ethereum.Network, - NetworkType: chains.Ethereum.NetworkType, - Vm: chains.Ethereum.Vm, - Consensus: chains.Ethereum.Consensus, - IsExternal: chains.Ethereum.IsExternal, - CctxGateway: chains.Ethereum.CctxGateway, - Name: chains.Ethereum.Name, - }, - }, - }) - - method = "/zetachain.zetacore.observer.Query/Keygen" - s.ExpectUnary(method). - UnlimitedTimes(). - WithPayload(observertypes.QueryGetKeygenRequest{}). - Return(observertypes.QueryGetKeygenResponse{ - Keygen: &observertypes.Keygen{ - Status: observertypes.KeygenStatus_KeyGenSuccess, - GranteePubkeys: nil, - BlockNumber: 5646, - }}) - - method = "/zetachain.zetacore.observer.Query/TSS" - s.ExpectUnary(method). - UnlimitedTimes(). - WithPayload(observertypes.QueryGetTSSRequest{}). - Return(observertypes.QueryGetTSSResponse{ - TSS: observertypes.TSS{ - TssPubkey: "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", - TssParticipantList: nil, - OperatorAddressList: nil, - FinalizedZetaHeight: 1000, - KeyGenZetaHeight: 900, - }, - }) - - method = "/zetachain.zetacore.observer.Query/CrosschainFlags" - s.ExpectUnary(method). - UnlimitedTimes(). - WithPayload(observertypes.QueryGetCrosschainFlagsRequest{}). - Return(observertypes.QueryGetCrosschainFlagsResponse{CrosschainFlags: observertypes.CrosschainFlags{ - IsInboundEnabled: true, - IsOutboundEnabled: false, - GasPriceIncreaseFlags: nil, - }}) - - method = "/zetachain.zetacore.authority.Query/ChainInfo" - s.ExpectUnary(method). - UnlimitedTimes(). - WithPayload(authoritytypes.QueryGetChainInfoRequest{}). - Return(authoritytypes.QueryGetChainInfoResponse{ - ChainInfo: authoritytypes.ChainInfo{ - Chains: []chains.Chain{ - sample.Chain(1000), - sample.Chain(1001), - sample.Chain(1002), - }, - }, - }) - }, - )(t) - - server.Serve() - defer server.Close() - - address := sdktypes.AccAddress(mocks.TestKeyringPair.PubKey().Address().Bytes()) - client := setupZetacoreClient(t, - withObserverKeys(keys.NewKeysWithKeybase(mocks.NewKeyring(), address, testSigner, "")), - withTendermint(mocks.NewSDKClientWithErr(t, nil, 0)), - ) - - t.Run("zetacore update success", func(t *testing.T) { - cfg := config.New(false) - appContext := zctx.New(cfg, nil, zerolog.Nop()) - err := client.UpdateAppContext(ctx, appContext, zerolog.New(zerolog.NewTestWriter(t))) - require.NoError(t, err) - }) -} - func TestZetacore_PostBlameData(t *testing.T) { ctx := context.Background() From 5631ad7be78477fa0d03cd23d8af59bce653893a Mon Sep 17 00:00:00 2001 From: skosito Date: Tue, 12 Nov 2024 17:08:37 +0000 Subject: [PATCH 06/14] feat: integrate withdraw SPL (#3134) * withdraw spl and e2e test * changelog * fix unit test * e2e test that rent payer creates receiver ata * comments fixes * PR comments fixes * fmt * PR comments * PR comments * fix unit test --- changelog.md | 1 + cmd/zetae2e/local/local.go | 4 +- contrib/localnet/solana/start-solana.sh | 3 +- e2e/e2etests/e2etests.go | 38 +++-- e2e/e2etests/test_solana_whitelist_spl.go | 3 +- e2e/e2etests/test_solana_withdraw.go | 14 +- ...test_solana_withdraw_restricted_address.go | 8 +- e2e/e2etests/test_spl_deposit.go | 16 +- e2e/e2etests/test_spl_deposit_and_call.go | 16 +- e2e/e2etests/test_spl_withdraw.go | 73 +++++++++ ...st_spl_withdraw_and_create_receiver_ata.go | 79 ++++++++++ e2e/runner/runner.go | 7 + e2e/runner/setup_solana.go | 24 +++ e2e/runner/solana.go | 60 +++++-- go.mod | 2 +- go.sum | 4 +- pkg/contracts/solana/gateway.go | 20 ++- pkg/contracts/solana/gateway_message.go | 138 +++++++++++++++- pkg/contracts/solana/instruction.go | 65 ++++++++ zetaclient/chains/solana/observer/observer.go | 2 +- zetaclient/chains/solana/observer/outbound.go | 2 + .../chains/solana/observer/outbound_test.go | 2 +- zetaclient/chains/solana/signer/signer.go | 102 +++++++++++- zetaclient/chains/solana/signer/whitelist.go | 53 ++----- zetaclient/chains/solana/signer/withdraw.go | 45 ++---- .../chains/solana/signer/withdraw_spl.go | 147 ++++++++++++++++++ 26 files changed, 790 insertions(+), 138 deletions(-) create mode 100644 e2e/e2etests/test_spl_withdraw.go create mode 100644 e2e/e2etests/test_spl_withdraw_and_create_receiver_ata.go create mode 100644 zetaclient/chains/solana/signer/withdraw_spl.go diff --git a/changelog.md b/changelog.md index e3c0336caa..ac4e4c351c 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,7 @@ * [2984](https://github.com/zeta-chain/node/pull/2984) - add Whitelist message ability to whitelist SPL tokens on Solana * [3091](https://github.com/zeta-chain/node/pull/3091) - improve build reproducability. `make release{,-build-only}` checksums should now be stable. * [3124](https://github.com/zeta-chain/node/pull/3124) - integrate SPL deposits +* [3134](https://github.com/zeta-chain/node/pull/3134) - integrate SPL tokens withdraw to Solana ### Tests diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index a170929cbb..5507d95014 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -438,9 +438,11 @@ func localE2ETest(cmd *cobra.Command, _ []string) { e2etests.TestSolanaWithdrawRestrictedName, // TODO move under admin tests // https://github.com/zeta-chain/node/issues/3085 - e2etests.TestSolanaWhitelistSPLName, e2etests.TestSPLDepositName, e2etests.TestSPLDepositAndCallName, + e2etests.TestSPLWithdrawName, + e2etests.TestSPLWithdrawAndCreateReceiverAtaName, + e2etests.TestSolanaWhitelistSPLName, } eg.Go(solanaTestRoutine(conf, deployerRunner, verbose, solanaTests...)) } diff --git a/contrib/localnet/solana/start-solana.sh b/contrib/localnet/solana/start-solana.sh index d87e9672ae..73a4564f56 100644 --- a/contrib/localnet/solana/start-solana.sh +++ b/contrib/localnet/solana/start-solana.sh @@ -8,9 +8,10 @@ echo "starting solana test validator..." solana-test-validator & sleep 5 -# airdrop to e2e sol account +# airdrop to e2e sol account and rent payer (used to generate atas for withdraw spl receivers if they don't exist) solana airdrop 100 solana airdrop 100 37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ +solana airdrop 100 C6KPvGDYfNusoE4yfRP21F8wK35bxCBMT69xk4xo3X79 solana program deploy gateway.so diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index 3b5ef2ed00..ee4b8d92ce 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -55,14 +55,16 @@ const ( /* * Solana tests */ - TestSolanaDepositName = "solana_deposit" - TestSolanaWithdrawName = "solana_withdraw" - TestSolanaDepositAndCallName = "solana_deposit_and_call" - TestSolanaDepositAndCallRefundName = "solana_deposit_and_call_refund" - TestSolanaDepositRestrictedName = "solana_deposit_restricted" - TestSolanaWithdrawRestrictedName = "solana_withdraw_restricted" - TestSPLDepositName = "spl_deposit" - TestSPLDepositAndCallName = "spl_deposit_and_call" + TestSolanaDepositName = "solana_deposit" + TestSolanaWithdrawName = "solana_withdraw" + TestSolanaDepositAndCallName = "solana_deposit_and_call" + TestSolanaDepositAndCallRefundName = "solana_deposit_and_call_refund" + TestSolanaDepositRestrictedName = "solana_deposit_restricted" + TestSolanaWithdrawRestrictedName = "solana_withdraw_restricted" + TestSPLDepositName = "spl_deposit" + TestSPLDepositAndCallName = "spl_deposit_and_call" + TestSPLWithdrawName = "spl_withdraw" + TestSPLWithdrawAndCreateReceiverAtaName = "spl_withdraw_and_create_receiver_ata" /** * TON tests @@ -434,6 +436,22 @@ var AllE2ETests = []runner.E2ETest{ }, TestSolanaDepositAndCall, ), + runner.NewE2ETest( + TestSPLWithdrawName, + "withdraw SPL from ZEVM", + []runner.ArgDefinition{ + {Description: "amount in spl tokens", DefaultValue: "1000000"}, + }, + TestSPLWithdraw, + ), + runner.NewE2ETest( + TestSPLWithdrawAndCreateReceiverAtaName, + "withdraw SPL from ZEVM and create receiver ata", + []runner.ArgDefinition{ + {Description: "amount in spl tokens", DefaultValue: "1000000"}, + }, + TestSPLWithdrawAndCreateReceiverAta, + ), runner.NewE2ETest( TestSolanaDepositAndCallRefundName, "deposit SOL into ZEVM and call a contract that reverts; should refund", @@ -470,7 +488,7 @@ var AllE2ETests = []runner.E2ETest{ TestSPLDepositName, "deposit SPL into ZEVM", []runner.ArgDefinition{ - {Description: "amount of spl tokens", DefaultValue: "500000"}, + {Description: "amount of spl tokens", DefaultValue: "12000000"}, }, TestSPLDeposit, ), @@ -478,7 +496,7 @@ var AllE2ETests = []runner.E2ETest{ TestSPLDepositAndCallName, "deposit SPL into ZEVM and call", []runner.ArgDefinition{ - {Description: "amount of spl tokens", DefaultValue: "500000"}, + {Description: "amount of spl tokens", DefaultValue: "12000000"}, }, TestSPLDepositAndCall, ), diff --git a/e2e/e2etests/test_solana_whitelist_spl.go b/e2e/e2etests/test_solana_whitelist_spl.go index fadff22805..c07bdabb12 100644 --- a/e2e/e2etests/test_solana_whitelist_spl.go +++ b/e2e/e2etests/test_solana_whitelist_spl.go @@ -16,8 +16,7 @@ func TestSolanaWhitelistSPL(r *runner.E2ERunner, _ []string) { r.Logger.Info("Deploying new SPL") // load deployer private key - privkey, err := solana.PrivateKeyFromBase58(r.Account.SolanaPrivateKey.String()) - require.NoError(r, err) + privkey := r.GetSolanaPrivKey() // deploy SPL token, but don't whitelist in gateway spl := r.DeploySPL(&privkey, false) diff --git a/e2e/e2etests/test_solana_withdraw.go b/e2e/e2etests/test_solana_withdraw.go index c7f6ccc58b..d8e427a66c 100644 --- a/e2e/e2etests/test_solana_withdraw.go +++ b/e2e/e2etests/test_solana_withdraw.go @@ -8,6 +8,8 @@ import ( "github.com/stretchr/testify/require" "github.com/zeta-chain/node/e2e/runner" + "github.com/zeta-chain/node/e2e/utils" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" ) func TestSolanaWithdraw(r *runner.E2ERunner, args []string) { @@ -28,15 +30,19 @@ func TestSolanaWithdraw(r *runner.E2ERunner, args []string) { r, -1, withdrawAmount.Cmp(approvedAmount), - "Withdrawal amount must be less than the approved amount (1e9)", + "Withdrawal amount must be less than the approved amount: %v", + approvedAmount, ) // load deployer private key - privkey, err := solana.PrivateKeyFromBase58(r.Account.SolanaPrivateKey.String()) - require.NoError(r, err) + privkey := r.GetSolanaPrivKey() // withdraw - r.WithdrawSOLZRC20(privkey.PublicKey(), withdrawAmount, approvedAmount) + tx := r.WithdrawSOLZRC20(privkey.PublicKey(), withdrawAmount, approvedAmount) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_OutboundMined) // get ERC20 SOL balance after withdraw balanceAfter, err := r.SOLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) diff --git a/e2e/e2etests/test_solana_withdraw_restricted_address.go b/e2e/e2etests/test_solana_withdraw_restricted_address.go index e7964f3702..af2027ac98 100644 --- a/e2e/e2etests/test_solana_withdraw_restricted_address.go +++ b/e2e/e2etests/test_solana_withdraw_restricted_address.go @@ -8,7 +8,9 @@ import ( "github.com/stretchr/testify/require" "github.com/zeta-chain/node/e2e/runner" + "github.com/zeta-chain/node/e2e/utils" "github.com/zeta-chain/node/pkg/chains" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" ) func TestSolanaWithdrawRestricted(r *runner.E2ERunner, args []string) { @@ -29,7 +31,11 @@ func TestSolanaWithdrawRestricted(r *runner.E2ERunner, args []string) { ) // withdraw - cctx := r.WithdrawSOLZRC20(receiverRestricted, withdrawAmount, approvedAmount) + tx := r.WithdrawSOLZRC20(receiverRestricted, withdrawAmount, approvedAmount) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_OutboundMined) // the cctx should be cancelled with zero value verifySolanaWithdrawalAmountFromCCTX(r, cctx, 0) diff --git a/e2e/e2etests/test_spl_deposit.go b/e2e/e2etests/test_spl_deposit.go index ee5013d16c..e20ff5879a 100644 --- a/e2e/e2etests/test_spl_deposit.go +++ b/e2e/e2etests/test_spl_deposit.go @@ -4,7 +4,6 @@ import ( "math/big" "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" "github.com/stretchr/testify/require" @@ -18,18 +17,17 @@ func TestSPLDeposit(r *runner.E2ERunner, args []string) { amount := parseInt(r, args[0]) // load deployer private key - privKey, err := solana.PrivateKeyFromBase58(r.Account.SolanaPrivateKey.String()) - require.NoError(r, err) + privKey := r.GetSolanaPrivKey() // get SPL balance for pda and sender atas pda := r.ComputePdaAddress() - pdaAta := r.FindOrCreateAssociatedTokenAccount(privKey, pda, r.SPLAddr) + pdaAta := r.ResolveSolanaATA(privKey, pda, r.SPLAddr) - pdaBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, pdaAta, rpc.CommitmentConfirmed) + pdaBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, pdaAta, rpc.CommitmentFinalized) require.NoError(r, err) - senderAta := r.FindOrCreateAssociatedTokenAccount(privKey, privKey.PublicKey(), r.SPLAddr) - senderBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, senderAta, rpc.CommitmentConfirmed) + senderAta := r.ResolveSolanaATA(privKey, privKey.PublicKey(), r.SPLAddr) + senderBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, senderAta, rpc.CommitmentFinalized) require.NoError(r, err) // get zrc20 balance for recipient @@ -46,10 +44,10 @@ func TestSPLDeposit(r *runner.E2ERunner, args []string) { utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_OutboundMined) // verify balances are updated - pdaBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, pdaAta, rpc.CommitmentConfirmed) + pdaBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, pdaAta, rpc.CommitmentFinalized) require.NoError(r, err) - senderBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, senderAta, rpc.CommitmentConfirmed) + senderBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, senderAta, rpc.CommitmentFinalized) require.NoError(r, err) zrc20BalanceAfter, err := r.SPLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) diff --git a/e2e/e2etests/test_spl_deposit_and_call.go b/e2e/e2etests/test_spl_deposit_and_call.go index cdfc94daa1..d7e11cd999 100644 --- a/e2e/e2etests/test_spl_deposit_and_call.go +++ b/e2e/e2etests/test_spl_deposit_and_call.go @@ -4,7 +4,6 @@ import ( "math/big" "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" "github.com/stretchr/testify/require" @@ -24,18 +23,17 @@ func TestSPLDepositAndCall(r *runner.E2ERunner, args []string) { r.Logger.Info("Example contract deployed at: %s", contractAddr.String()) // load deployer private key - privKey, err := solana.PrivateKeyFromBase58(r.Account.SolanaPrivateKey.String()) - require.NoError(r, err) + privKey := r.GetSolanaPrivKey() // get SPL balance for pda and sender atas pda := r.ComputePdaAddress() - pdaAta := r.FindOrCreateAssociatedTokenAccount(privKey, pda, r.SPLAddr) + pdaAta := r.ResolveSolanaATA(privKey, pda, r.SPLAddr) - pdaBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, pdaAta, rpc.CommitmentConfirmed) + pdaBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, pdaAta, rpc.CommitmentFinalized) require.NoError(r, err) - senderAta := r.FindOrCreateAssociatedTokenAccount(privKey, privKey.PublicKey(), r.SPLAddr) - senderBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, senderAta, rpc.CommitmentConfirmed) + senderAta := r.ResolveSolanaATA(privKey, privKey.PublicKey(), r.SPLAddr) + senderBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, senderAta, rpc.CommitmentFinalized) require.NoError(r, err) // get zrc20 balance for recipient @@ -56,10 +54,10 @@ func TestSPLDepositAndCall(r *runner.E2ERunner, args []string) { utils.MustHaveCalledExampleContract(r, contract, big.NewInt(int64(amount))) // verify balances are updated - pdaBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, pdaAta, rpc.CommitmentConfirmed) + pdaBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, pdaAta, rpc.CommitmentFinalized) require.NoError(r, err) - senderBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, senderAta, rpc.CommitmentConfirmed) + senderBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, senderAta, rpc.CommitmentFinalized) require.NoError(r, err) zrc20BalanceAfter, err := r.SPLZRC20.BalanceOf(&bind.CallOpts{}, contractAddr) diff --git a/e2e/e2etests/test_spl_withdraw.go b/e2e/e2etests/test_spl_withdraw.go new file mode 100644 index 0000000000..2af4ddfd94 --- /dev/null +++ b/e2e/e2etests/test_spl_withdraw.go @@ -0,0 +1,73 @@ +package e2etests + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/gagliardetto/solana-go" + "github.com/gagliardetto/solana-go/rpc" + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/node/e2e/runner" + "github.com/zeta-chain/node/e2e/utils" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" +) + +func TestSPLWithdraw(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + withdrawAmount := parseBigInt(r, args[0]) + + // get SPL ZRC20 balance before withdraw + zrc20BalanceBefore, err := r.SPLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) + require.NoError(r, err) + r.Logger.Info("runner balance of SPL before withdraw: %d", zrc20BalanceBefore) + + require.Equal(r, 1, zrc20BalanceBefore.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) + require.Equal( + r, + -1, + withdrawAmount.Cmp(approvedAmount), + "Withdrawal amount must be less than the %v", + approvedAmount, + ) + + // load deployer private key + privkey := r.GetSolanaPrivKey() + + // get receiver ata balance before withdraw + receiverAta := r.ResolveSolanaATA(privkey, privkey.PublicKey(), r.SPLAddr) + receiverBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, receiverAta, rpc.CommitmentFinalized) + require.NoError(r, err) + r.Logger.Info("receiver balance of SPL before withdraw: %s", receiverBalanceBefore.Value.Amount) + + // withdraw + tx := r.WithdrawSPLZRC20(privkey.PublicKey(), withdrawAmount, approvedAmount) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_OutboundMined) + + // get SPL ZRC20 balance after withdraw + zrc20BalanceAfter, err := r.SPLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) + require.NoError(r, err) + r.Logger.Info("runner balance of SPL after withdraw: %d", zrc20BalanceAfter) + + // verify balances are updated + receiverBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, receiverAta, rpc.CommitmentFinalized) + require.NoError(r, err) + r.Logger.Info("receiver balance of SPL after withdraw: %s", receiverBalanceAfter.Value.Amount) + + // verify amount is added to receiver ata + require.EqualValues( + r, + new(big.Int).Add(withdrawAmount, parseBigInt(r, receiverBalanceBefore.Value.Amount)).String(), + parseBigInt(r, receiverBalanceAfter.Value.Amount).String(), + ) + + // verify amount is subtracted on zrc20 + require.EqualValues(r, new(big.Int).Sub(zrc20BalanceBefore, withdrawAmount).String(), zrc20BalanceAfter.String()) +} diff --git a/e2e/e2etests/test_spl_withdraw_and_create_receiver_ata.go b/e2e/e2etests/test_spl_withdraw_and_create_receiver_ata.go new file mode 100644 index 0000000000..51196c8a46 --- /dev/null +++ b/e2e/e2etests/test_spl_withdraw_and_create_receiver_ata.go @@ -0,0 +1,79 @@ +package e2etests + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/gagliardetto/solana-go" + "github.com/gagliardetto/solana-go/rpc" + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/node/e2e/runner" + "github.com/zeta-chain/node/e2e/utils" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" +) + +// TestSPLWithdrawAndCreateReceiverAta withdraws spl, but letting gateway to create receiver ata using rent payer +// instead of providing receiver that has it already created +func TestSPLWithdrawAndCreateReceiverAta(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + withdrawAmount := parseBigInt(r, args[0]) + + // get SPL ZRC20 balance before withdraw + zrc20BalanceBefore, err := r.SPLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) + require.NoError(r, err) + r.Logger.Info("runner balance of SPL before withdraw: %d", zrc20BalanceBefore) + + require.Equal(r, 1, zrc20BalanceBefore.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) + require.Equal( + r, + -1, + withdrawAmount.Cmp(approvedAmount), + "Withdrawal amount must be less than the %v", + approvedAmount, + ) + + // create new priv key, with empty ata + receiverPrivKey, err := solana.NewRandomPrivateKey() + require.NoError(r, err) + + // verify receiver ata account doesn't exist + receiverAta, _, err := solana.FindAssociatedTokenAddress(receiverPrivKey.PublicKey(), r.SPLAddr) + require.NoError(r, err) + + receiverAtaAcc, err := r.SolanaClient.GetAccountInfo(r.Ctx, receiverAta) + require.Error(r, err) + require.Nil(r, receiverAtaAcc) + + // withdraw + tx := r.WithdrawSPLZRC20(receiverPrivKey.PublicKey(), withdrawAmount, approvedAmount) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_OutboundMined) + + // get SPL ZRC20 balance after withdraw + zrc20BalanceAfter, err := r.SPLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) + require.NoError(r, err) + r.Logger.Info("runner balance of SPL after withdraw: %d", zrc20BalanceAfter) + + // verify receiver ata was created + receiverAtaAcc, err = r.SolanaClient.GetAccountInfo(r.Ctx, receiverAta) + require.NoError(r, err) + require.NotNil(r, receiverAtaAcc) + + // verify balances are updated + receiverBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, receiverAta, rpc.CommitmentFinalized) + require.NoError(r, err) + r.Logger.Info("receiver balance of SPL after withdraw: %s", receiverBalanceAfter.Value.Amount) + + // verify amount is added to receiver ata + require.EqualValues(r, withdrawAmount.String(), parseBigInt(r, receiverBalanceAfter.Value.Amount).String()) + + // verify amount is subtracted on zrc20 + require.EqualValues(r, new(big.Int).Sub(zrc20BalanceBefore, withdrawAmount).String(), zrc20BalanceAfter.String()) +} diff --git a/e2e/runner/runner.go b/e2e/runner/runner.go index ef68abe6a8..f117758c28 100644 --- a/e2e/runner/runner.go +++ b/e2e/runner/runner.go @@ -20,6 +20,7 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" + "github.com/stretchr/testify/require" "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" @@ -434,3 +435,9 @@ func (r *E2ERunner) requireTxSuccessful(receipt *ethtypes.Receipt, msgAndArgs .. func (r *E2ERunner) EVMAddress() ethcommon.Address { return r.Account.EVMAddress() } + +func (r *E2ERunner) GetSolanaPrivKey() solana.PrivateKey { + privkey, err := solana.PrivateKeyFromBase58(r.Account.SolanaPrivateKey.String()) + require.NoError(r, err) + return privkey +} diff --git a/e2e/runner/setup_solana.go b/e2e/runner/setup_solana.go index a7589d6af1..1a46326d02 100644 --- a/e2e/runner/setup_solana.go +++ b/e2e/runner/setup_solana.go @@ -85,6 +85,30 @@ func (r *E2ERunner) SetupSolana(deployerPrivateKey string) { require.NoError(r, err) r.Logger.Info("initial PDA balance: %d lamports", balance.Value) + // initialize rent payer + var instRentPayer solana.GenericInstruction + rentPayerPdaComputed := r.SolanaRentPayerPDA() + + // create 'initialize_rent_payer' instruction + accountSlice = []*solana.AccountMeta{} + accountSlice = append(accountSlice, solana.Meta(rentPayerPdaComputed).WRITE()) + accountSlice = append(accountSlice, solana.Meta(privkey.PublicKey()).WRITE().SIGNER()) + accountSlice = append(accountSlice, solana.Meta(solana.SystemProgramID)) + instRentPayer.ProgID = r.GatewayProgram + instRentPayer.AccountValues = accountSlice + + instRentPayer.DataBytes, err = borsh.Serialize(solanacontracts.InitializeRentPayerParams{ + Discriminator: solanacontracts.DiscriminatorInitializeRentPayer, + }) + require.NoError(r, err) + + // create and sign the transaction + signedTx = r.CreateSignedTransaction([]solana.Instruction{&instRentPayer}, privkey, []solana.PrivateKey{}) + + // broadcast the transaction and wait for finalization + _, out = r.BroadcastTxSync(signedTx) + r.Logger.Info("initialize_rent_payer logs: %v", out.Meta.LogMessages) + err = r.ensureSolanaChainParams() require.NoError(r, err) diff --git a/e2e/runner/solana.go b/e2e/runner/solana.go index 542968938d..d395c60d76 100644 --- a/e2e/runner/solana.go +++ b/e2e/runner/solana.go @@ -5,6 +5,7 @@ import ( "time" ethcommon "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/gagliardetto/solana-go" associatedtokenaccount "github.com/gagliardetto/solana-go/programs/associated-token-account" "github.com/gagliardetto/solana-go/programs/system" @@ -15,7 +16,6 @@ import ( "github.com/zeta-chain/node/e2e/utils" solanacontract "github.com/zeta-chain/node/pkg/contracts/solana" - crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" ) // ComputePdaAddress computes the PDA address for the gateway program @@ -30,6 +30,18 @@ func (r *E2ERunner) ComputePdaAddress() solana.PublicKey { return pdaComputed } +// SolanaRentPayerPDA computes the rent payer PDA (Program Derived Address) address for the gateway program +func (r *E2ERunner) SolanaRentPayerPDA() solana.PublicKey { + seed := []byte(solanacontract.RentPayerPDASeed) + GatewayProgramID := solana.MustPublicKeyFromBase58(solanacontract.SolanaGatewayProgramID) + pdaComputed, bump, err := solana.FindProgramAddress([][]byte{seed}, GatewayProgramID) + require.NoError(r, err) + + r.Logger.Info("computed rent payer pda: %s, bump %d\n", pdaComputed, bump) + + return pdaComputed +} + // CreateDepositInstruction creates a 'deposit' instruction func (r *E2ERunner) CreateDepositInstruction( signer solana.PublicKey, @@ -147,8 +159,8 @@ func (r *E2ERunner) CreateSignedTransaction( return tx } -// FindOrCreateAssociatedTokenAccount checks if ata exists, and if not creates it -func (r *E2ERunner) FindOrCreateAssociatedTokenAccount( +// ResolveSolanaATA finds or creates SOL associated token account +func (r *E2ERunner) ResolveSolanaATA( payer solana.PrivateKey, owner solana.PublicKey, tokenAccount solana.PublicKey, @@ -184,10 +196,10 @@ func (r *E2ERunner) SPLDepositAndCall( ) solana.Signature { // ata for pda pda := r.ComputePdaAddress() - pdaAta := r.FindOrCreateAssociatedTokenAccount(*privateKey, pda, tokenAccount) + pdaAta := r.ResolveSolanaATA(*privateKey, pda, tokenAccount) // deployer ata - ata := r.FindOrCreateAssociatedTokenAccount(*privateKey, privateKey.PublicKey(), tokenAccount) + ata := r.ResolveSolanaATA(*privateKey, privateKey.PublicKey(), tokenAccount) // deposit spl seed := [][]byte{[]byte("whitelist"), tokenAccount.Bytes()} @@ -248,9 +260,9 @@ func (r *E2ERunner) DeploySPL(privateKey *solana.PrivateKey, whitelist bool) *so r.Logger.Info("create spl logs: %v", out.Meta.LogMessages) // minting some tokens to deployer for testing - ata := r.FindOrCreateAssociatedTokenAccount(*privateKey, privateKey.PublicKey(), tokenAccount.PublicKey()) + ata := r.ResolveSolanaATA(*privateKey, privateKey.PublicKey(), tokenAccount.PublicKey()) - mintToInstruction := token.NewMintToInstruction(uint64(1_000_000), tokenAccount.PublicKey(), ata, privateKey.PublicKey(), []solana.PublicKey{}). + mintToInstruction := token.NewMintToInstruction(uint64(1_000_000_000), tokenAccount.PublicKey(), ata, privateKey.PublicKey(), []solana.PublicKey{}). Build() signedTx = r.CreateSignedTransaction( []solana.Instruction{mintToInstruction}, @@ -333,8 +345,7 @@ func (r *E2ERunner) SOLDepositAndCall( ) solana.Signature { // if signer is not provided, use the runner account as default if signerPrivKey == nil { - privkey, err := solana.PrivateKeyFromBase58(r.Account.SolanaPrivateKey.String()) - require.NoError(r, err) + privkey := r.GetSolanaPrivKey() signerPrivKey = &privkey } @@ -356,7 +367,7 @@ func (r *E2ERunner) WithdrawSOLZRC20( to solana.PublicKey, amount *big.Int, approveAmount *big.Int, -) *crosschaintypes.CrossChainTx { +) *ethtypes.Transaction { // approve tx, err := r.SOLZRC20.Approve(r.ZEVMAuth, r.SOLZRC20Addr, approveAmount) require.NoError(r, err) @@ -373,9 +384,30 @@ func (r *E2ERunner) WithdrawSOLZRC20( utils.RequireTxSuccessful(r, receipt, "withdraw") r.Logger.Info("Receipt txhash %s status %d", receipt.TxHash, receipt.Status) - // wait for the cctx to be mined - cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) - utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_OutboundMined) + return tx +} + +// WithdrawSPLZRC20 withdraws an amount of ZRC20 SPL tokens +func (r *E2ERunner) WithdrawSPLZRC20( + to solana.PublicKey, + amount *big.Int, + approveAmount *big.Int, +) *ethtypes.Transaction { + // approve splzrc20 to spend gas tokens to pay gas fee + tx, err := r.SOLZRC20.Approve(r.ZEVMAuth, r.SPLZRC20Addr, approveAmount) + require.NoError(r, err) + receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) + utils.RequireTxSuccessful(r, receipt, "approve") + + // withdraw + tx, err = r.SPLZRC20.Withdraw(r.ZEVMAuth, []byte(to.String()), amount) + require.NoError(r, err) + r.Logger.EVMTransaction(*tx, "withdraw") + + // wait for tx receipt + receipt = utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) + utils.RequireTxSuccessful(r, receipt, "withdraw") + r.Logger.Info("Receipt txhash %s status %d", receipt.TxHash, receipt.Status) - return cctx + return tx } diff --git a/go.mod b/go.mod index 795cfb4528..494398e08c 100644 --- a/go.mod +++ b/go.mod @@ -318,7 +318,7 @@ require ( github.com/montanaflynn/stats v0.7.1 github.com/showa-93/go-mask v0.6.2 github.com/tonkeeper/tongo v1.9.3 - github.com/zeta-chain/protocol-contracts-solana/go-idl v0.0.0-20241025181051-d8d49e4fc85b + github.com/zeta-chain/protocol-contracts-solana/go-idl v0.0.0-20241108171442-e48d82f94892 ) require ( diff --git a/go.sum b/go.sum index 4297217034..97df1129f4 100644 --- a/go.sum +++ b/go.sum @@ -1531,8 +1531,8 @@ github.com/zeta-chain/go-tss v0.0.0-20241031223543-18765295f992 h1:jpfOoQGHQo29C github.com/zeta-chain/go-tss v0.0.0-20241031223543-18765295f992/go.mod h1:nqelgf4HKkqlXaVg8X38a61WfyYB+ivCt6nnjoTIgCc= 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/protocol-contracts-solana/go-idl v0.0.0-20241025181051-d8d49e4fc85b h1:w4YVBbWxk9TI+7HM8hTvK66IgOo5XvEFsmH7n6WgW50= -github.com/zeta-chain/protocol-contracts-solana/go-idl v0.0.0-20241025181051-d8d49e4fc85b/go.mod h1:DcDY828o773soiU/h0XpC+naxitrIMFVZqEvq/EJxMA= +github.com/zeta-chain/protocol-contracts-solana/go-idl v0.0.0-20241108171442-e48d82f94892 h1:oI5qCrw2SXDf2a2UYAn0tpaKHbKpJcR+XDtceyY00wE= +github.com/zeta-chain/protocol-contracts-solana/go-idl v0.0.0-20241108171442-e48d82f94892/go.mod h1:DcDY828o773soiU/h0XpC+naxitrIMFVZqEvq/EJxMA= 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.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= diff --git a/pkg/contracts/solana/gateway.go b/pkg/contracts/solana/gateway.go index 12e3a10aa8..bf674aa081 100644 --- a/pkg/contracts/solana/gateway.go +++ b/pkg/contracts/solana/gateway.go @@ -14,6 +14,9 @@ const ( // PDASeed is the seed for the Solana gateway program derived address PDASeed = "meta" + // RentPayerPDASeed is the seed for the Solana gateway program derived address + RentPayerPDASeed = "rent-payer" + // AccountsNumberOfDeposit is the number of accounts required for Solana gateway deposit instruction // [signer, pda, system_program] accountsNumDeposit = 3 @@ -26,6 +29,8 @@ const ( var ( // DiscriminatorInitialize returns the discriminator for Solana gateway 'initialize' instruction DiscriminatorInitialize = idlgateway.IDLGateway.GetDiscriminator("initialize") + // DiscriminatorInitializeRentPayer returns the discriminator for Solana gateway 'initialize_rent_payer' instruction + DiscriminatorInitializeRentPayer = idlgateway.IDLGateway.GetDiscriminator("initialize_rent_payer") // DiscriminatorDeposit returns the discriminator for Solana gateway 'deposit' instruction DiscriminatorDeposit = idlgateway.IDLGateway.GetDiscriminator("deposit") // DiscriminatorDepositSPL returns the discriminator for Solana gateway 'deposit_spl_token' instruction @@ -38,12 +43,12 @@ var ( DiscriminatorWhitelistSplMint = idlgateway.IDLGateway.GetDiscriminator("whitelist_spl_mint") ) -// ParseGatewayAddressAndPda parses the gateway id and program derived address from the given string -func ParseGatewayIDAndPda(address string) (solana.PublicKey, solana.PublicKey, error) { +// ParseGatewayWithPDA parses the gateway id and program derived address from the given string +func ParseGatewayWithPDA(gatewayAddress string) (solana.PublicKey, solana.PublicKey, error) { var gatewayID, pda solana.PublicKey // decode gateway address - gatewayID, err := solana.PublicKeyFromBase58(address) + gatewayID, err := solana.PublicKeyFromBase58(gatewayAddress) if err != nil { return gatewayID, pda, errors.Wrap(err, "unable to decode address") } @@ -54,3 +59,12 @@ func ParseGatewayIDAndPda(address string) (solana.PublicKey, solana.PublicKey, e return gatewayID, pda, err } + +// ParseRentPayerPda parses the rent payer program derived address from the given string +func RentPayerPDA(gateway solana.PublicKey) (solana.PublicKey, error) { + var rentPayerPda solana.PublicKey + seed := []byte(RentPayerPDASeed) + rentPayerPda, _, err := solana.FindProgramAddress([][]byte{seed}, gateway) + + return rentPayerPda, err +} diff --git a/pkg/contracts/solana/gateway_message.go b/pkg/contracts/solana/gateway_message.go index 1c8abaca23..bd8cbb51af 100644 --- a/pkg/contracts/solana/gateway_message.go +++ b/pkg/contracts/solana/gateway_message.go @@ -8,18 +8,18 @@ import ( "github.com/gagliardetto/solana-go" ) -// MsgWithdraw is the message for the Solana gateway withdraw/withdraw_spl instruction +// MsgWithdraw is the message for the Solana gateway withdraw instruction type MsgWithdraw struct { // chainID is the chain ID of Solana chain chainID uint64 - // Nonce is the nonce for the withdraw/withdraw_spl + // Nonce is the nonce for the withdraw nonce uint64 - // amount is the lamports amount for the withdraw/withdraw_spl + // amount is the lamports amount for the withdraw amount uint64 - // To is the recipient address for the withdraw/withdraw_spl + // To is the recipient address for the withdraw to solana.PublicKey // signature is the signature of the message @@ -108,6 +108,136 @@ func (msg *MsgWithdraw) Signer() (common.Address, error) { return RecoverSigner(msgHash[:], msgSig[:]) } +// MsgWithdrawSPL is the message for the Solana gateway withdraw_spl instruction +type MsgWithdrawSPL struct { + // chainID is the chain ID of Solana chain + chainID uint64 + + // Nonce is the nonce for the withdraw_spl + nonce uint64 + + // amount is the lamports amount for the withdraw_spl + amount uint64 + + // tokenAccount is the address for the spl token + tokenAccount solana.PublicKey + + // decimals of spl token + decimals uint8 + + // to is the recipient address for the withdraw_spl + to solana.PublicKey + + // recipientAta is the recipient associated token account for the withdraw_spl + recipientAta solana.PublicKey + + // signature is the signature of the message + signature [65]byte +} + +// NewMsgWithdrawSPL returns a new withdraw spl message +func NewMsgWithdrawSPL( + chainID, nonce, amount uint64, + decimals uint8, + tokenAccount, to, toAta solana.PublicKey, +) *MsgWithdrawSPL { + return &MsgWithdrawSPL{ + chainID: chainID, + nonce: nonce, + amount: amount, + to: to, + recipientAta: toAta, + tokenAccount: tokenAccount, + decimals: decimals, + } +} + +// ChainID returns the chain ID of the message +func (msg *MsgWithdrawSPL) ChainID() uint64 { + return msg.chainID +} + +// Nonce returns the nonce of the message +func (msg *MsgWithdrawSPL) Nonce() uint64 { + return msg.nonce +} + +// Amount returns the amount of the message +func (msg *MsgWithdrawSPL) Amount() uint64 { + return msg.amount +} + +// To returns the recipient address of the message +func (msg *MsgWithdrawSPL) To() solana.PublicKey { + return msg.to +} + +func (msg *MsgWithdrawSPL) RecipientAta() solana.PublicKey { + return msg.recipientAta +} + +func (msg *MsgWithdrawSPL) TokenAccount() solana.PublicKey { + return msg.tokenAccount +} + +func (msg *MsgWithdrawSPL) Decimals() uint8 { + return msg.decimals +} + +// Hash packs the withdraw spl message and computes the hash +func (msg *MsgWithdrawSPL) Hash() [32]byte { + var message []byte + buff := make([]byte, 8) + + message = append(message, []byte("withdraw_spl_token")...) + + binary.BigEndian.PutUint64(buff, msg.chainID) + message = append(message, buff...) + + binary.BigEndian.PutUint64(buff, msg.nonce) + message = append(message, buff...) + + binary.BigEndian.PutUint64(buff, msg.amount) + message = append(message, buff...) + + message = append(message, msg.tokenAccount.Bytes()...) + + message = append(message, msg.recipientAta.Bytes()...) + + return crypto.Keccak256Hash(message) +} + +// SetSignature attaches the signature to the message +func (msg *MsgWithdrawSPL) SetSignature(signature [65]byte) *MsgWithdrawSPL { + msg.signature = signature + return msg +} + +// SigRSV returns the full 65-byte [R+S+V] signature +func (msg *MsgWithdrawSPL) SigRSV() [65]byte { + return msg.signature +} + +// SigRS returns the 64-byte [R+S] core part of the signature +func (msg *MsgWithdrawSPL) SigRS() [64]byte { + var sig [64]byte + copy(sig[:], msg.signature[:64]) + return sig +} + +// SigV returns the V part (recovery ID) of the signature +func (msg *MsgWithdrawSPL) SigV() uint8 { + return msg.signature[64] +} + +// Signer returns the signer of the message +func (msg *MsgWithdrawSPL) Signer() (common.Address, error) { + msgHash := msg.Hash() + msgSig := msg.SigRSV() + + return RecoverSigner(msgHash[:], msgSig[:]) +} + // MsgWhitelist is the message for the Solana gateway whitelist_spl_mint instruction type MsgWhitelist struct { // whitelistCandidate is the SPL token to be whitelisted in gateway program diff --git a/pkg/contracts/solana/instruction.go b/pkg/contracts/solana/instruction.go index 65b6e6e4c3..44aa80c16b 100644 --- a/pkg/contracts/solana/instruction.go +++ b/pkg/contracts/solana/instruction.go @@ -22,6 +22,12 @@ type InitializeParams struct { ChainID uint64 } +// InitializeRentPayerParams contains the parameters for a gateway initialize_rent_payer instruction +type InitializeRentPayerParams struct { + // Discriminator is the unique identifier for the initialize_rent_payer instruction + Discriminator [8]byte +} + // DepositInstructionParams contains the parameters for a gateway deposit instruction type DepositInstructionParams struct { // Discriminator is the unique identifier for the deposit instruction @@ -118,6 +124,65 @@ func ParseInstructionWithdraw(instruction solana.CompiledInstruction) (*Withdraw return inst, nil } +type WithdrawSPLInstructionParams struct { + // Discriminator is the unique identifier for the withdraw instruction + Discriminator [8]byte + + Decimals uint8 + + // Amount is the lamports amount for the withdraw + Amount uint64 + + // Signature is the ECDSA signature (by TSS) for the withdraw + Signature [64]byte + + // RecoveryID is the recovery ID used to recover the public key from ECDSA signature + RecoveryID uint8 + + // MessageHash is the hash of the message signed by TSS + MessageHash [32]byte + + // Nonce is the nonce for the withdraw + Nonce uint64 +} + +// Signer returns the signer of the signature contained +func (inst *WithdrawSPLInstructionParams) Signer() (signer common.Address, err error) { + var signature [65]byte + copy(signature[:], inst.Signature[:64]) + signature[64] = inst.RecoveryID + + return RecoverSigner(inst.MessageHash[:], signature[:]) +} + +// GatewayNonce returns the nonce of the instruction +func (inst *WithdrawSPLInstructionParams) GatewayNonce() uint64 { + return inst.Nonce +} + +// TokenAmount returns the amount of the instruction +func (inst *WithdrawSPLInstructionParams) TokenAmount() uint64 { + return inst.Amount +} + +// ParseInstructionWithdraw tries to parse the instruction as a 'withdraw'. +// It returns nil if the instruction can't be parsed as a 'withdraw'. +func ParseInstructionWithdrawSPL(instruction solana.CompiledInstruction) (*WithdrawSPLInstructionParams, error) { + // try deserializing instruction as a 'withdraw' + inst := &WithdrawSPLInstructionParams{} + err := borsh.Deserialize(inst, instruction.Data) + if err != nil { + return nil, errors.Wrap(err, "error deserializing instruction") + } + + // check the discriminator to ensure it's a 'withdraw' instruction + if inst.Discriminator != DiscriminatorWithdrawSPL { + return nil, fmt.Errorf("not a withdraw instruction: %v", inst.Discriminator) + } + + return inst, nil +} + // RecoverSigner recover the ECDSA signer from given message hash and signature func RecoverSigner(msgHash []byte, msgSig []byte) (signer common.Address, err error) { // recover the public key diff --git a/zetaclient/chains/solana/observer/observer.go b/zetaclient/chains/solana/observer/observer.go index 0548fcd6d3..6187ca2c39 100644 --- a/zetaclient/chains/solana/observer/observer.go +++ b/zetaclient/chains/solana/observer/observer.go @@ -67,7 +67,7 @@ func NewObserver( } // parse gateway ID and PDA - gatewayID, pda, err := contracts.ParseGatewayIDAndPda(chainParams.GatewayAddress) + gatewayID, pda, err := contracts.ParseGatewayWithPDA(chainParams.GatewayAddress) if err != nil { return nil, errors.Wrapf(err, "cannot parse gateway address %s", chainParams.GatewayAddress) } diff --git a/zetaclient/chains/solana/observer/outbound.go b/zetaclient/chains/solana/observer/outbound.go index 60bd70bec7..2ed98575c4 100644 --- a/zetaclient/chains/solana/observer/outbound.go +++ b/zetaclient/chains/solana/observer/outbound.go @@ -356,6 +356,8 @@ func ParseGatewayInstruction( return contracts.ParseInstructionWithdraw(instruction) case coin.CoinType_Cmd: return contracts.ParseInstructionWhitelist(instruction) + case coin.CoinType_ERC20: + return contracts.ParseInstructionWithdrawSPL(instruction) default: return nil, fmt.Errorf("unsupported outbound coin type %s", coinType) } diff --git a/zetaclient/chains/solana/observer/outbound_test.go b/zetaclient/chains/solana/observer/outbound_test.go index 73af8da573..bdda96c451 100644 --- a/zetaclient/chains/solana/observer/outbound_test.go +++ b/zetaclient/chains/solana/observer/outbound_test.go @@ -234,7 +234,7 @@ func Test_ParseGatewayInstruction(t *testing.T) { // load and unmarshal archived transaction txResult := testutils.LoadSolanaOutboundTxResult(t, TestDataDir, chain.ChainId, txHash) - inst, err := observer.ParseGatewayInstruction(txResult, gatewayID, coin.CoinType_ERC20) + inst, err := observer.ParseGatewayInstruction(txResult, gatewayID, coin.CoinType_Zeta) require.ErrorContains(t, err, "unsupported outbound coin type") require.Nil(t, inst) }) diff --git a/zetaclient/chains/solana/signer/signer.go b/zetaclient/chains/solana/signer/signer.go index 9100f5c628..7405ddaf87 100644 --- a/zetaclient/chains/solana/signer/signer.go +++ b/zetaclient/chains/solana/signer/signer.go @@ -7,7 +7,9 @@ import ( "cosmossdk.io/errors" ethcommon "github.com/ethereum/go-ethereum/common" + bin "github.com/gagliardetto/binary" "github.com/gagliardetto/solana-go" + "github.com/gagliardetto/solana-go/programs/token" "github.com/gagliardetto/solana-go/rpc" "github.com/rs/zerolog" @@ -43,6 +45,9 @@ type Signer struct { // pda is the program derived address of the gateway program pda solana.PublicKey + + // rent payer pda is the program derived address of the gateway program to pay rent for creating atas + rentPayerPda solana.PublicKey } // NewSigner creates a new Solana signer @@ -59,17 +64,24 @@ func NewSigner( baseSigner := base.NewSigner(chain, tss, ts, logger) // parse gateway ID and PDA - gatewayID, pda, err := contracts.ParseGatewayIDAndPda(chainParams.GatewayAddress) + gatewayID, pda, err := contracts.ParseGatewayWithPDA(chainParams.GatewayAddress) + if err != nil { + return nil, errors.Wrapf(err, "cannot parse gateway address %s", chainParams.GatewayAddress) + } + + // parse rent payer PDA, used in case receiver ATA should be created in gateway + rentPayerPda, err := contracts.RentPayerPDA(gatewayID) if err != nil { return nil, errors.Wrapf(err, "cannot parse gateway address %s", chainParams.GatewayAddress) } // create Solana signer signer := &Signer{ - Signer: baseSigner, - client: solClient, - gatewayID: gatewayID, - pda: pda, + Signer: baseSigner, + client: solClient, + gatewayID: gatewayID, + pda: pda, + rentPayerPda: rentPayerPda, } // construct Solana private key if present @@ -151,6 +163,15 @@ func (signer *Signer) TryProcessOutbound( } tx = withdrawTx + + case coin.CoinType_ERC20: + withdrawSPLTx, err := signer.prepareWithdrawSPLTx(ctx, cctx, height, logger) + if err != nil { + logger.Error().Err(err).Msgf("TryProcessOutbound: Fail to sign withdraw spl outbound") + return + } + + tx = withdrawSPLTx default: logger.Error(). Msgf("TryProcessOutbound: can only send SOL to the Solana network") @@ -219,6 +240,56 @@ func (signer *Signer) prepareWithdrawTx( return tx, nil } +func (signer *Signer) prepareWithdrawSPLTx( + ctx context.Context, + cctx *types.CrossChainTx, + height uint64, + logger zerolog.Logger, +) (*solana.Transaction, error) { + params := cctx.GetCurrentOutboundParam() + // compliance check + cancelTx := compliance.IsCctxRestricted(cctx) + if cancelTx { + compliance.PrintComplianceLog( + logger, + signer.Logger().Compliance, + true, + signer.Chain().ChainId, + cctx.Index, + cctx.InboundParams.Sender, + params.Receiver, + "SPL", + ) + } + + // get mint details to get decimals + mint, err := signer.decodeMintAccountDetails(ctx, cctx.InboundParams.Asset) + if err != nil { + return nil, err + } + + // sign gateway withdraw spl message by TSS + msg, err := signer.createAndSignMsgWithdrawSPL( + ctx, + params, + height, + cctx.InboundParams.Asset, + mint.Decimals, + cancelTx, + ) + if err != nil { + return nil, err + } + + // sign the withdraw transaction by relayer key + tx, err := signer.signWithdrawSPLTx(ctx, *msg) + if err != nil { + return nil, err + } + + return tx, nil +} + func (signer *Signer) prepareWhitelistTx( ctx context.Context, cctx *types.CrossChainTx, @@ -256,6 +327,23 @@ func (signer *Signer) prepareWhitelistTx( return tx, nil } +func (signer *Signer) decodeMintAccountDetails(ctx context.Context, asset string) (token.Mint, error) { + info, err := signer.client.GetAccountInfo(ctx, solana.MustPublicKeyFromBase58(asset)) + if err != nil { + return token.Mint{}, err + } + + var mint token.Mint + // Account{}.Data.GetBinary() returns the *decoded* binary data + // regardless the original encoding (it can handle them all). + err = bin.NewBinDecoder(info.Value.Data.GetBinary()).Decode(&mint) + if err != nil { + return token.Mint{}, err + } + + return mint, nil +} + // SetGatewayAddress sets the gateway address func (signer *Signer) SetGatewayAddress(address string) { // noop @@ -264,9 +352,9 @@ func (signer *Signer) SetGatewayAddress(address string) { } // parse gateway ID and PDA - gatewayID, pda, err := contracts.ParseGatewayIDAndPda(address) + gatewayID, pda, err := contracts.ParseGatewayWithPDA(address) if err != nil { - signer.Logger().Std.Error().Err(err).Str("address", address).Msgf("Unable to parse gateway address") + signer.Logger().Std.Error().Err(err).Msgf("cannot parse gateway address: %s", address) return } diff --git a/zetaclient/chains/solana/signer/whitelist.go b/zetaclient/chains/solana/signer/whitelist.go index 73ee769039..6d9055adc7 100644 --- a/zetaclient/chains/solana/signer/whitelist.go +++ b/zetaclient/chains/solana/signer/whitelist.go @@ -35,7 +35,6 @@ func (signer *Signer) createAndSignMsgWhitelist( if err != nil { return nil, errors.Wrap(err, "Key-sign failed") } - signer.Logger().Std.Info().Msgf("Key-sign succeed for chain %d nonce %d", chainID, nonce) // attach the signature and return return msg.SetSignature(signature), nil @@ -44,9 +43,7 @@ func (signer *Signer) createAndSignMsgWhitelist( // signWhitelistTx wraps the whitelist 'msg' into a Solana transaction and signs it with the relayer key. func (signer *Signer) signWhitelistTx(ctx context.Context, msg *contracts.MsgWhitelist) (*solana.Transaction, error) { // create whitelist_spl_mint instruction with program call data - var err error - var inst solana.GenericInstruction - inst.DataBytes, err = borsh.Serialize(contracts.WhitelistInstructionParams{ + dataBytes, err := borsh.Serialize(contracts.WhitelistInstructionParams{ Discriminator: contracts.DiscriminatorWhitelistSplMint, Signature: msg.SigRS(), RecoveryID: msg.SigV(), @@ -57,16 +54,17 @@ func (signer *Signer) signWhitelistTx(ctx context.Context, msg *contracts.MsgWhi return nil, errors.Wrap(err, "cannot serialize whitelist_spl_mint instruction") } - // attach required accounts to the instruction - privkey := signer.relayerKey - attachWhitelistAccounts( - &inst, - privkey.PublicKey(), - signer.pda, - msg.WhitelistCandidate(), - msg.WhitelistEntry(), - signer.gatewayID, - ) + inst := solana.GenericInstruction{ + ProgID: signer.gatewayID, + DataBytes: dataBytes, + AccountValues: []*solana.AccountMeta{ + solana.Meta(msg.WhitelistEntry()).WRITE(), + solana.Meta(msg.WhitelistCandidate()), + solana.Meta(signer.pda).WRITE(), + solana.Meta(signer.relayerKey.PublicKey()).WRITE().SIGNER(), + solana.Meta(solana.SystemProgramID), + }, + } // get a recent blockhash recent, err := signer.client.GetLatestBlockhash(ctx, rpc.CommitmentFinalized) @@ -83,7 +81,7 @@ func (signer *Signer) signWhitelistTx(ctx context.Context, msg *contracts.MsgWhi // programs.ComputeBudgetSetComputeUnitPrice(computeUnitPrice), &inst}, recent.Value.Blockhash, - solana.TransactionPayer(privkey.PublicKey()), + solana.TransactionPayer(signer.relayerKey.PublicKey()), ) if err != nil { return nil, errors.Wrap(err, "NewTransaction error") @@ -91,8 +89,8 @@ func (signer *Signer) signWhitelistTx(ctx context.Context, msg *contracts.MsgWhi // relayer signs the transaction _, err = tx.Sign(func(key solana.PublicKey) *solana.PrivateKey { - if key.Equals(privkey.PublicKey()) { - return privkey + if key.Equals(signer.relayerKey.PublicKey()) { + return signer.relayerKey } return nil }) @@ -102,24 +100,3 @@ func (signer *Signer) signWhitelistTx(ctx context.Context, msg *contracts.MsgWhi return tx, nil } - -// attachWhitelistAccounts attaches the required accounts for the gateway whitelist instruction. -func attachWhitelistAccounts( - inst *solana.GenericInstruction, - signer solana.PublicKey, - pda solana.PublicKey, - whitelistCandidate solana.PublicKey, - whitelistEntry solana.PublicKey, - gatewayID solana.PublicKey, -) { - // attach required accounts to the instruction - var accountSlice []*solana.AccountMeta - accountSlice = append(accountSlice, solana.Meta(whitelistEntry).WRITE()) - accountSlice = append(accountSlice, solana.Meta(whitelistCandidate)) - accountSlice = append(accountSlice, solana.Meta(pda).WRITE()) - accountSlice = append(accountSlice, solana.Meta(signer).WRITE().SIGNER()) - accountSlice = append(accountSlice, solana.Meta(solana.SystemProgramID)) - inst.ProgID = gatewayID - - inst.AccountValues = accountSlice -} diff --git a/zetaclient/chains/solana/signer/withdraw.go b/zetaclient/chains/solana/signer/withdraw.go index 51f4cceeea..5a27095f6f 100644 --- a/zetaclient/chains/solana/signer/withdraw.go +++ b/zetaclient/chains/solana/signer/withdraw.go @@ -13,7 +13,7 @@ import ( "github.com/zeta-chain/node/x/crosschain/types" ) -// createAndSignMsgWithdraw creates and signs a withdraw message (for gateway withdraw/withdraw_spl instruction) with TSS. +// createAndSignMsgWithdraw creates and signs a withdraw message for gateway withdraw instruction with TSS. func (signer *Signer) createAndSignMsgWithdraw( ctx context.Context, params *types.OutboundParams, @@ -26,7 +26,7 @@ func (signer *Signer) createAndSignMsgWithdraw( nonce := params.TssNonce amount := params.Amount.Uint64() - // zero out the amount if cancelTx is set. It's legal to withdraw 0 lamports thru the gateway. + // zero out the amount if cancelTx is set. It's legal to withdraw 0 lamports through the gateway. if cancelTx { amount = 0 } @@ -47,7 +47,6 @@ func (signer *Signer) createAndSignMsgWithdraw( if err != nil { return nil, errors.Wrap(err, "Key-sign failed") } - signer.Logger().Std.Info().Msgf("Key-sign succeed for chain %d nonce %d", chainID, nonce) // attach the signature and return return msg.SetSignature(signature), nil @@ -56,9 +55,7 @@ func (signer *Signer) createAndSignMsgWithdraw( // signWithdrawTx wraps the withdraw 'msg' into a Solana transaction and signs it with the relayer key. func (signer *Signer) signWithdrawTx(ctx context.Context, msg contracts.MsgWithdraw) (*solana.Transaction, error) { // create withdraw instruction with program call data - var err error - var inst solana.GenericInstruction - inst.DataBytes, err = borsh.Serialize(contracts.WithdrawInstructionParams{ + dataBytes, err := borsh.Serialize(contracts.WithdrawInstructionParams{ Discriminator: contracts.DiscriminatorWithdraw, Amount: msg.Amount(), Signature: msg.SigRS(), @@ -70,9 +67,15 @@ func (signer *Signer) signWithdrawTx(ctx context.Context, msg contracts.MsgWithd return nil, errors.Wrap(err, "cannot serialize withdraw instruction") } - // attach required accounts to the instruction - privkey := signer.relayerKey - attachWithdrawAccounts(&inst, privkey.PublicKey(), signer.pda, msg.To(), signer.gatewayID) + inst := solana.GenericInstruction{ + ProgID: signer.gatewayID, + DataBytes: dataBytes, + AccountValues: []*solana.AccountMeta{ + solana.Meta(signer.relayerKey.PublicKey()).WRITE().SIGNER(), + solana.Meta(signer.pda).WRITE(), + solana.Meta(msg.To()).WRITE(), + }, + } // get a recent blockhash recent, err := signer.client.GetLatestBlockhash(ctx, rpc.CommitmentFinalized) @@ -89,7 +92,7 @@ func (signer *Signer) signWithdrawTx(ctx context.Context, msg contracts.MsgWithd // programs.ComputeBudgetSetComputeUnitPrice(computeUnitPrice), &inst}, recent.Value.Blockhash, - solana.TransactionPayer(privkey.PublicKey()), + solana.TransactionPayer(signer.relayerKey.PublicKey()), ) if err != nil { return nil, errors.Wrap(err, "NewTransaction error") @@ -97,8 +100,8 @@ func (signer *Signer) signWithdrawTx(ctx context.Context, msg contracts.MsgWithd // relayer signs the transaction _, err = tx.Sign(func(key solana.PublicKey) *solana.PrivateKey { - if key.Equals(privkey.PublicKey()) { - return privkey + if key.Equals(signer.relayerKey.PublicKey()) { + return signer.relayerKey } return nil }) @@ -108,21 +111,3 @@ func (signer *Signer) signWithdrawTx(ctx context.Context, msg contracts.MsgWithd return tx, nil } - -// attachWithdrawAccounts attaches the required accounts for the gateway withdraw instruction. -func attachWithdrawAccounts( - inst *solana.GenericInstruction, - signer solana.PublicKey, - pda solana.PublicKey, - to solana.PublicKey, - gatewayID solana.PublicKey, -) { - // attach required accounts to the instruction - var accountSlice []*solana.AccountMeta - accountSlice = append(accountSlice, solana.Meta(signer).WRITE().SIGNER()) - accountSlice = append(accountSlice, solana.Meta(pda).WRITE()) - accountSlice = append(accountSlice, solana.Meta(to).WRITE()) - inst.ProgID = gatewayID - - inst.AccountValues = accountSlice -} diff --git a/zetaclient/chains/solana/signer/withdraw_spl.go b/zetaclient/chains/solana/signer/withdraw_spl.go new file mode 100644 index 0000000000..bf03260eca --- /dev/null +++ b/zetaclient/chains/solana/signer/withdraw_spl.go @@ -0,0 +1,147 @@ +package signer + +import ( + "context" + + "cosmossdk.io/errors" + "github.com/gagliardetto/solana-go" + "github.com/gagliardetto/solana-go/rpc" + "github.com/near/borsh-go" + + "github.com/zeta-chain/node/pkg/chains" + contracts "github.com/zeta-chain/node/pkg/contracts/solana" + "github.com/zeta-chain/node/x/crosschain/types" +) + +// createAndSignMsgWithdrawSPL creates and signs a withdraw spl message for gateway withdraw_spl instruction with TSS. +func (signer *Signer) createAndSignMsgWithdrawSPL( + ctx context.Context, + params *types.OutboundParams, + height uint64, + asset string, + decimals uint8, + cancelTx bool, +) (*contracts.MsgWithdrawSPL, error) { + chain := signer.Chain() + // #nosec G115 always positive + chainID := uint64(signer.Chain().ChainId) + nonce := params.TssNonce + amount := params.Amount.Uint64() + + // zero out the amount if cancelTx is set. It's legal to withdraw 0 spl through the gateway. + if cancelTx { + amount = 0 + } + + // check receiver address + to, err := chains.DecodeSolanaWalletAddress(params.Receiver) + if err != nil { + return nil, errors.Wrapf(err, "cannot decode receiver address %s", params.Receiver) + } + + // parse token account + tokenAccount, err := solana.PublicKeyFromBase58(asset) + if err != nil { + return nil, errors.Wrapf(err, "cannot parse asset public key %s", asset) + } + + // get recipient ata + recipientAta, _, err := solana.FindAssociatedTokenAddress(to, tokenAccount) + if err != nil { + return nil, errors.Wrapf(err, "cannot find ATA for %s and token account %s", to, tokenAccount) + } + + // prepare withdraw spl msg and compute hash + msg := contracts.NewMsgWithdrawSPL(chainID, nonce, amount, decimals, tokenAccount, to, recipientAta) + msgHash := msg.Hash() + + // sign the message with TSS to get an ECDSA signature. + // the produced signature is in the [R || S || V] format where V is 0 or 1. + signature, err := signer.TSS().Sign(ctx, msgHash[:], height, nonce, chain.ChainId, "") + if err != nil { + return nil, errors.Wrap(err, "Key-sign failed") + } + + // attach the signature and return + return msg.SetSignature(signature), nil +} + +// signWithdrawSPLTx wraps the withdraw spl 'msg' into a Solana transaction and signs it with the relayer key. +func (signer *Signer) signWithdrawSPLTx( + ctx context.Context, + msg contracts.MsgWithdrawSPL, +) (*solana.Transaction, error) { + // create withdraw spl instruction with program call data + dataBytes, err := borsh.Serialize(contracts.WithdrawSPLInstructionParams{ + Discriminator: contracts.DiscriminatorWithdrawSPL, + Decimals: msg.Decimals(), + Amount: msg.Amount(), + Signature: msg.SigRS(), + RecoveryID: msg.SigV(), + MessageHash: msg.Hash(), + Nonce: msg.Nonce(), + }) + if err != nil { + return nil, errors.Wrap(err, "cannot serialize withdraw instruction") + } + + pdaAta, _, err := solana.FindAssociatedTokenAddress(signer.pda, msg.TokenAccount()) + if err != nil { + return nil, errors.Wrapf(err, "cannot find ATA for %s and token account %s", signer.pda, msg.TokenAccount()) + } + + recipientAta, _, err := solana.FindAssociatedTokenAddress(msg.To(), msg.TokenAccount()) + if err != nil { + return nil, errors.Wrapf(err, "cannot find ATA for %s and token account %s", msg.To(), msg.TokenAccount()) + } + + inst := solana.GenericInstruction{ + ProgID: signer.gatewayID, + DataBytes: dataBytes, + AccountValues: []*solana.AccountMeta{ + solana.Meta(signer.relayerKey.PublicKey()).WRITE().SIGNER(), + solana.Meta(signer.pda).WRITE(), + solana.Meta(pdaAta).WRITE(), + solana.Meta(msg.TokenAccount()), + solana.Meta(msg.To()), + solana.Meta(recipientAta).WRITE(), + solana.Meta(signer.rentPayerPda).WRITE(), + solana.Meta(solana.TokenProgramID), + solana.Meta(solana.SPLAssociatedTokenAccountProgramID), + solana.Meta(solana.SystemProgramID), + }, + } + // get a recent blockhash + recent, err := signer.client.GetLatestBlockhash(ctx, rpc.CommitmentFinalized) + if err != nil { + return nil, errors.Wrap(err, "GetLatestBlockhash error") + } + + // create a transaction that wraps the instruction + tx, err := solana.NewTransaction( + []solana.Instruction{ + // TODO: outbound now uses 5K lamports as the fixed fee, we could explore priority fee and compute budget + // https://github.com/zeta-chain/node/issues/2599 + // programs.ComputeBudgetSetComputeUnitLimit(computeUnitLimit), + // programs.ComputeBudgetSetComputeUnitPrice(computeUnitPrice), + &inst}, + recent.Value.Blockhash, + solana.TransactionPayer(signer.relayerKey.PublicKey()), + ) + if err != nil { + return nil, errors.Wrap(err, "NewTransaction error") + } + + // relayer signs the transaction + _, err = tx.Sign(func(key solana.PublicKey) *solana.PrivateKey { + if key.Equals(signer.relayerKey.PublicKey()) { + return signer.relayerKey + } + return nil + }) + if err != nil { + return nil, errors.Wrap(err, "signer unable to sign transaction") + } + + return tx, nil +} From 5012d289ac817cd882453cfaff83ca7c9f6d3d6a Mon Sep 17 00:00:00 2001 From: Lucas Bertrand Date: Wed, 13 Nov 2024 09:56:56 +0100 Subject: [PATCH 07/14] fix: out of gas on ZetaClient during `onRevert` (#3144) * add gas consumption on revert * comment tests * format * fix testdapp deploy * set no limit * set higher gas * decrease gas usage * try again with 4M * push back old values * add tests back * fix unit test --- e2e/e2etests/test_deploy_contract.go | 2 ++ e2e/runner/v2_setup_evm.go | 8 ++++- e2e/runner/v2_setup_zeta.go | 8 ++++- pkg/contracts/testdappv2/TestDAppV2.abi | 24 +++++++++++++++ pkg/contracts/testdappv2/TestDAppV2.bin | 2 +- pkg/contracts/testdappv2/TestDAppV2.go | 39 +++++++++++++++++++++--- pkg/contracts/testdappv2/TestDAppV2.json | 26 +++++++++++++++- pkg/contracts/testdappv2/TestDAppV2.sol | 34 +++++++++++++++++++++ x/fungible/keeper/v2_deposits_test.go | 2 +- zetaclient/zetacore/constant.go | 4 +-- 10 files changed, 138 insertions(+), 11 deletions(-) diff --git a/e2e/e2etests/test_deploy_contract.go b/e2e/e2etests/test_deploy_contract.go index e2623a1381..92b1ae9257 100644 --- a/e2e/e2etests/test_deploy_contract.go +++ b/e2e/e2etests/test_deploy_contract.go @@ -45,6 +45,7 @@ func deployZEVMTestDApp(r *runner.E2ERunner) (ethcommon.Address, error) { addr, tx, _, err := testdappv2.DeployTestDAppV2( r.ZEVMAuth, r.ZEVMClient, + true, ) if err != nil { return addr, err @@ -64,6 +65,7 @@ func deployEVMTestDApp(r *runner.E2ERunner) (ethcommon.Address, error) { addr, tx, _, err := testdappv2.DeployTestDAppV2( r.EVMAuth, r.EVMClient, + false, ) if err != nil { return addr, err diff --git a/e2e/runner/v2_setup_evm.go b/e2e/runner/v2_setup_evm.go index 5b8e686d9b..8545302fd3 100644 --- a/e2e/runner/v2_setup_evm.go +++ b/e2e/runner/v2_setup_evm.go @@ -4,6 +4,7 @@ import ( "math/big" "time" + "github.com/ethereum/go-ethereum/accounts/abi/bind" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/require" erc20custodyv2 "github.com/zeta-chain/protocol-contracts/v2/pkg/erc20custody.sol" @@ -101,7 +102,7 @@ func (r *E2ERunner) SetupEVMV2() { require.NoError(r, err) // deploy test dapp v2 - testDAppV2Addr, txTestDAppV2, _, err := testdappv2.DeployTestDAppV2(r.EVMAuth, r.EVMClient) + testDAppV2Addr, txTestDAppV2, _, err := testdappv2.DeployTestDAppV2(r.EVMAuth, r.EVMClient, false) require.NoError(r, err) r.TestDAppV2EVMAddr = testDAppV2Addr @@ -115,6 +116,11 @@ func (r *E2ERunner) SetupEVMV2() { ensureTxReceipt(txSetCustody, "Set custody in Gateway failed") ensureTxReceipt(txTestDAppV2, "TestDAppV2 deployment failed") + // check isZetaChain is false + isZetaChain, err := r.TestDAppV2EVM.IsZetaChain(&bind.CallOpts{}) + require.NoError(r, err) + require.False(r, isZetaChain) + // whitelist the ERC20 txWhitelist, err := r.ERC20CustodyV2.Whitelist(r.EVMAuth, r.ERC20Addr) require.NoError(r, err) diff --git a/e2e/runner/v2_setup_zeta.go b/e2e/runner/v2_setup_zeta.go index 6d0607a7f9..69f16e2658 100644 --- a/e2e/runner/v2_setup_zeta.go +++ b/e2e/runner/v2_setup_zeta.go @@ -3,6 +3,7 @@ package runner import ( "time" + "github.com/ethereum/go-ethereum/accounts/abi/bind" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/require" "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" @@ -64,7 +65,7 @@ func (r *E2ERunner) SetZEVMContractsV2() { require.NoError(r, err) // deploy test dapp v2 - testDAppV2Addr, txTestDAppV2, _, err := testdappv2.DeployTestDAppV2(r.ZEVMAuth, r.ZEVMClient) + testDAppV2Addr, txTestDAppV2, _, err := testdappv2.DeployTestDAppV2(r.ZEVMAuth, r.ZEVMClient, true) require.NoError(r, err) r.TestDAppV2ZEVMAddr = testDAppV2Addr @@ -73,6 +74,11 @@ func (r *E2ERunner) SetZEVMContractsV2() { ensureTxReceipt(txProxy, "Gateway proxy deployment failed") ensureTxReceipt(txTestDAppV2, "TestDAppV2 deployment failed") + + // check isZetaChain is true + isZetaChain, err := r.TestDAppV2ZEVM.IsZetaChain(&bind.CallOpts{}) + require.NoError(r, err) + require.True(r, isZetaChain) } // UpdateChainParamsV2Contracts update the erc20 custody contract and gateway address in the chain params diff --git a/pkg/contracts/testdappv2/TestDAppV2.abi b/pkg/contracts/testdappv2/TestDAppV2.abi index 33270e31e3..d87050a8a0 100644 --- a/pkg/contracts/testdappv2/TestDAppV2.abi +++ b/pkg/contracts/testdappv2/TestDAppV2.abi @@ -1,4 +1,15 @@ [ + { + "inputs": [ + { + "internalType": "bool", + "name": "isZetaChain_", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, { "inputs": [], "name": "NO_MESSAGE_CALL", @@ -143,6 +154,19 @@ "stateMutability": "pure", "type": "function" }, + { + "inputs": [], + "name": "isZetaChain", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { diff --git a/pkg/contracts/testdappv2/TestDAppV2.bin b/pkg/contracts/testdappv2/TestDAppV2.bin index b53d34a684..b46669b67e 100644 --- a/pkg/contracts/testdappv2/TestDAppV2.bin +++ b/pkg/contracts/testdappv2/TestDAppV2.bin @@ -1 +1 @@ -6080604052348015600f57600080fd5b506115288061001f6000396000f3fe6080604052600436106100c65760003560e01c8063ad23b28b1161007f578063c9028a3611610059578063c9028a361461027b578063e2842ed7146102a4578063f592cbfb146102e1578063f936ae851461031e576100cd565b8063ad23b28b146101ea578063c7a339a914610227578063c85f843414610250576100cd565b806336e980a0146100d25780634297a263146100fb5780635bcfd61614610138578063676cc054146101615780639291fe2614610191578063a799911f146101ce576100cd565b366100cd57005b600080fd5b3480156100de57600080fd5b506100f960048036038101906100f49190610b86565b61035b565b005b34801561010757600080fd5b50610122600480360381019061011d9190610c05565b610385565b60405161012f9190610c4b565b60405180910390f35b34801561014457600080fd5b5061015f600480360381019061015a9190610d74565b61039d565b005b61017b60048036038101906101769190610e37565b610483565b6040516101889190610f16565b60405180910390f35b34801561019d57600080fd5b506101b860048036038101906101b39190610b86565b610595565b6040516101c59190610c4b565b60405180910390f35b6101e860048036038101906101e39190610b86565b6105d8565b005b3480156101f657600080fd5b50610211600480360381019061020c9190610f38565b610601565b60405161021e9190610fba565b60405180910390f35b34801561023357600080fd5b5061024e6004803603810190610249919061101a565b610661565b005b34801561025c57600080fd5b50610265610715565b6040516102729190610fba565b60405180910390f35b34801561028757600080fd5b506102a2600480360381019061029d91906110a8565b61074e565b005b3480156102b057600080fd5b506102cb60048036038101906102c69190610c05565b610888565b6040516102d8919061110c565b60405180910390f35b3480156102ed57600080fd5b5061030860048036038101906103039190610b86565b6108a8565b604051610315919061110c565b60405180910390f35b34801561032a57600080fd5b50610345600480360381019061034091906111c8565b6108f7565b6040516103529190611220565b60405180910390f35b61036481610940565b1561036e57600080fd5b61037781610996565b6103828160006109ea565b50565b60026020528060005260406000206000915090505481565b6103ea82828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610940565b156103f457600080fd5b600080838390501461044a5782828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610466565b6104658660200160208101906104609190610f38565b610601565b5b905061047181610996565b61047b81856109ea565b505050505050565b606060008084849050146104db5783838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506104f7565b6104f68560000160208101906104f19190610f38565b610601565b5b905061050281610996565b61050c81346109ea565b84600001602081019061051f9190610f38565b60018260405161052f9190611277565b908152602001604051809103902060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550604051806020016040528060008152509150509392505050565b600060026000836040516020016105ac91906112ca565b604051602081830303815290604052805190602001208152602001908152602001600020549050919050565b6105e181610940565b156105eb57600080fd5b6105f481610996565b6105fe81346109ea565b50565b60606040518060400160405280601681526020017f63616c6c65642077697468206e6f206d657373616765000000000000000000008152508260405160200161064b929190611329565b6040516020818303038152906040529050919050565b61066a81610940565b1561067457600080fd5b8273ffffffffffffffffffffffffffffffffffffffff166323b872dd3330856040518463ffffffff1660e01b81526004016106b193929190611351565b6020604051808303816000875af11580156106d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106f491906113b4565b6106fd57600080fd5b61070681610996565b61071081836109ea565b505050565b6040518060400160405280601681526020017f63616c6c65642077697468206e6f206d6573736167650000000000000000000081525081565b6107a981806060019061076191906113f0565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610996565b6108068180606001906107bc91906113f0565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505060006109ea565b8060000160208101906108199190610f38565b600182806060019061082b91906113f0565b604051610839929190611478565b908152602001604051809103902060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60006020528060005260406000206000915054906101000a900460ff1681565b6000806000836040516020016108be91906112ca565b60405160208183030381529060405280519060200120815260200190815260200160002060009054906101000a900460ff169050919050565b6001818051602081018201805184825260208301602085012081835280955050505050506000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000604051602001610951906114dd565b604051602081830303815290604052805190602001208260405160200161097891906112ca565b60405160208183030381529060405280519060200120149050919050565b6001600080836040516020016109ac91906112ca565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a81548160ff02191690831515021790555050565b806002600084604051602001610a0091906112ca565b604051602081830303815290604052805190602001208152602001908152602001600020819055505050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610a9382610a4a565b810181811067ffffffffffffffff82111715610ab257610ab1610a5b565b5b80604052505050565b6000610ac5610a2c565b9050610ad18282610a8a565b919050565b600067ffffffffffffffff821115610af157610af0610a5b565b5b610afa82610a4a565b9050602081019050919050565b82818337600083830152505050565b6000610b29610b2484610ad6565b610abb565b905082815260208101848484011115610b4557610b44610a45565b5b610b50848285610b07565b509392505050565b600082601f830112610b6d57610b6c610a40565b5b8135610b7d848260208601610b16565b91505092915050565b600060208284031215610b9c57610b9b610a36565b5b600082013567ffffffffffffffff811115610bba57610bb9610a3b565b5b610bc684828501610b58565b91505092915050565b6000819050919050565b610be281610bcf565b8114610bed57600080fd5b50565b600081359050610bff81610bd9565b92915050565b600060208284031215610c1b57610c1a610a36565b5b6000610c2984828501610bf0565b91505092915050565b6000819050919050565b610c4581610c32565b82525050565b6000602082019050610c606000830184610c3c565b92915050565b600080fd5b600060608284031215610c8157610c80610c66565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610cb582610c8a565b9050919050565b610cc581610caa565b8114610cd057600080fd5b50565b600081359050610ce281610cbc565b92915050565b610cf181610c32565b8114610cfc57600080fd5b50565b600081359050610d0e81610ce8565b92915050565b600080fd5b600080fd5b60008083601f840112610d3457610d33610a40565b5b8235905067ffffffffffffffff811115610d5157610d50610d14565b5b602083019150836001820283011115610d6d57610d6c610d19565b5b9250929050565b600080600080600060808688031215610d9057610d8f610a36565b5b600086013567ffffffffffffffff811115610dae57610dad610a3b565b5b610dba88828901610c6b565b9550506020610dcb88828901610cd3565b9450506040610ddc88828901610cff565b935050606086013567ffffffffffffffff811115610dfd57610dfc610a3b565b5b610e0988828901610d1e565b92509250509295509295909350565b600060208284031215610e2e57610e2d610c66565b5b81905092915050565b600080600060408486031215610e5057610e4f610a36565b5b6000610e5e86828701610e18565b935050602084013567ffffffffffffffff811115610e7f57610e7e610a3b565b5b610e8b86828701610d1e565b92509250509250925092565b600081519050919050565b600082825260208201905092915050565b60005b83811015610ed1578082015181840152602081019050610eb6565b60008484015250505050565b6000610ee882610e97565b610ef28185610ea2565b9350610f02818560208601610eb3565b610f0b81610a4a565b840191505092915050565b60006020820190508181036000830152610f308184610edd565b905092915050565b600060208284031215610f4e57610f4d610a36565b5b6000610f5c84828501610cd3565b91505092915050565b600081519050919050565b600082825260208201905092915050565b6000610f8c82610f65565b610f968185610f70565b9350610fa6818560208601610eb3565b610faf81610a4a565b840191505092915050565b60006020820190508181036000830152610fd48184610f81565b905092915050565b6000610fe782610caa565b9050919050565b610ff781610fdc565b811461100257600080fd5b50565b60008135905061101481610fee565b92915050565b60008060006060848603121561103357611032610a36565b5b600061104186828701611005565b935050602061105286828701610cff565b925050604084013567ffffffffffffffff81111561107357611072610a3b565b5b61107f86828701610b58565b9150509250925092565b60006080828403121561109f5761109e610c66565b5b81905092915050565b6000602082840312156110be576110bd610a36565b5b600082013567ffffffffffffffff8111156110dc576110db610a3b565b5b6110e884828501611089565b91505092915050565b60008115159050919050565b611106816110f1565b82525050565b600060208201905061112160008301846110fd565b92915050565b600067ffffffffffffffff82111561114257611141610a5b565b5b61114b82610a4a565b9050602081019050919050565b600061116b61116684611127565b610abb565b90508281526020810184848401111561118757611186610a45565b5b611192848285610b07565b509392505050565b600082601f8301126111af576111ae610a40565b5b81356111bf848260208601611158565b91505092915050565b6000602082840312156111de576111dd610a36565b5b600082013567ffffffffffffffff8111156111fc576111fb610a3b565b5b6112088482850161119a565b91505092915050565b61121a81610caa565b82525050565b60006020820190506112356000830184611211565b92915050565b600081905092915050565b600061125182610e97565b61125b818561123b565b935061126b818560208601610eb3565b80840191505092915050565b60006112838284611246565b915081905092915050565b600081905092915050565b60006112a482610f65565b6112ae818561128e565b93506112be818560208601610eb3565b80840191505092915050565b60006112d68284611299565b915081905092915050565b60008160601b9050919050565b60006112f9826112e1565b9050919050565b600061130b826112ee565b9050919050565b61132361131e82610caa565b611300565b82525050565b60006113358285611299565b91506113418284611312565b6014820191508190509392505050565b60006060820190506113666000830186611211565b6113736020830185611211565b6113806040830184610c3c565b949350505050565b611391816110f1565b811461139c57600080fd5b50565b6000815190506113ae81611388565b92915050565b6000602082840312156113ca576113c9610a36565b5b60006113d88482850161139f565b91505092915050565b600080fd5b600080fd5b600080fd5b6000808335600160200384360303811261140d5761140c6113e1565b5b80840192508235915067ffffffffffffffff82111561142f5761142e6113e6565b5b60208301925060018202360383131561144b5761144a6113eb565b5b509250929050565b600061145f838561123b565b935061146c838584610b07565b82840190509392505050565b6000611485828486611453565b91508190509392505050565b7f7265766572740000000000000000000000000000000000000000000000000000600082015250565b60006114c760068361128e565b91506114d282611491565b600682019050919050565b60006114e8826114ba565b915081905091905056fea26469706673582212205747e512af435680fec20f1d2e088f93d6c12052fee7e353f45e5e49a671377b64736f6c634300081a0033 +60a0604052348015600f57600080fd5b506040516117953803806117958339818101604052810190602f91906078565b8015156080811515815250505060a0565b600080fd5b60008115159050919050565b6058816045565b8114606257600080fd5b50565b6000815190506072816051565b92915050565b600060208284031215608b57608a6040565b5b60006097848285016065565b91505092915050565b6080516116d36100c26000396000818161079601526108ff01526116d36000f3fe6080604052600436106100e15760003560e01c8063c7a339a91161007f578063c91f356711610059578063c91f3567146102bf578063e2842ed7146102ea578063f592cbfb14610327578063f936ae8514610364576100e8565b8063c7a339a914610242578063c85f84341461026b578063c9028a3614610296576100e8565b8063676cc054116100bb578063676cc0541461017c5780639291fe26146101ac578063a799911f146101e9578063ad23b28b14610205576100e8565b806336e980a0146100ed5780634297a263146101165780635bcfd61614610153576100e8565b366100e857005b600080fd5b3480156100f957600080fd5b50610114600480360381019061010f9190610cd1565b6103a1565b005b34801561012257600080fd5b5061013d60048036038101906101389190610d50565b6103cb565b60405161014a9190610d96565b60405180910390f35b34801561015f57600080fd5b5061017a60048036038101906101759190610ebf565b6103e3565b005b61019660048036038101906101919190610f82565b6104c9565b6040516101a39190611061565b60405180910390f35b3480156101b857600080fd5b506101d360048036038101906101ce9190610cd1565b6105db565b6040516101e09190610d96565b60405180910390f35b61020360048036038101906101fe9190610cd1565b61061e565b005b34801561021157600080fd5b5061022c60048036038101906102279190611083565b610647565b6040516102399190611105565b60405180910390f35b34801561024e57600080fd5b5061026960048036038101906102649190611165565b6106a7565b005b34801561027757600080fd5b5061028061075b565b60405161028d9190611105565b60405180910390f35b3480156102a257600080fd5b506102bd60048036038101906102b891906111f3565b610794565b005b3480156102cb57600080fd5b506102d46108fd565b6040516102e19190611257565b60405180910390f35b3480156102f657600080fd5b50610311600480360381019061030c9190610d50565b610921565b60405161031e9190611257565b60405180910390f35b34801561033357600080fd5b5061034e60048036038101906103499190610cd1565b610941565b60405161035b9190611257565b60405180910390f35b34801561037057600080fd5b5061038b60048036038101906103869190611313565b610991565b604051610398919061136b565b60405180910390f35b6103aa816109da565b156103b457600080fd5b6103bd81610a30565b6103c8816000610a84565b50565b60036020528060005260406000206000915090505481565b61043082828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506109da565b1561043a57600080fd5b60008083839050146104905782828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506104ac565b6104ab8660200160208101906104a69190611083565b610647565b5b90506104b781610a30565b6104c18185610a84565b505050505050565b606060008084849050146105215783838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505061053d565b61053c8560000160208101906105379190611083565b610647565b5b905061054881610a30565b6105528134610a84565b8460000160208101906105659190611083565b60028260405161057591906113c2565b908152602001604051809103902060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550604051806020016040528060008152509150509392505050565b600060036000836040516020016105f29190611415565b604051602081830303815290604052805190602001208152602001908152602001600020549050919050565b610627816109da565b1561063157600080fd5b61063a81610a30565b6106448134610a84565b50565b60606040518060400160405280601681526020017f63616c6c65642077697468206e6f206d6573736167650000000000000000000081525082604051602001610691929190611474565b6040516020818303038152906040529050919050565b6106b0816109da565b156106ba57600080fd5b8273ffffffffffffffffffffffffffffffffffffffff166323b872dd3330856040518463ffffffff1660e01b81526004016106f79392919061149c565b6020604051808303816000875af1158015610716573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061073a91906114ff565b61074357600080fd5b61074c81610a30565b6107568183610a84565b505050565b6040518060400160405280601681526020017f63616c6c65642077697468206e6f206d6573736167650000000000000000000081525081565b7f0000000000000000000000000000000000000000000000000000000000000000156107c3576107c2610ac6565b5b61081e8180606001906107d6919061153b565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610a30565b61087b818060600190610831919061153b565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506000610a84565b80600001602081019061088e9190611083565b60028280606001906108a0919061153b565b6040516108ae9291906115c3565b908152602001604051809103902060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b7f000000000000000000000000000000000000000000000000000000000000000081565b60016020528060005260406000206000915054906101000a900460ff1681565b600060016000836040516020016109589190611415565b60405160208183030381529060405280519060200120815260200190815260200160002060009054906101000a900460ff169050919050565b6002818051602081018201805184825260208301602085012081835280955050505050506000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006040516020016109eb90611628565b6040516020818303038152906040528051906020012082604051602001610a129190611415565b60405160208183030381529060405280519060200120149050919050565b600180600083604051602001610a469190611415565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a81548160ff02191690831515021790555050565b806003600084604051602001610a9a9190611415565b604051602081830303815290604052805190602001208152602001908152602001600020819055505050565b60006207a12090506000614e20905060008183610ae3919061166c565b905060005b81811015610b265760008190806001815401808255809150506001900390600052602060002001600090919091909150558080600101915050610ae8565b50600080610b349190610b39565b505050565b5080546000825590600052602060002090810190610b579190610b5a565b50565b5b80821115610b73576000816000905550600101610b5b565b5090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610bde82610b95565b810181811067ffffffffffffffff82111715610bfd57610bfc610ba6565b5b80604052505050565b6000610c10610b77565b9050610c1c8282610bd5565b919050565b600067ffffffffffffffff821115610c3c57610c3b610ba6565b5b610c4582610b95565b9050602081019050919050565b82818337600083830152505050565b6000610c74610c6f84610c21565b610c06565b905082815260208101848484011115610c9057610c8f610b90565b5b610c9b848285610c52565b509392505050565b600082601f830112610cb857610cb7610b8b565b5b8135610cc8848260208601610c61565b91505092915050565b600060208284031215610ce757610ce6610b81565b5b600082013567ffffffffffffffff811115610d0557610d04610b86565b5b610d1184828501610ca3565b91505092915050565b6000819050919050565b610d2d81610d1a565b8114610d3857600080fd5b50565b600081359050610d4a81610d24565b92915050565b600060208284031215610d6657610d65610b81565b5b6000610d7484828501610d3b565b91505092915050565b6000819050919050565b610d9081610d7d565b82525050565b6000602082019050610dab6000830184610d87565b92915050565b600080fd5b600060608284031215610dcc57610dcb610db1565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610e0082610dd5565b9050919050565b610e1081610df5565b8114610e1b57600080fd5b50565b600081359050610e2d81610e07565b92915050565b610e3c81610d7d565b8114610e4757600080fd5b50565b600081359050610e5981610e33565b92915050565b600080fd5b600080fd5b60008083601f840112610e7f57610e7e610b8b565b5b8235905067ffffffffffffffff811115610e9c57610e9b610e5f565b5b602083019150836001820283011115610eb857610eb7610e64565b5b9250929050565b600080600080600060808688031215610edb57610eda610b81565b5b600086013567ffffffffffffffff811115610ef957610ef8610b86565b5b610f0588828901610db6565b9550506020610f1688828901610e1e565b9450506040610f2788828901610e4a565b935050606086013567ffffffffffffffff811115610f4857610f47610b86565b5b610f5488828901610e69565b92509250509295509295909350565b600060208284031215610f7957610f78610db1565b5b81905092915050565b600080600060408486031215610f9b57610f9a610b81565b5b6000610fa986828701610f63565b935050602084013567ffffffffffffffff811115610fca57610fc9610b86565b5b610fd686828701610e69565b92509250509250925092565b600081519050919050565b600082825260208201905092915050565b60005b8381101561101c578082015181840152602081019050611001565b60008484015250505050565b600061103382610fe2565b61103d8185610fed565b935061104d818560208601610ffe565b61105681610b95565b840191505092915050565b6000602082019050818103600083015261107b8184611028565b905092915050565b60006020828403121561109957611098610b81565b5b60006110a784828501610e1e565b91505092915050565b600081519050919050565b600082825260208201905092915050565b60006110d7826110b0565b6110e181856110bb565b93506110f1818560208601610ffe565b6110fa81610b95565b840191505092915050565b6000602082019050818103600083015261111f81846110cc565b905092915050565b600061113282610df5565b9050919050565b61114281611127565b811461114d57600080fd5b50565b60008135905061115f81611139565b92915050565b60008060006060848603121561117e5761117d610b81565b5b600061118c86828701611150565b935050602061119d86828701610e4a565b925050604084013567ffffffffffffffff8111156111be576111bd610b86565b5b6111ca86828701610ca3565b9150509250925092565b6000608082840312156111ea576111e9610db1565b5b81905092915050565b60006020828403121561120957611208610b81565b5b600082013567ffffffffffffffff81111561122757611226610b86565b5b611233848285016111d4565b91505092915050565b60008115159050919050565b6112518161123c565b82525050565b600060208201905061126c6000830184611248565b92915050565b600067ffffffffffffffff82111561128d5761128c610ba6565b5b61129682610b95565b9050602081019050919050565b60006112b66112b184611272565b610c06565b9050828152602081018484840111156112d2576112d1610b90565b5b6112dd848285610c52565b509392505050565b600082601f8301126112fa576112f9610b8b565b5b813561130a8482602086016112a3565b91505092915050565b60006020828403121561132957611328610b81565b5b600082013567ffffffffffffffff81111561134757611346610b86565b5b611353848285016112e5565b91505092915050565b61136581610df5565b82525050565b6000602082019050611380600083018461135c565b92915050565b600081905092915050565b600061139c82610fe2565b6113a68185611386565b93506113b6818560208601610ffe565b80840191505092915050565b60006113ce8284611391565b915081905092915050565b600081905092915050565b60006113ef826110b0565b6113f981856113d9565b9350611409818560208601610ffe565b80840191505092915050565b600061142182846113e4565b915081905092915050565b60008160601b9050919050565b60006114448261142c565b9050919050565b600061145682611439565b9050919050565b61146e61146982610df5565b61144b565b82525050565b600061148082856113e4565b915061148c828461145d565b6014820191508190509392505050565b60006060820190506114b1600083018661135c565b6114be602083018561135c565b6114cb6040830184610d87565b949350505050565b6114dc8161123c565b81146114e757600080fd5b50565b6000815190506114f9816114d3565b92915050565b60006020828403121561151557611514610b81565b5b6000611523848285016114ea565b91505092915050565b600080fd5b600080fd5b600080fd5b600080833560016020038436030381126115585761155761152c565b5b80840192508235915067ffffffffffffffff82111561157a57611579611531565b5b60208301925060018202360383131561159657611595611536565b5b509250929050565b60006115aa8385611386565b93506115b7838584610c52565b82840190509392505050565b60006115d082848661159e565b91508190509392505050565b7f7265766572740000000000000000000000000000000000000000000000000000600082015250565b60006116126006836113d9565b915061161d826115dc565b600682019050919050565b600061163382611605565b9150819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061167782610d7d565b915061168283610d7d565b9250826116925761169161163d565b5b82820490509291505056fea2646970667358221220eabca6f5232e330b7bfacfcaa51280bd616323d8694afc461b86845313fcbed364736f6c634300081a0033 diff --git a/pkg/contracts/testdappv2/TestDAppV2.go b/pkg/contracts/testdappv2/TestDAppV2.go index 7414aaccf2..aa825ebcce 100644 --- a/pkg/contracts/testdappv2/TestDAppV2.go +++ b/pkg/contracts/testdappv2/TestDAppV2.go @@ -51,8 +51,8 @@ type TestDAppV2zContext struct { // TestDAppV2MetaData contains all meta data concerning the TestDAppV2 contract. var TestDAppV2MetaData = &bind.MetaData{ - 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", + ABI: "[{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"isZetaChain_\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"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\":[],\"name\":\"isZetaChain\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"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: "0x60a0604052348015600f57600080fd5b506040516117953803806117958339818101604052810190602f91906078565b8015156080811515815250505060a0565b600080fd5b60008115159050919050565b6058816045565b8114606257600080fd5b50565b6000815190506072816051565b92915050565b600060208284031215608b57608a6040565b5b60006097848285016065565b91505092915050565b6080516116d36100c26000396000818161079601526108ff01526116d36000f3fe6080604052600436106100e15760003560e01c8063c7a339a91161007f578063c91f356711610059578063c91f3567146102bf578063e2842ed7146102ea578063f592cbfb14610327578063f936ae8514610364576100e8565b8063c7a339a914610242578063c85f84341461026b578063c9028a3614610296576100e8565b8063676cc054116100bb578063676cc0541461017c5780639291fe26146101ac578063a799911f146101e9578063ad23b28b14610205576100e8565b806336e980a0146100ed5780634297a263146101165780635bcfd61614610153576100e8565b366100e857005b600080fd5b3480156100f957600080fd5b50610114600480360381019061010f9190610cd1565b6103a1565b005b34801561012257600080fd5b5061013d60048036038101906101389190610d50565b6103cb565b60405161014a9190610d96565b60405180910390f35b34801561015f57600080fd5b5061017a60048036038101906101759190610ebf565b6103e3565b005b61019660048036038101906101919190610f82565b6104c9565b6040516101a39190611061565b60405180910390f35b3480156101b857600080fd5b506101d360048036038101906101ce9190610cd1565b6105db565b6040516101e09190610d96565b60405180910390f35b61020360048036038101906101fe9190610cd1565b61061e565b005b34801561021157600080fd5b5061022c60048036038101906102279190611083565b610647565b6040516102399190611105565b60405180910390f35b34801561024e57600080fd5b5061026960048036038101906102649190611165565b6106a7565b005b34801561027757600080fd5b5061028061075b565b60405161028d9190611105565b60405180910390f35b3480156102a257600080fd5b506102bd60048036038101906102b891906111f3565b610794565b005b3480156102cb57600080fd5b506102d46108fd565b6040516102e19190611257565b60405180910390f35b3480156102f657600080fd5b50610311600480360381019061030c9190610d50565b610921565b60405161031e9190611257565b60405180910390f35b34801561033357600080fd5b5061034e60048036038101906103499190610cd1565b610941565b60405161035b9190611257565b60405180910390f35b34801561037057600080fd5b5061038b60048036038101906103869190611313565b610991565b604051610398919061136b565b60405180910390f35b6103aa816109da565b156103b457600080fd5b6103bd81610a30565b6103c8816000610a84565b50565b60036020528060005260406000206000915090505481565b61043082828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506109da565b1561043a57600080fd5b60008083839050146104905782828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506104ac565b6104ab8660200160208101906104a69190611083565b610647565b5b90506104b781610a30565b6104c18185610a84565b505050505050565b606060008084849050146105215783838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505061053d565b61053c8560000160208101906105379190611083565b610647565b5b905061054881610a30565b6105528134610a84565b8460000160208101906105659190611083565b60028260405161057591906113c2565b908152602001604051809103902060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550604051806020016040528060008152509150509392505050565b600060036000836040516020016105f29190611415565b604051602081830303815290604052805190602001208152602001908152602001600020549050919050565b610627816109da565b1561063157600080fd5b61063a81610a30565b6106448134610a84565b50565b60606040518060400160405280601681526020017f63616c6c65642077697468206e6f206d6573736167650000000000000000000081525082604051602001610691929190611474565b6040516020818303038152906040529050919050565b6106b0816109da565b156106ba57600080fd5b8273ffffffffffffffffffffffffffffffffffffffff166323b872dd3330856040518463ffffffff1660e01b81526004016106f79392919061149c565b6020604051808303816000875af1158015610716573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061073a91906114ff565b61074357600080fd5b61074c81610a30565b6107568183610a84565b505050565b6040518060400160405280601681526020017f63616c6c65642077697468206e6f206d6573736167650000000000000000000081525081565b7f0000000000000000000000000000000000000000000000000000000000000000156107c3576107c2610ac6565b5b61081e8180606001906107d6919061153b565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610a30565b61087b818060600190610831919061153b565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506000610a84565b80600001602081019061088e9190611083565b60028280606001906108a0919061153b565b6040516108ae9291906115c3565b908152602001604051809103902060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b7f000000000000000000000000000000000000000000000000000000000000000081565b60016020528060005260406000206000915054906101000a900460ff1681565b600060016000836040516020016109589190611415565b60405160208183030381529060405280519060200120815260200190815260200160002060009054906101000a900460ff169050919050565b6002818051602081018201805184825260208301602085012081835280955050505050506000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006040516020016109eb90611628565b6040516020818303038152906040528051906020012082604051602001610a129190611415565b60405160208183030381529060405280519060200120149050919050565b600180600083604051602001610a469190611415565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a81548160ff02191690831515021790555050565b806003600084604051602001610a9a9190611415565b604051602081830303815290604052805190602001208152602001908152602001600020819055505050565b60006207a12090506000614e20905060008183610ae3919061166c565b905060005b81811015610b265760008190806001815401808255809150506001900390600052602060002001600090919091909150558080600101915050610ae8565b50600080610b349190610b39565b505050565b5080546000825590600052602060002090810190610b579190610b5a565b50565b5b80821115610b73576000816000905550600101610b5b565b5090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610bde82610b95565b810181811067ffffffffffffffff82111715610bfd57610bfc610ba6565b5b80604052505050565b6000610c10610b77565b9050610c1c8282610bd5565b919050565b600067ffffffffffffffff821115610c3c57610c3b610ba6565b5b610c4582610b95565b9050602081019050919050565b82818337600083830152505050565b6000610c74610c6f84610c21565b610c06565b905082815260208101848484011115610c9057610c8f610b90565b5b610c9b848285610c52565b509392505050565b600082601f830112610cb857610cb7610b8b565b5b8135610cc8848260208601610c61565b91505092915050565b600060208284031215610ce757610ce6610b81565b5b600082013567ffffffffffffffff811115610d0557610d04610b86565b5b610d1184828501610ca3565b91505092915050565b6000819050919050565b610d2d81610d1a565b8114610d3857600080fd5b50565b600081359050610d4a81610d24565b92915050565b600060208284031215610d6657610d65610b81565b5b6000610d7484828501610d3b565b91505092915050565b6000819050919050565b610d9081610d7d565b82525050565b6000602082019050610dab6000830184610d87565b92915050565b600080fd5b600060608284031215610dcc57610dcb610db1565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610e0082610dd5565b9050919050565b610e1081610df5565b8114610e1b57600080fd5b50565b600081359050610e2d81610e07565b92915050565b610e3c81610d7d565b8114610e4757600080fd5b50565b600081359050610e5981610e33565b92915050565b600080fd5b600080fd5b60008083601f840112610e7f57610e7e610b8b565b5b8235905067ffffffffffffffff811115610e9c57610e9b610e5f565b5b602083019150836001820283011115610eb857610eb7610e64565b5b9250929050565b600080600080600060808688031215610edb57610eda610b81565b5b600086013567ffffffffffffffff811115610ef957610ef8610b86565b5b610f0588828901610db6565b9550506020610f1688828901610e1e565b9450506040610f2788828901610e4a565b935050606086013567ffffffffffffffff811115610f4857610f47610b86565b5b610f5488828901610e69565b92509250509295509295909350565b600060208284031215610f7957610f78610db1565b5b81905092915050565b600080600060408486031215610f9b57610f9a610b81565b5b6000610fa986828701610f63565b935050602084013567ffffffffffffffff811115610fca57610fc9610b86565b5b610fd686828701610e69565b92509250509250925092565b600081519050919050565b600082825260208201905092915050565b60005b8381101561101c578082015181840152602081019050611001565b60008484015250505050565b600061103382610fe2565b61103d8185610fed565b935061104d818560208601610ffe565b61105681610b95565b840191505092915050565b6000602082019050818103600083015261107b8184611028565b905092915050565b60006020828403121561109957611098610b81565b5b60006110a784828501610e1e565b91505092915050565b600081519050919050565b600082825260208201905092915050565b60006110d7826110b0565b6110e181856110bb565b93506110f1818560208601610ffe565b6110fa81610b95565b840191505092915050565b6000602082019050818103600083015261111f81846110cc565b905092915050565b600061113282610df5565b9050919050565b61114281611127565b811461114d57600080fd5b50565b60008135905061115f81611139565b92915050565b60008060006060848603121561117e5761117d610b81565b5b600061118c86828701611150565b935050602061119d86828701610e4a565b925050604084013567ffffffffffffffff8111156111be576111bd610b86565b5b6111ca86828701610ca3565b9150509250925092565b6000608082840312156111ea576111e9610db1565b5b81905092915050565b60006020828403121561120957611208610b81565b5b600082013567ffffffffffffffff81111561122757611226610b86565b5b611233848285016111d4565b91505092915050565b60008115159050919050565b6112518161123c565b82525050565b600060208201905061126c6000830184611248565b92915050565b600067ffffffffffffffff82111561128d5761128c610ba6565b5b61129682610b95565b9050602081019050919050565b60006112b66112b184611272565b610c06565b9050828152602081018484840111156112d2576112d1610b90565b5b6112dd848285610c52565b509392505050565b600082601f8301126112fa576112f9610b8b565b5b813561130a8482602086016112a3565b91505092915050565b60006020828403121561132957611328610b81565b5b600082013567ffffffffffffffff81111561134757611346610b86565b5b611353848285016112e5565b91505092915050565b61136581610df5565b82525050565b6000602082019050611380600083018461135c565b92915050565b600081905092915050565b600061139c82610fe2565b6113a68185611386565b93506113b6818560208601610ffe565b80840191505092915050565b60006113ce8284611391565b915081905092915050565b600081905092915050565b60006113ef826110b0565b6113f981856113d9565b9350611409818560208601610ffe565b80840191505092915050565b600061142182846113e4565b915081905092915050565b60008160601b9050919050565b60006114448261142c565b9050919050565b600061145682611439565b9050919050565b61146e61146982610df5565b61144b565b82525050565b600061148082856113e4565b915061148c828461145d565b6014820191508190509392505050565b60006060820190506114b1600083018661135c565b6114be602083018561135c565b6114cb6040830184610d87565b949350505050565b6114dc8161123c565b81146114e757600080fd5b50565b6000815190506114f9816114d3565b92915050565b60006020828403121561151557611514610b81565b5b6000611523848285016114ea565b91505092915050565b600080fd5b600080fd5b600080fd5b600080833560016020038436030381126115585761155761152c565b5b80840192508235915067ffffffffffffffff82111561157a57611579611531565b5b60208301925060018202360383131561159657611595611536565b5b509250929050565b60006115aa8385611386565b93506115b7838584610c52565b82840190509392505050565b60006115d082848661159e565b91508190509392505050565b7f7265766572740000000000000000000000000000000000000000000000000000600082015250565b60006116126006836113d9565b915061161d826115dc565b600682019050919050565b600061163382611605565b9150819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061167782610d7d565b915061168283610d7d565b9250826116925761169161163d565b5b82820490509291505056fea2646970667358221220eabca6f5232e330b7bfacfcaa51280bd616323d8694afc461b86845313fcbed364736f6c634300081a0033", } // TestDAppV2ABI is the input ABI used to generate the binding from. @@ -64,7 +64,7 @@ var TestDAppV2ABI = TestDAppV2MetaData.ABI var TestDAppV2Bin = TestDAppV2MetaData.Bin // DeployTestDAppV2 deploys a new Ethereum contract, binding an instance of TestDAppV2 to it. -func DeployTestDAppV2(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *TestDAppV2, error) { +func DeployTestDAppV2(auth *bind.TransactOpts, backend bind.ContractBackend, isZetaChain_ bool) (common.Address, *types.Transaction, *TestDAppV2, error) { parsed, err := TestDAppV2MetaData.GetAbi() if err != nil { return common.Address{}, nil, nil, err @@ -73,7 +73,7 @@ func DeployTestDAppV2(auth *bind.TransactOpts, backend bind.ContractBackend) (co return common.Address{}, nil, nil, errors.New("GetABI returned nil") } - address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(TestDAppV2Bin), backend) + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(TestDAppV2Bin), backend, isZetaChain_) if err != nil { return common.Address{}, nil, nil, err } @@ -408,6 +408,37 @@ func (_TestDAppV2 *TestDAppV2CallerSession) GetNoMessageIndex(sender common.Addr return _TestDAppV2.Contract.GetNoMessageIndex(&_TestDAppV2.CallOpts, sender) } +// IsZetaChain is a free data retrieval call binding the contract method 0xc91f3567. +// +// Solidity: function isZetaChain() view returns(bool) +func (_TestDAppV2 *TestDAppV2Caller) IsZetaChain(opts *bind.CallOpts) (bool, error) { + var out []interface{} + err := _TestDAppV2.contract.Call(opts, &out, "isZetaChain") + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// IsZetaChain is a free data retrieval call binding the contract method 0xc91f3567. +// +// Solidity: function isZetaChain() view returns(bool) +func (_TestDAppV2 *TestDAppV2Session) IsZetaChain() (bool, error) { + return _TestDAppV2.Contract.IsZetaChain(&_TestDAppV2.CallOpts) +} + +// IsZetaChain is a free data retrieval call binding the contract method 0xc91f3567. +// +// Solidity: function isZetaChain() view returns(bool) +func (_TestDAppV2 *TestDAppV2CallerSession) IsZetaChain() (bool, error) { + return _TestDAppV2.Contract.IsZetaChain(&_TestDAppV2.CallOpts) +} + // SenderWithMessage is a free data retrieval call binding the contract method 0xf936ae85. // // Solidity: function senderWithMessage(bytes ) view returns(address) diff --git a/pkg/contracts/testdappv2/TestDAppV2.json b/pkg/contracts/testdappv2/TestDAppV2.json index 189d8b853f..9e3e79499c 100644 --- a/pkg/contracts/testdappv2/TestDAppV2.json +++ b/pkg/contracts/testdappv2/TestDAppV2.json @@ -1,5 +1,16 @@ { "abi": [ + { + "inputs": [ + { + "internalType": "bool", + "name": "isZetaChain_", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, { "inputs": [], "name": "NO_MESSAGE_CALL", @@ -144,6 +155,19 @@ "stateMutability": "pure", "type": "function" }, + { + "inputs": [], + "name": "isZetaChain", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -292,5 +316,5 @@ "type": "receive" } ], - "bin": "6080604052348015600f57600080fd5b506115288061001f6000396000f3fe6080604052600436106100c65760003560e01c8063ad23b28b1161007f578063c9028a3611610059578063c9028a361461027b578063e2842ed7146102a4578063f592cbfb146102e1578063f936ae851461031e576100cd565b8063ad23b28b146101ea578063c7a339a914610227578063c85f843414610250576100cd565b806336e980a0146100d25780634297a263146100fb5780635bcfd61614610138578063676cc054146101615780639291fe2614610191578063a799911f146101ce576100cd565b366100cd57005b600080fd5b3480156100de57600080fd5b506100f960048036038101906100f49190610b86565b61035b565b005b34801561010757600080fd5b50610122600480360381019061011d9190610c05565b610385565b60405161012f9190610c4b565b60405180910390f35b34801561014457600080fd5b5061015f600480360381019061015a9190610d74565b61039d565b005b61017b60048036038101906101769190610e37565b610483565b6040516101889190610f16565b60405180910390f35b34801561019d57600080fd5b506101b860048036038101906101b39190610b86565b610595565b6040516101c59190610c4b565b60405180910390f35b6101e860048036038101906101e39190610b86565b6105d8565b005b3480156101f657600080fd5b50610211600480360381019061020c9190610f38565b610601565b60405161021e9190610fba565b60405180910390f35b34801561023357600080fd5b5061024e6004803603810190610249919061101a565b610661565b005b34801561025c57600080fd5b50610265610715565b6040516102729190610fba565b60405180910390f35b34801561028757600080fd5b506102a2600480360381019061029d91906110a8565b61074e565b005b3480156102b057600080fd5b506102cb60048036038101906102c69190610c05565b610888565b6040516102d8919061110c565b60405180910390f35b3480156102ed57600080fd5b5061030860048036038101906103039190610b86565b6108a8565b604051610315919061110c565b60405180910390f35b34801561032a57600080fd5b50610345600480360381019061034091906111c8565b6108f7565b6040516103529190611220565b60405180910390f35b61036481610940565b1561036e57600080fd5b61037781610996565b6103828160006109ea565b50565b60026020528060005260406000206000915090505481565b6103ea82828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610940565b156103f457600080fd5b600080838390501461044a5782828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610466565b6104658660200160208101906104609190610f38565b610601565b5b905061047181610996565b61047b81856109ea565b505050505050565b606060008084849050146104db5783838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506104f7565b6104f68560000160208101906104f19190610f38565b610601565b5b905061050281610996565b61050c81346109ea565b84600001602081019061051f9190610f38565b60018260405161052f9190611277565b908152602001604051809103902060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550604051806020016040528060008152509150509392505050565b600060026000836040516020016105ac91906112ca565b604051602081830303815290604052805190602001208152602001908152602001600020549050919050565b6105e181610940565b156105eb57600080fd5b6105f481610996565b6105fe81346109ea565b50565b60606040518060400160405280601681526020017f63616c6c65642077697468206e6f206d657373616765000000000000000000008152508260405160200161064b929190611329565b6040516020818303038152906040529050919050565b61066a81610940565b1561067457600080fd5b8273ffffffffffffffffffffffffffffffffffffffff166323b872dd3330856040518463ffffffff1660e01b81526004016106b193929190611351565b6020604051808303816000875af11580156106d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106f491906113b4565b6106fd57600080fd5b61070681610996565b61071081836109ea565b505050565b6040518060400160405280601681526020017f63616c6c65642077697468206e6f206d6573736167650000000000000000000081525081565b6107a981806060019061076191906113f0565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610996565b6108068180606001906107bc91906113f0565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505060006109ea565b8060000160208101906108199190610f38565b600182806060019061082b91906113f0565b604051610839929190611478565b908152602001604051809103902060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60006020528060005260406000206000915054906101000a900460ff1681565b6000806000836040516020016108be91906112ca565b60405160208183030381529060405280519060200120815260200190815260200160002060009054906101000a900460ff169050919050565b6001818051602081018201805184825260208301602085012081835280955050505050506000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000604051602001610951906114dd565b604051602081830303815290604052805190602001208260405160200161097891906112ca565b60405160208183030381529060405280519060200120149050919050565b6001600080836040516020016109ac91906112ca565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a81548160ff02191690831515021790555050565b806002600084604051602001610a0091906112ca565b604051602081830303815290604052805190602001208152602001908152602001600020819055505050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610a9382610a4a565b810181811067ffffffffffffffff82111715610ab257610ab1610a5b565b5b80604052505050565b6000610ac5610a2c565b9050610ad18282610a8a565b919050565b600067ffffffffffffffff821115610af157610af0610a5b565b5b610afa82610a4a565b9050602081019050919050565b82818337600083830152505050565b6000610b29610b2484610ad6565b610abb565b905082815260208101848484011115610b4557610b44610a45565b5b610b50848285610b07565b509392505050565b600082601f830112610b6d57610b6c610a40565b5b8135610b7d848260208601610b16565b91505092915050565b600060208284031215610b9c57610b9b610a36565b5b600082013567ffffffffffffffff811115610bba57610bb9610a3b565b5b610bc684828501610b58565b91505092915050565b6000819050919050565b610be281610bcf565b8114610bed57600080fd5b50565b600081359050610bff81610bd9565b92915050565b600060208284031215610c1b57610c1a610a36565b5b6000610c2984828501610bf0565b91505092915050565b6000819050919050565b610c4581610c32565b82525050565b6000602082019050610c606000830184610c3c565b92915050565b600080fd5b600060608284031215610c8157610c80610c66565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610cb582610c8a565b9050919050565b610cc581610caa565b8114610cd057600080fd5b50565b600081359050610ce281610cbc565b92915050565b610cf181610c32565b8114610cfc57600080fd5b50565b600081359050610d0e81610ce8565b92915050565b600080fd5b600080fd5b60008083601f840112610d3457610d33610a40565b5b8235905067ffffffffffffffff811115610d5157610d50610d14565b5b602083019150836001820283011115610d6d57610d6c610d19565b5b9250929050565b600080600080600060808688031215610d9057610d8f610a36565b5b600086013567ffffffffffffffff811115610dae57610dad610a3b565b5b610dba88828901610c6b565b9550506020610dcb88828901610cd3565b9450506040610ddc88828901610cff565b935050606086013567ffffffffffffffff811115610dfd57610dfc610a3b565b5b610e0988828901610d1e565b92509250509295509295909350565b600060208284031215610e2e57610e2d610c66565b5b81905092915050565b600080600060408486031215610e5057610e4f610a36565b5b6000610e5e86828701610e18565b935050602084013567ffffffffffffffff811115610e7f57610e7e610a3b565b5b610e8b86828701610d1e565b92509250509250925092565b600081519050919050565b600082825260208201905092915050565b60005b83811015610ed1578082015181840152602081019050610eb6565b60008484015250505050565b6000610ee882610e97565b610ef28185610ea2565b9350610f02818560208601610eb3565b610f0b81610a4a565b840191505092915050565b60006020820190508181036000830152610f308184610edd565b905092915050565b600060208284031215610f4e57610f4d610a36565b5b6000610f5c84828501610cd3565b91505092915050565b600081519050919050565b600082825260208201905092915050565b6000610f8c82610f65565b610f968185610f70565b9350610fa6818560208601610eb3565b610faf81610a4a565b840191505092915050565b60006020820190508181036000830152610fd48184610f81565b905092915050565b6000610fe782610caa565b9050919050565b610ff781610fdc565b811461100257600080fd5b50565b60008135905061101481610fee565b92915050565b60008060006060848603121561103357611032610a36565b5b600061104186828701611005565b935050602061105286828701610cff565b925050604084013567ffffffffffffffff81111561107357611072610a3b565b5b61107f86828701610b58565b9150509250925092565b60006080828403121561109f5761109e610c66565b5b81905092915050565b6000602082840312156110be576110bd610a36565b5b600082013567ffffffffffffffff8111156110dc576110db610a3b565b5b6110e884828501611089565b91505092915050565b60008115159050919050565b611106816110f1565b82525050565b600060208201905061112160008301846110fd565b92915050565b600067ffffffffffffffff82111561114257611141610a5b565b5b61114b82610a4a565b9050602081019050919050565b600061116b61116684611127565b610abb565b90508281526020810184848401111561118757611186610a45565b5b611192848285610b07565b509392505050565b600082601f8301126111af576111ae610a40565b5b81356111bf848260208601611158565b91505092915050565b6000602082840312156111de576111dd610a36565b5b600082013567ffffffffffffffff8111156111fc576111fb610a3b565b5b6112088482850161119a565b91505092915050565b61121a81610caa565b82525050565b60006020820190506112356000830184611211565b92915050565b600081905092915050565b600061125182610e97565b61125b818561123b565b935061126b818560208601610eb3565b80840191505092915050565b60006112838284611246565b915081905092915050565b600081905092915050565b60006112a482610f65565b6112ae818561128e565b93506112be818560208601610eb3565b80840191505092915050565b60006112d68284611299565b915081905092915050565b60008160601b9050919050565b60006112f9826112e1565b9050919050565b600061130b826112ee565b9050919050565b61132361131e82610caa565b611300565b82525050565b60006113358285611299565b91506113418284611312565b6014820191508190509392505050565b60006060820190506113666000830186611211565b6113736020830185611211565b6113806040830184610c3c565b949350505050565b611391816110f1565b811461139c57600080fd5b50565b6000815190506113ae81611388565b92915050565b6000602082840312156113ca576113c9610a36565b5b60006113d88482850161139f565b91505092915050565b600080fd5b600080fd5b600080fd5b6000808335600160200384360303811261140d5761140c6113e1565b5b80840192508235915067ffffffffffffffff82111561142f5761142e6113e6565b5b60208301925060018202360383131561144b5761144a6113eb565b5b509250929050565b600061145f838561123b565b935061146c838584610b07565b82840190509392505050565b6000611485828486611453565b91508190509392505050565b7f7265766572740000000000000000000000000000000000000000000000000000600082015250565b60006114c760068361128e565b91506114d282611491565b600682019050919050565b60006114e8826114ba565b915081905091905056fea26469706673582212205747e512af435680fec20f1d2e088f93d6c12052fee7e353f45e5e49a671377b64736f6c634300081a0033" + "bin": "60a0604052348015600f57600080fd5b506040516117953803806117958339818101604052810190602f91906078565b8015156080811515815250505060a0565b600080fd5b60008115159050919050565b6058816045565b8114606257600080fd5b50565b6000815190506072816051565b92915050565b600060208284031215608b57608a6040565b5b60006097848285016065565b91505092915050565b6080516116d36100c26000396000818161079601526108ff01526116d36000f3fe6080604052600436106100e15760003560e01c8063c7a339a91161007f578063c91f356711610059578063c91f3567146102bf578063e2842ed7146102ea578063f592cbfb14610327578063f936ae8514610364576100e8565b8063c7a339a914610242578063c85f84341461026b578063c9028a3614610296576100e8565b8063676cc054116100bb578063676cc0541461017c5780639291fe26146101ac578063a799911f146101e9578063ad23b28b14610205576100e8565b806336e980a0146100ed5780634297a263146101165780635bcfd61614610153576100e8565b366100e857005b600080fd5b3480156100f957600080fd5b50610114600480360381019061010f9190610cd1565b6103a1565b005b34801561012257600080fd5b5061013d60048036038101906101389190610d50565b6103cb565b60405161014a9190610d96565b60405180910390f35b34801561015f57600080fd5b5061017a60048036038101906101759190610ebf565b6103e3565b005b61019660048036038101906101919190610f82565b6104c9565b6040516101a39190611061565b60405180910390f35b3480156101b857600080fd5b506101d360048036038101906101ce9190610cd1565b6105db565b6040516101e09190610d96565b60405180910390f35b61020360048036038101906101fe9190610cd1565b61061e565b005b34801561021157600080fd5b5061022c60048036038101906102279190611083565b610647565b6040516102399190611105565b60405180910390f35b34801561024e57600080fd5b5061026960048036038101906102649190611165565b6106a7565b005b34801561027757600080fd5b5061028061075b565b60405161028d9190611105565b60405180910390f35b3480156102a257600080fd5b506102bd60048036038101906102b891906111f3565b610794565b005b3480156102cb57600080fd5b506102d46108fd565b6040516102e19190611257565b60405180910390f35b3480156102f657600080fd5b50610311600480360381019061030c9190610d50565b610921565b60405161031e9190611257565b60405180910390f35b34801561033357600080fd5b5061034e60048036038101906103499190610cd1565b610941565b60405161035b9190611257565b60405180910390f35b34801561037057600080fd5b5061038b60048036038101906103869190611313565b610991565b604051610398919061136b565b60405180910390f35b6103aa816109da565b156103b457600080fd5b6103bd81610a30565b6103c8816000610a84565b50565b60036020528060005260406000206000915090505481565b61043082828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506109da565b1561043a57600080fd5b60008083839050146104905782828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506104ac565b6104ab8660200160208101906104a69190611083565b610647565b5b90506104b781610a30565b6104c18185610a84565b505050505050565b606060008084849050146105215783838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505061053d565b61053c8560000160208101906105379190611083565b610647565b5b905061054881610a30565b6105528134610a84565b8460000160208101906105659190611083565b60028260405161057591906113c2565b908152602001604051809103902060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550604051806020016040528060008152509150509392505050565b600060036000836040516020016105f29190611415565b604051602081830303815290604052805190602001208152602001908152602001600020549050919050565b610627816109da565b1561063157600080fd5b61063a81610a30565b6106448134610a84565b50565b60606040518060400160405280601681526020017f63616c6c65642077697468206e6f206d6573736167650000000000000000000081525082604051602001610691929190611474565b6040516020818303038152906040529050919050565b6106b0816109da565b156106ba57600080fd5b8273ffffffffffffffffffffffffffffffffffffffff166323b872dd3330856040518463ffffffff1660e01b81526004016106f79392919061149c565b6020604051808303816000875af1158015610716573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061073a91906114ff565b61074357600080fd5b61074c81610a30565b6107568183610a84565b505050565b6040518060400160405280601681526020017f63616c6c65642077697468206e6f206d6573736167650000000000000000000081525081565b7f0000000000000000000000000000000000000000000000000000000000000000156107c3576107c2610ac6565b5b61081e8180606001906107d6919061153b565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610a30565b61087b818060600190610831919061153b565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506000610a84565b80600001602081019061088e9190611083565b60028280606001906108a0919061153b565b6040516108ae9291906115c3565b908152602001604051809103902060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b7f000000000000000000000000000000000000000000000000000000000000000081565b60016020528060005260406000206000915054906101000a900460ff1681565b600060016000836040516020016109589190611415565b60405160208183030381529060405280519060200120815260200190815260200160002060009054906101000a900460ff169050919050565b6002818051602081018201805184825260208301602085012081835280955050505050506000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006040516020016109eb90611628565b6040516020818303038152906040528051906020012082604051602001610a129190611415565b60405160208183030381529060405280519060200120149050919050565b600180600083604051602001610a469190611415565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a81548160ff02191690831515021790555050565b806003600084604051602001610a9a9190611415565b604051602081830303815290604052805190602001208152602001908152602001600020819055505050565b60006207a12090506000614e20905060008183610ae3919061166c565b905060005b81811015610b265760008190806001815401808255809150506001900390600052602060002001600090919091909150558080600101915050610ae8565b50600080610b349190610b39565b505050565b5080546000825590600052602060002090810190610b579190610b5a565b50565b5b80821115610b73576000816000905550600101610b5b565b5090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610bde82610b95565b810181811067ffffffffffffffff82111715610bfd57610bfc610ba6565b5b80604052505050565b6000610c10610b77565b9050610c1c8282610bd5565b919050565b600067ffffffffffffffff821115610c3c57610c3b610ba6565b5b610c4582610b95565b9050602081019050919050565b82818337600083830152505050565b6000610c74610c6f84610c21565b610c06565b905082815260208101848484011115610c9057610c8f610b90565b5b610c9b848285610c52565b509392505050565b600082601f830112610cb857610cb7610b8b565b5b8135610cc8848260208601610c61565b91505092915050565b600060208284031215610ce757610ce6610b81565b5b600082013567ffffffffffffffff811115610d0557610d04610b86565b5b610d1184828501610ca3565b91505092915050565b6000819050919050565b610d2d81610d1a565b8114610d3857600080fd5b50565b600081359050610d4a81610d24565b92915050565b600060208284031215610d6657610d65610b81565b5b6000610d7484828501610d3b565b91505092915050565b6000819050919050565b610d9081610d7d565b82525050565b6000602082019050610dab6000830184610d87565b92915050565b600080fd5b600060608284031215610dcc57610dcb610db1565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610e0082610dd5565b9050919050565b610e1081610df5565b8114610e1b57600080fd5b50565b600081359050610e2d81610e07565b92915050565b610e3c81610d7d565b8114610e4757600080fd5b50565b600081359050610e5981610e33565b92915050565b600080fd5b600080fd5b60008083601f840112610e7f57610e7e610b8b565b5b8235905067ffffffffffffffff811115610e9c57610e9b610e5f565b5b602083019150836001820283011115610eb857610eb7610e64565b5b9250929050565b600080600080600060808688031215610edb57610eda610b81565b5b600086013567ffffffffffffffff811115610ef957610ef8610b86565b5b610f0588828901610db6565b9550506020610f1688828901610e1e565b9450506040610f2788828901610e4a565b935050606086013567ffffffffffffffff811115610f4857610f47610b86565b5b610f5488828901610e69565b92509250509295509295909350565b600060208284031215610f7957610f78610db1565b5b81905092915050565b600080600060408486031215610f9b57610f9a610b81565b5b6000610fa986828701610f63565b935050602084013567ffffffffffffffff811115610fca57610fc9610b86565b5b610fd686828701610e69565b92509250509250925092565b600081519050919050565b600082825260208201905092915050565b60005b8381101561101c578082015181840152602081019050611001565b60008484015250505050565b600061103382610fe2565b61103d8185610fed565b935061104d818560208601610ffe565b61105681610b95565b840191505092915050565b6000602082019050818103600083015261107b8184611028565b905092915050565b60006020828403121561109957611098610b81565b5b60006110a784828501610e1e565b91505092915050565b600081519050919050565b600082825260208201905092915050565b60006110d7826110b0565b6110e181856110bb565b93506110f1818560208601610ffe565b6110fa81610b95565b840191505092915050565b6000602082019050818103600083015261111f81846110cc565b905092915050565b600061113282610df5565b9050919050565b61114281611127565b811461114d57600080fd5b50565b60008135905061115f81611139565b92915050565b60008060006060848603121561117e5761117d610b81565b5b600061118c86828701611150565b935050602061119d86828701610e4a565b925050604084013567ffffffffffffffff8111156111be576111bd610b86565b5b6111ca86828701610ca3565b9150509250925092565b6000608082840312156111ea576111e9610db1565b5b81905092915050565b60006020828403121561120957611208610b81565b5b600082013567ffffffffffffffff81111561122757611226610b86565b5b611233848285016111d4565b91505092915050565b60008115159050919050565b6112518161123c565b82525050565b600060208201905061126c6000830184611248565b92915050565b600067ffffffffffffffff82111561128d5761128c610ba6565b5b61129682610b95565b9050602081019050919050565b60006112b66112b184611272565b610c06565b9050828152602081018484840111156112d2576112d1610b90565b5b6112dd848285610c52565b509392505050565b600082601f8301126112fa576112f9610b8b565b5b813561130a8482602086016112a3565b91505092915050565b60006020828403121561132957611328610b81565b5b600082013567ffffffffffffffff81111561134757611346610b86565b5b611353848285016112e5565b91505092915050565b61136581610df5565b82525050565b6000602082019050611380600083018461135c565b92915050565b600081905092915050565b600061139c82610fe2565b6113a68185611386565b93506113b6818560208601610ffe565b80840191505092915050565b60006113ce8284611391565b915081905092915050565b600081905092915050565b60006113ef826110b0565b6113f981856113d9565b9350611409818560208601610ffe565b80840191505092915050565b600061142182846113e4565b915081905092915050565b60008160601b9050919050565b60006114448261142c565b9050919050565b600061145682611439565b9050919050565b61146e61146982610df5565b61144b565b82525050565b600061148082856113e4565b915061148c828461145d565b6014820191508190509392505050565b60006060820190506114b1600083018661135c565b6114be602083018561135c565b6114cb6040830184610d87565b949350505050565b6114dc8161123c565b81146114e757600080fd5b50565b6000815190506114f9816114d3565b92915050565b60006020828403121561151557611514610b81565b5b6000611523848285016114ea565b91505092915050565b600080fd5b600080fd5b600080fd5b600080833560016020038436030381126115585761155761152c565b5b80840192508235915067ffffffffffffffff82111561157a57611579611531565b5b60208301925060018202360383131561159657611595611536565b5b509250929050565b60006115aa8385611386565b93506115b7838584610c52565b82840190509392505050565b60006115d082848661159e565b91508190509392505050565b7f7265766572740000000000000000000000000000000000000000000000000000600082015250565b60006116126006836113d9565b915061161d826115dc565b600682019050919050565b600061163382611605565b9150819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061167782610d7d565b915061168283610d7d565b9250826116925761169161163d565b5b82820490509291505056fea2646970667358221220eabca6f5232e330b7bfacfcaa51280bd616323d8694afc461b86845313fcbed364736f6c634300081a0033" } diff --git a/pkg/contracts/testdappv2/TestDAppV2.sol b/pkg/contracts/testdappv2/TestDAppV2.sol index 3282cbe383..bf0f5887a4 100644 --- a/pkg/contracts/testdappv2/TestDAppV2.sol +++ b/pkg/contracts/testdappv2/TestDAppV2.sol @@ -6,8 +6,14 @@ interface IERC20 { } contract TestDAppV2 { + // used to simulate gas consumption + uint256[] private storageArray; + string public constant NO_MESSAGE_CALL = "called with no message"; + // define if the chain is ZetaChain + bool immutable public isZetaChain; + struct zContext { bytes origin; address sender; @@ -37,6 +43,11 @@ contract TestDAppV2 { mapping(bytes => address) public senderWithMessage; mapping(bytes32 => uint256) public amountWithMessage; + // the constructor is used to determine if the chain is ZetaChain + constructor(bool isZetaChain_) { + isZetaChain = isZetaChain_; + } + // 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 @@ -110,6 +121,13 @@ contract TestDAppV2 { // Revertable interface function onRevert(RevertContext calldata revertContext) external { + + // if the chain is ZetaChain, consume gas to test the gas consumption + // we do it specifically for ZetaChain to test the outbound processing workflow + if (isZetaChain) { + consumeGas(); + } + setCalledWithMessage(string(revertContext.revertMessage)); setAmountWithMessage(string(revertContext.revertMessage), 0); senderWithMessage[revertContext.revertMessage] = revertContext.sender; @@ -126,5 +144,21 @@ contract TestDAppV2 { return ""; } + function consumeGas() internal { + // Approximate target gas consumption + uint256 targetGas = 500000; + // Approximate gas cost for a single storage write + uint256 storageWriteGasCost = 20000; + uint256 iterations = targetGas / storageWriteGasCost; + + // Perform the storage writes + for (uint256 i = 0; i < iterations; i++) { + storageArray.push(i); + } + + // Reset the storage array to avoid accumulation of storage cost + delete storageArray; + } + receive() external payable {} } \ No newline at end of file diff --git a/x/fungible/keeper/v2_deposits_test.go b/x/fungible/keeper/v2_deposits_test.go index 7b13eeef89..7f25ec42e9 100644 --- a/x/fungible/keeper/v2_deposits_test.go +++ b/x/fungible/keeper/v2_deposits_test.go @@ -51,7 +51,7 @@ func getTestDAppNoMessageIndex( // 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) + testDAppV2, err := k.DeployContract(ctx, testdappv2.TestDAppV2MetaData, true) require.NoError(t, err) require.NotEmpty(t, testDAppV2) assertContractDeployment(t, evmk, ctx, testDAppV2) diff --git a/zetaclient/zetacore/constant.go b/zetaclient/zetacore/constant.go index c2185a08e1..f5ff75ebdd 100644 --- a/zetaclient/zetacore/constant.go +++ b/zetaclient/zetacore/constant.go @@ -37,8 +37,8 @@ const ( PostVoteOutboundGasLimit = 500_000 // PostVoteOutboundRevertGasLimit is the gas limit for voting on observed outbound tx for revert (when outbound fails) - // The value needs to be higher because reverting implies interacting with the EVM to perform swaps for the gas token - PostVoteOutboundRevertGasLimit = 4_000_000 + // The value is set to 7M because in case of onRevert call, it might consume lot of gas + PostVoteOutboundRevertGasLimit = 7_000_000 ) // constants for monitoring tx results From e217ec42d10bb746c67501c3fefef7083d8d7b0b Mon Sep 17 00:00:00 2001 From: Tanmay Date: Wed, 13 Nov 2024 07:31:18 -0500 Subject: [PATCH 08/14] fix: register withdraw emissions to legacy amino (#3117) * register withdraw emissions to legacy amino * add changelog entry * register update params * format code * add changelog * refactor changelog entry * remove MsgWithdrawEmissions from zetaclient grants * Update changelog.md --------- Co-authored-by: Lucas Bertrand --- changelog.md | 1 + x/emissions/types/codec.go | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index ac4e4c351c..244bc9d08d 100644 --- a/changelog.md +++ b/changelog.md @@ -21,6 +21,7 @@ * [3137](https://github.com/zeta-chain/node/pull/3137) - remove chain.Chain from zetaclientd config ### Fixes +* [3117](https://github.com/zeta-chain/node/pull/3117) - register messages for emissions module to legacy amino codec. * [3041](https://github.com/zeta-chain/node/pull/3041) - replace libp2p public DHT with private gossip peer discovery and connection gater for inbound connections * [3106](https://github.com/zeta-chain/node/pull/3106) - prevent blocked CCTX on out of gas during omnichain calls * [3139](https://github.com/zeta-chain/node/pull/3139) - fix config resolution in orchestrator diff --git a/x/emissions/types/codec.go b/x/emissions/types/codec.go index 4f0a3e3b48..4c8ca20637 100644 --- a/x/emissions/types/codec.go +++ b/x/emissions/types/codec.go @@ -3,13 +3,20 @@ package types import ( "github.com/cosmos/cosmos-sdk/codec" cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/msgservice" ) -func RegisterCodec(_ *codec.LegacyAmino) { +func RegisterCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgWithdrawEmission{}, "emissions/WithdrawEmission", nil) + cdc.RegisterConcrete(&MsgUpdateParams{}, "emissions/UpdateParams", nil) } func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { + registry.RegisterImplementations((*sdk.Msg)(nil), + &MsgWithdrawEmission{}, + &MsgUpdateParams{}, + ) msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) } From d6ed9aa5ce5862f9ff90cb34669cdf1e5b228826 Mon Sep 17 00:00:00 2001 From: Alex Gartner Date: Wed, 13 Nov 2024 11:38:11 -0800 Subject: [PATCH 09/14] test: fix evm signer instability (#3133) * test: fix evm signer instability * fix * coderabbit feedback --- zetaclient/chains/evm/signer/sign_test.go | 33 +++++++--------- .../chains/evm/signer/signer_admin_test.go | 39 ++++++++----------- zetaclient/chains/evm/signer/signer_test.go | 17 ++++---- zetaclient/testutils/mocks/tss_signer.go | 10 +++++ 4 files changed, 48 insertions(+), 51 deletions(-) diff --git a/zetaclient/chains/evm/signer/sign_test.go b/zetaclient/chains/evm/signer/sign_test.go index 5123968573..c3d64ebabc 100644 --- a/zetaclient/chains/evm/signer/sign_test.go +++ b/zetaclient/chains/evm/signer/sign_test.go @@ -8,6 +8,7 @@ import ( "github.com/rs/zerolog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/zetaclient/testutils/mocks" ) @@ -15,7 +16,7 @@ func TestSigner_SignConnectorOnReceive(t *testing.T) { ctx := makeCtx(t) // Setup evm signer - tss := mocks.NewTSSMainnet() + tss := mocks.NewDerivedTSS(chains.BitcoinMainnet) evmSigner, err := getNewEvmSigner(tss) require.NoError(t, err) @@ -33,8 +34,7 @@ func TestSigner_SignConnectorOnReceive(t *testing.T) { require.NoError(t, err) // Verify Signature - tss := mocks.NewTSSMainnet() - verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + verifyTxSender(t, tx, tss.EVMAddress(), evmSigner.EvmSigner()) }) t.Run("SignConnectorOnReceive - should fail if keysign fails", func(t *testing.T) { // Pause tss to make keysign fail @@ -53,8 +53,7 @@ func TestSigner_SignConnectorOnReceive(t *testing.T) { require.NoError(t, err) // Verify Signature - tss := mocks.NewTSSMainnet() - verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + verifyTxSender(t, tx, tss.EVMAddress(), evmSigner.EvmSigner()) // check that by default tx type is legacy tx assert.Equal(t, ethtypes.LegacyTxType, int(tx.Type())) @@ -86,7 +85,7 @@ func TestSigner_SignConnectorOnReceive(t *testing.T) { require.NoError(t, err) // ASSERT - verifyTxSignature(t, tx, mocks.NewTSSMainnet().Pubkey(), evmSigner.EvmSigner()) + verifyTxSender(t, tx, tss.EVMAddress(), evmSigner.EvmSigner()) // check that by default tx type is a dynamic fee tx assert.Equal(t, ethtypes.DynamicFeeTxType, int(tx.Type())) @@ -101,7 +100,7 @@ func TestSigner_SignConnectorOnRevert(t *testing.T) { ctx := makeCtx(t) // Setup evm signer - tss := mocks.NewTSSMainnet() + tss := mocks.NewDerivedTSS(chains.BitcoinMainnet) evmSigner, err := getNewEvmSigner(tss) require.NoError(t, err) @@ -118,8 +117,7 @@ func TestSigner_SignConnectorOnRevert(t *testing.T) { require.NoError(t, err) // Verify tx signature - tss := mocks.NewTSSMainnet() - verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + verifyTxSender(t, tx, tss.EVMAddress(), evmSigner.EvmSigner()) // Verify tx body basics // Note: Revert tx calls connector contract with 0 gas token @@ -140,7 +138,7 @@ func TestSigner_SignCancel(t *testing.T) { ctx := makeCtx(t) // Setup evm signer - tss := mocks.NewTSSMainnet() + tss := mocks.NewDerivedTSS(chains.BitcoinMainnet) evmSigner, err := getNewEvmSigner(tss) require.NoError(t, err) @@ -157,12 +155,11 @@ func TestSigner_SignCancel(t *testing.T) { require.NoError(t, err) // Verify tx signature - tss := mocks.NewTSSMainnet() - verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + verifyTxSender(t, tx, tss.EVMAddress(), evmSigner.EvmSigner()) // Verify tx body basics // Note: Cancel tx sends 0 gas token to TSS self address - verifyTxBodyBasics(t, tx, evmSigner.TSS().EVMAddress(), txData.nonce, big.NewInt(0)) + verifyTxBodyBasics(t, tx, tss.EVMAddress(), txData.nonce, big.NewInt(0)) }) t.Run("SignCancel - should fail if keysign fails", func(t *testing.T) { // Pause tss to make keysign fail @@ -179,7 +176,7 @@ func TestSigner_SignGasWithdraw(t *testing.T) { ctx := makeCtx(t) // Setup evm signer - tss := mocks.NewTSSMainnet() + tss := mocks.NewDerivedTSS(chains.BitcoinMainnet) evmSigner, err := getNewEvmSigner(tss) require.NoError(t, err) @@ -196,8 +193,7 @@ func TestSigner_SignGasWithdraw(t *testing.T) { require.NoError(t, err) // Verify tx signature - tss := mocks.NewTSSMainnet() - verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + verifyTxSender(t, tx, tss.EVMAddress(), evmSigner.EvmSigner()) // Verify tx body basics verifyTxBodyBasics(t, tx, txData.to, txData.nonce, txData.amount) @@ -217,7 +213,7 @@ func TestSigner_SignERC20Withdraw(t *testing.T) { ctx := makeCtx(t) // Setup evm signer - tss := mocks.NewTSSMainnet() + tss := mocks.NewDerivedTSS(chains.BitcoinMainnet) evmSigner, err := getNewEvmSigner(tss) require.NoError(t, err) @@ -233,8 +229,7 @@ func TestSigner_SignERC20Withdraw(t *testing.T) { require.NoError(t, err) // Verify tx signature - tss := mocks.NewTSSMainnet() - verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + verifyTxSender(t, tx, tss.EVMAddress(), evmSigner.EvmSigner()) // Verify tx body basics // Note: Withdraw tx calls erc20 custody contract with 0 gas token diff --git a/zetaclient/chains/evm/signer/signer_admin_test.go b/zetaclient/chains/evm/signer/signer_admin_test.go index 6b43653ffb..e5896edcc2 100644 --- a/zetaclient/chains/evm/signer/signer_admin_test.go +++ b/zetaclient/chains/evm/signer/signer_admin_test.go @@ -7,6 +7,7 @@ import ( "github.com/rs/zerolog" "github.com/stretchr/testify/require" + "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/constant" "github.com/zeta-chain/node/testutil/sample" "github.com/zeta-chain/node/zetaclient/testutils/mocks" @@ -16,7 +17,8 @@ func TestSigner_SignAdminTx(t *testing.T) { ctx := makeCtx(t) // Setup evm signer - evmSigner, err := getNewEvmSigner(nil) + tss := mocks.NewDerivedTSS(chains.BitcoinMainnet) + evmSigner, err := getNewEvmSigner(tss) require.NoError(t, err) // Setup txData struct @@ -36,8 +38,7 @@ func TestSigner_SignAdminTx(t *testing.T) { require.NoError(t, err) // Verify tx signature - tss := mocks.NewTSSMainnet() - verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + verifyTxSender(t, tx, tss.EVMAddress(), evmSigner.EvmSigner()) // Verify tx body basics // Note: Revert tx calls erc20 custody contract with 0 gas token @@ -57,8 +58,7 @@ func TestSigner_SignAdminTx(t *testing.T) { require.NoError(t, err) // Verify tx signature - tss := mocks.NewTSSMainnet() - verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + verifyTxSender(t, tx, tss.EVMAddress(), evmSigner.EvmSigner()) // Verify tx body basics // Note: Revert tx calls erc20 custody contract with 0 gas token @@ -73,8 +73,7 @@ func TestSigner_SignAdminTx(t *testing.T) { require.NoError(t, err) // Verify tx signature - tss := mocks.NewTSSMainnet() - verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + verifyTxSender(t, tx, tss.EVMAddress(), evmSigner.EvmSigner()) // Verify tx body basics // Note: Revert tx calls erc20 custody contract with 0 gas token @@ -88,8 +87,7 @@ func TestSigner_SignAdminTx(t *testing.T) { require.NoError(t, err) // Verify tx signature - tss := mocks.NewTSSMainnet() - verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + verifyTxSender(t, tx, tss.EVMAddress(), evmSigner.EvmSigner()) // Verify tx body basics verifyTxBodyBasics(t, tx, txData.to, txData.nonce, txData.amount) @@ -100,7 +98,7 @@ func TestSigner_SignWhitelistERC20Cmd(t *testing.T) { ctx := makeCtx(t) // Setup evm signer - tss := mocks.NewTSSMainnet() + tss := mocks.NewDerivedTSS(chains.BitcoinMainnet) evmSigner, err := getNewEvmSigner(tss) require.NoError(t, err) @@ -121,8 +119,7 @@ func TestSigner_SignWhitelistERC20Cmd(t *testing.T) { require.NotNil(t, tx) // Verify tx signature - tss := mocks.NewTSSMainnet() - verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + verifyTxSender(t, tx, tss.EVMAddress(), evmSigner.EvmSigner()) // Verify tx body basics verifyTxBodyBasics(t, tx, txData.to, txData.nonce, zeroValue) @@ -149,7 +146,7 @@ func TestSigner_SignMigrateERC20CustodyFundsCmd(t *testing.T) { ctx := makeCtx(t) // Setup evm signer - tss := mocks.NewTSSMainnet() + tss := mocks.NewDerivedTSS(chains.BitcoinMainnet) evmSigner, err := getNewEvmSigner(tss) require.NoError(t, err) @@ -178,8 +175,7 @@ func TestSigner_SignMigrateERC20CustodyFundsCmd(t *testing.T) { require.NotNil(t, tx) // Verify tx signature - tss := mocks.NewTSSMainnet() - verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + verifyTxSender(t, tx, tss.EVMAddress(), evmSigner.EvmSigner()) // Verify tx body basics verifyTxBodyBasics(t, tx, txData.to, txData.nonce, zeroValue) @@ -215,7 +211,7 @@ func TestSigner_SignUpdateERC20CustodyPauseStatusCmd(t *testing.T) { ctx := makeCtx(t) // Setup evm signer - tss := mocks.NewTSSMainnet() + tss := mocks.NewDerivedTSS(chains.BitcoinMainnet) evmSigner, err := getNewEvmSigner(tss) require.NoError(t, err) @@ -238,8 +234,7 @@ func TestSigner_SignUpdateERC20CustodyPauseStatusCmd(t *testing.T) { require.NotNil(t, tx) // Verify tx signature - tss := mocks.NewTSSMainnet() - verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + verifyTxSender(t, tx, tss.EVMAddress(), evmSigner.EvmSigner()) // Verify tx body basics verifyTxBodyBasics(t, tx, txData.to, txData.nonce, zeroValue) @@ -255,8 +250,7 @@ func TestSigner_SignUpdateERC20CustodyPauseStatusCmd(t *testing.T) { require.NotNil(t, tx) // Verify tx signature - tss := mocks.NewTSSMainnet() - verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + verifyTxSender(t, tx, tss.EVMAddress(), evmSigner.EvmSigner()) // Verify tx body basics verifyTxBodyBasics(t, tx, txData.to, txData.nonce, zeroValue) @@ -293,7 +287,7 @@ func TestSigner_SignMigrateTssFundsCmd(t *testing.T) { ctx := makeCtx(t) // Setup evm signer - tss := mocks.NewTSSMainnet() + tss := mocks.NewDerivedTSS(chains.BitcoinMainnet) evmSigner, err := getNewEvmSigner(tss) require.NoError(t, err) @@ -313,8 +307,7 @@ func TestSigner_SignMigrateTssFundsCmd(t *testing.T) { require.NotNil(t, tx) // Verify tx signature - tss := mocks.NewTSSMainnet() - verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + verifyTxSender(t, tx, tss.EVMAddress(), evmSigner.EvmSigner()) // Verify tx body basics verifyTxBodyBasics(t, tx, txData.to, txData.nonce, txData.amount) diff --git a/zetaclient/chains/evm/signer/signer_test.go b/zetaclient/chains/evm/signer/signer_test.go index b2c266b438..43577b475d 100644 --- a/zetaclient/chains/evm/signer/signer_test.go +++ b/zetaclient/chains/evm/signer/signer_test.go @@ -8,7 +8,6 @@ import ( sdktypes "github.com/cosmos/cosmos-sdk/types" ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" "github.com/rs/zerolog" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -113,14 +112,14 @@ func getInvalidCCTX(t *testing.T) *crosschaintypes.CrossChainTx { return cctx } -// verifyTxSignature is a helper function to verify the signature of a transaction -func verifyTxSignature(t *testing.T, tx *ethtypes.Transaction, tssPubkey []byte, signer ethtypes.Signer) { - _, r, s := tx.RawSignatureValues() - signature := append(r.Bytes(), s.Bytes()...) - hash := signer.Hash(tx) - - verified := crypto.VerifySignature(tssPubkey, hash.Bytes(), signature) - require.True(t, verified) +// verifyTxSender is a helper function to verify the signature of a transaction +// +// signer.Sender() will ecrecover the public key of the transaction internally +// and will fail if the transaction is not valid or has been tampered with +func verifyTxSender(t *testing.T, tx *ethtypes.Transaction, expectedSender ethcommon.Address, signer ethtypes.Signer) { + senderAddr, err := signer.Sender(tx) + require.NoError(t, err) + require.Equal(t, expectedSender.String(), senderAddr.String()) } // verifyTxBodyBasics is a helper function to verify 'to', 'nonce' and 'amount' of a transaction diff --git a/zetaclient/testutils/mocks/tss_signer.go b/zetaclient/testutils/mocks/tss_signer.go index 78ce36649a..3fa003a3c0 100644 --- a/zetaclient/testutils/mocks/tss_signer.go +++ b/zetaclient/testutils/mocks/tss_signer.go @@ -63,6 +63,16 @@ func NewTSSAthens3() *TSS { return NewMockTSS(chains.BscTestnet, testutils.TSSAddressEVMAthens3, testutils.TSSAddressBTCAthens3) } +// NewDerivedTSS creates a TSS where evmAddress and btcAdresses are always derived from the test +// private key +func NewDerivedTSS(chain chains.Chain) *TSS { + return &TSS{ + paused: false, + chain: chain, + PrivKey: TestPrivateKey, + } +} + func NewGeneratedTSS(t *testing.T, chain chains.Chain) *TSS { pk, err := crypto.GenerateKey() require.NoError(t, err) From 07120f14f6403e2f6b651eef576598e61d76f187 Mon Sep 17 00:00:00 2001 From: Charlie Chen <34498985+ws4charlie@users.noreply.github.com> Date: Thu, 14 Nov 2024 10:30:03 -0600 Subject: [PATCH 10/14] test: configure Solana gateway program id for E2E tests (#3154) * configure Solana gateway program id in E2E test * add changelog entry --- changelog.md | 1 + cmd/zetae2e/config/config.go | 2 +- cmd/zetae2e/config/contracts.go | 3 +-- cmd/zetae2e/config/local.yml | 5 ++++- cmd/zetae2e/config/localnet.yml | 6 +++++- cmd/zetae2e/local/local.go | 5 ++++- e2e/config/config.go | 2 +- e2e/runner/setup_solana.go | 6 +++--- e2e/runner/solana.go | 6 ++---- pkg/contracts/solana/gateway.go | 9 ++++++--- pkg/contracts/solana/inbound_test.go | 4 ++-- zetaclient/chains/solana/observer/inbound_test.go | 2 +- zetaclient/orchestrator/orchestrator_test.go | 5 ++--- zetaclient/testutils/constant.go | 10 ++++++---- 14 files changed, 39 insertions(+), 27 deletions(-) diff --git a/changelog.md b/changelog.md index 244bc9d08d..07b7d10bc5 100644 --- a/changelog.md +++ b/changelog.md @@ -12,6 +12,7 @@ * [3075](https://github.com/zeta-chain/node/pull/3075) - ton: withdraw concurrent, deposit & revert. * [3105](https://github.com/zeta-chain/node/pull/3105) - split Bitcoin E2E tests into two runners for deposit and withdraw +* [3154](https://github.com/zeta-chain/node/pull/3154) - configure Solana gateway program id for E2E tests ### Refactor * [3118](https://github.com/zeta-chain/node/pull/3118) - zetaclient: remove hsm signer diff --git a/cmd/zetae2e/config/config.go b/cmd/zetae2e/config/config.go index 65e5418f53..cffee5943b 100644 --- a/cmd/zetae2e/config/config.go +++ b/cmd/zetae2e/config/config.go @@ -55,7 +55,7 @@ func RunnerFromConfig( // ExportContractsFromRunner export contracts from the runner to config using a source config func ExportContractsFromRunner(r *runner.E2ERunner, conf config.Config) config.Config { // copy contracts from deployer runner - conf.Contracts.Solana.GatewayProgramID = r.GatewayProgram.String() + conf.Contracts.Solana.GatewayProgramID = config.DoubleQuotedString(r.GatewayProgram.String()) conf.Contracts.Solana.SPLAddr = config.DoubleQuotedString(r.SPLAddr.String()) conf.Contracts.EVM.ZetaEthAddr = config.DoubleQuotedString(r.ZetaEthAddr.Hex()) diff --git a/cmd/zetae2e/config/contracts.go b/cmd/zetae2e/config/contracts.go index 5c46cdc047..6b508096b5 100644 --- a/cmd/zetae2e/config/contracts.go +++ b/cmd/zetae2e/config/contracts.go @@ -31,7 +31,7 @@ func setContractsFromConfig(r *runner.E2ERunner, conf config.Config) error { // set Solana contracts if c := conf.Contracts.Solana.GatewayProgramID; c != "" { - r.GatewayProgram = solana.MustPublicKeyFromBase58(c) + r.GatewayProgram = solana.MustPublicKeyFromBase58(c.String()) } if c := conf.Contracts.Solana.SPLAddr; c != "" { @@ -242,7 +242,6 @@ func setContractsFromConfig(r *runner.E2ERunner, conf config.Config) error { } // v2 contracts - if c := conf.Contracts.EVM.Gateway; c != "" { r.GatewayEVMAddr, err = c.AsEVMAddress() if err != nil { diff --git a/cmd/zetae2e/config/local.yml b/cmd/zetae2e/config/local.yml index 5cc87be394..e4ec147e49 100644 --- a/cmd/zetae2e/config/local.yml +++ b/cmd/zetae2e/config/local.yml @@ -116,4 +116,7 @@ contracts: custody: "0xff3135df4F2775f4091b81f4c7B6359CfA07862a" erc20: "0xbD1e64A22B9F92D9Ce81aA9B4b0fFacd80215564" test_dapp: "0xBFF76e77D56B3C1202107f059425D56f0AEF87Ed" - gateway: "0xF0deebCB0E9C829519C4baa794c5445171973826" \ No newline at end of file + gateway: "0xF0deebCB0E9C829519C4baa794c5445171973826" + solana: + gateway_program_id: "94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d" + spl: "" \ No newline at end of file diff --git a/cmd/zetae2e/config/localnet.yml b/cmd/zetae2e/config/localnet.yml index 24e51223ef..15f1e332f6 100644 --- a/cmd/zetae2e/config/localnet.yml +++ b/cmd/zetae2e/config/localnet.yml @@ -98,4 +98,8 @@ rpcs: ton_sidecar_url: "http://ton:8000" zetacore_grpc: "zetacore0:9090" zetacore_rpc: "http://zetacore0:26657" -# contracts will be populated on first run +contracts: +# configure localnet solana gateway program id + solana: + gateway_program_id: "94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d" + spl: "" \ No newline at end of file diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index 5507d95014..6dc466cef8 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -224,7 +224,10 @@ func localE2ETest(cmd *cobra.Command, _ []string) { deployerRunner.SetupEVMV2() if testSolana { - deployerRunner.SetupSolana(conf.AdditionalAccounts.UserSolana.SolanaPrivateKey.String()) + deployerRunner.SetupSolana( + conf.Contracts.Solana.GatewayProgramID.String(), + conf.AdditionalAccounts.UserSolana.SolanaPrivateKey.String(), + ) } deployerRunner.SetZEVMSystemContracts() diff --git a/e2e/config/config.go b/e2e/config/config.go index 33a8c2bea8..22382c1fa1 100644 --- a/e2e/config/config.go +++ b/e2e/config/config.go @@ -120,7 +120,7 @@ type Contracts struct { // Solana contains the addresses of predeployed contracts and accounts on the Solana chain type Solana struct { - GatewayProgramID string `yaml:"gateway_program_id"` + GatewayProgramID DoubleQuotedString `yaml:"gateway_program_id"` SPLAddr DoubleQuotedString `yaml:"spl"` } diff --git a/e2e/runner/setup_solana.go b/e2e/runner/setup_solana.go index 1a46326d02..28a4fb65fa 100644 --- a/e2e/runner/setup_solana.go +++ b/e2e/runner/setup_solana.go @@ -27,11 +27,11 @@ func (r *E2ERunner) SetupSolanaAccount() { } // SetupSolana sets Solana contracts and params -func (r *E2ERunner) SetupSolana(deployerPrivateKey string) { +func (r *E2ERunner) SetupSolana(gatewayID, deployerPrivateKey string) { r.Logger.Print("⚙️ initializing gateway program on Solana") // set Solana contracts - r.GatewayProgram = solana.MustPublicKeyFromBase58(solanacontracts.SolanaGatewayProgramID) + r.GatewayProgram = solana.MustPublicKeyFromBase58(gatewayID) // get deployer account balance privkey, err := solana.PrivateKeyFromBase58(deployerPrivateKey) @@ -141,7 +141,7 @@ func (r *E2ERunner) ensureSolanaChainParams() error { BallotThreshold: observertypes.DefaultBallotThreshold, MinObserverDelegation: observertypes.DefaultMinObserverDelegation, IsSupported: true, - GatewayAddress: solanacontracts.SolanaGatewayProgramID, + GatewayAddress: r.GatewayProgram.String(), } updateMsg := observertypes.NewMsgUpdateChainParams(creator, chainParams) diff --git a/e2e/runner/solana.go b/e2e/runner/solana.go index d395c60d76..380219dee4 100644 --- a/e2e/runner/solana.go +++ b/e2e/runner/solana.go @@ -21,8 +21,7 @@ import ( // ComputePdaAddress computes the PDA address for the gateway program func (r *E2ERunner) ComputePdaAddress() solana.PublicKey { seed := []byte(solanacontract.PDASeed) - GatewayProgramID := solana.MustPublicKeyFromBase58(solanacontract.SolanaGatewayProgramID) - pdaComputed, bump, err := solana.FindProgramAddress([][]byte{seed}, GatewayProgramID) + pdaComputed, bump, err := solana.FindProgramAddress([][]byte{seed}, r.GatewayProgram) require.NoError(r, err) r.Logger.Info("computed pda: %s, bump %d\n", pdaComputed, bump) @@ -33,8 +32,7 @@ func (r *E2ERunner) ComputePdaAddress() solana.PublicKey { // SolanaRentPayerPDA computes the rent payer PDA (Program Derived Address) address for the gateway program func (r *E2ERunner) SolanaRentPayerPDA() solana.PublicKey { seed := []byte(solanacontract.RentPayerPDASeed) - GatewayProgramID := solana.MustPublicKeyFromBase58(solanacontract.SolanaGatewayProgramID) - pdaComputed, bump, err := solana.FindProgramAddress([][]byte{seed}, GatewayProgramID) + pdaComputed, bump, err := solana.FindProgramAddress([][]byte{seed}, r.GatewayProgram) require.NoError(r, err) r.Logger.Info("computed rent payer pda: %s, bump %d\n", pdaComputed, bump) diff --git a/pkg/contracts/solana/gateway.go b/pkg/contracts/solana/gateway.go index bf674aa081..7465893149 100644 --- a/pkg/contracts/solana/gateway.go +++ b/pkg/contracts/solana/gateway.go @@ -8,9 +8,6 @@ import ( ) const ( - // SolanaGatewayProgramID is the program ID of the Solana gateway program - SolanaGatewayProgramID = "94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d" - // PDASeed is the seed for the Solana gateway program derived address PDASeed = "meta" @@ -29,16 +26,22 @@ const ( var ( // DiscriminatorInitialize returns the discriminator for Solana gateway 'initialize' instruction DiscriminatorInitialize = idlgateway.IDLGateway.GetDiscriminator("initialize") + // DiscriminatorInitializeRentPayer returns the discriminator for Solana gateway 'initialize_rent_payer' instruction DiscriminatorInitializeRentPayer = idlgateway.IDLGateway.GetDiscriminator("initialize_rent_payer") + // DiscriminatorDeposit returns the discriminator for Solana gateway 'deposit' instruction DiscriminatorDeposit = idlgateway.IDLGateway.GetDiscriminator("deposit") + // DiscriminatorDepositSPL returns the discriminator for Solana gateway 'deposit_spl_token' instruction DiscriminatorDepositSPL = idlgateway.IDLGateway.GetDiscriminator("deposit_spl_token") + // DiscriminatorWithdraw returns the discriminator for Solana gateway 'withdraw' instruction DiscriminatorWithdraw = idlgateway.IDLGateway.GetDiscriminator("withdraw") + // DiscriminatorWithdrawSPL returns the discriminator for Solana gateway 'withdraw_spl_token' instruction DiscriminatorWithdrawSPL = idlgateway.IDLGateway.GetDiscriminator("withdraw_spl_token") + // DiscriminatorWhitelist returns the discriminator for Solana gateway 'whitelist_spl_mint' instruction DiscriminatorWhitelistSplMint = idlgateway.IDLGateway.GetDiscriminator("whitelist_spl_mint") ) diff --git a/pkg/contracts/solana/inbound_test.go b/pkg/contracts/solana/inbound_test.go index 00bb17f994..7b19badf7a 100644 --- a/pkg/contracts/solana/inbound_test.go +++ b/pkg/contracts/solana/inbound_test.go @@ -47,7 +47,7 @@ func Test_ParseInboundAsDeposit(t *testing.T) { // create observer chainParams := sample.ChainParams(chain.ChainId) - chainParams.GatewayAddress = testutils.GatewayAddresses[chain.ChainId] + chainParams.GatewayAddress = testutils.OldSolanaGatewayAddressDevnet require.NoError(t, err) // expected result @@ -79,7 +79,7 @@ func Test_ParseInboundAsDepositSPL(t *testing.T) { // create observer chainParams := sample.ChainParams(chain.ChainId) - chainParams.GatewayAddress = testutils.GatewayAddresses[chain.ChainId] + chainParams.GatewayAddress = testutils.OldSolanaGatewayAddressDevnet // expected result // solana e2e deployer account diff --git a/zetaclient/chains/solana/observer/inbound_test.go b/zetaclient/chains/solana/observer/inbound_test.go index c38d3ae281..28c31f04db 100644 --- a/zetaclient/chains/solana/observer/inbound_test.go +++ b/zetaclient/chains/solana/observer/inbound_test.go @@ -71,7 +71,7 @@ func Test_FilterInboundEvents(t *testing.T) { // create observer chainParams := sample.ChainParams(chain.ChainId) - chainParams.GatewayAddress = testutils.GatewayAddresses[chain.ChainId] + chainParams.GatewayAddress = testutils.OldSolanaGatewayAddressDevnet ob, err := observer.NewObserver(chain, nil, *chainParams, nil, nil, 60, database, base.DefaultLogger(), nil) require.NoError(t, err) diff --git a/zetaclient/orchestrator/orchestrator_test.go b/zetaclient/orchestrator/orchestrator_test.go index b86e85cc15..4b85ccbb03 100644 --- a/zetaclient/orchestrator/orchestrator_test.go +++ b/zetaclient/orchestrator/orchestrator_test.go @@ -14,7 +14,6 @@ import ( "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/coin" - solanacontracts "github.com/zeta-chain/node/pkg/contracts/solana" "github.com/zeta-chain/node/testutil/sample" crosschainkeeper "github.com/zeta-chain/node/x/crosschain/keeper" crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" @@ -41,7 +40,7 @@ func Test_GetUpdatedSigner(t *testing.T) { tonChainParams = mocks.MockChainParams(tonChain.ChainId, 100) ) - solChainParams.GatewayAddress = solanacontracts.SolanaGatewayProgramID + solChainParams.GatewayAddress = testutils.GatewayAddresses[solChain.ChainId] // new chain params in AppContext evmChainParamsNew := mocks.MockChainParams(evmChainParams.ChainId, 100) @@ -126,7 +125,7 @@ func Test_GetUpdatedChainObserver(t *testing.T) { tonChainParams = mocks.MockChainParams(tonChain.ChainId, 100) ) - solChainParams.GatewayAddress = solanacontracts.SolanaGatewayProgramID + solChainParams.GatewayAddress = testutils.GatewayAddresses[solChain.ChainId] tonChainParams.GatewayAddress = sample.GenerateTONAccountID().ToRaw() // new chain params in AppContext diff --git a/zetaclient/testutils/constant.go b/zetaclient/testutils/constant.go index f776c7019f..304cd859c3 100644 --- a/zetaclient/testutils/constant.go +++ b/zetaclient/testutils/constant.go @@ -39,12 +39,14 @@ const ( EventERC20Withdraw = "Withdrawn" ) +// OldSolanaGatewayAddressDevnet is the old gateway address deployed on Solana devnet +const OldSolanaGatewayAddressDevnet = "94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d" + // GatewayAddresses contains constants gateway addresses for testing var GatewayAddresses = map[int64]string{ - // Gateway address on Solana devnet - // NOTE: currently different deployer key pair is used for development compared to live networks - // as live networks key pair is sensitive information at this point, can be unified once we have deployments completed - chains.SolanaDevnet.ChainId: "94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d", + // Solana gateway addresses + chains.SolanaDevnet.ChainId: "ZETAjseVjuFsxdRxo6MmTCvqFwb3ZHUx56Co3vCmGis", + chains.SolanaMainnet.ChainId: "ZETAjseVjuFsxdRxo6MmTCvqFwb3ZHUx56Co3vCmGis", } // ConnectorAddresses contains constants ERC20 connector addresses for testing From 420f83a25ff17eafd573aab32844fc28f5487d99 Mon Sep 17 00:00:00 2001 From: Charlie Chen <34498985+ws4charlie@users.noreply.github.com> Date: Thu, 14 Nov 2024 11:35:24 -0600 Subject: [PATCH 11/14] fix: abort cctx on dust amount in revert outbound (Bitcoin and Solana) (#3149) * fix dust amount for revert case in Bitcoin and Solana * add changelog entry for the fix * verify dust amount in the CCTX revert outbound * add error message check for aborted CCTX caused by dust amount * Update changelog.md Co-authored-by: Tanmay --------- Co-authored-by: Tanmay --- changelog.md | 1 + cmd/zetae2e/local/local.go | 3 +- e2e/e2etests/e2etests.go | 33 +++++++++++------- ...tcoin_deposit_and_call_revert_with_dust.go | 18 +++++----- ...olana_deposit_and_call_revert_with_dust.go | 34 +++++++++++++++++++ e2e/e2etests/test_solana_deposit_refund.go | 4 +-- e2e/utils/zetacore.go | 14 ++++++++ .../cctx_orchestrator_validate_outbound.go | 13 +++++++ 8 files changed, 95 insertions(+), 25 deletions(-) create mode 100644 e2e/e2etests/test_solana_deposit_and_call_revert_with_dust.go diff --git a/changelog.md b/changelog.md index 07b7d10bc5..4facc05f04 100644 --- a/changelog.md +++ b/changelog.md @@ -26,6 +26,7 @@ * [3041](https://github.com/zeta-chain/node/pull/3041) - replace libp2p public DHT with private gossip peer discovery and connection gater for inbound connections * [3106](https://github.com/zeta-chain/node/pull/3106) - prevent blocked CCTX on out of gas during omnichain calls * [3139](https://github.com/zeta-chain/node/pull/3139) - fix config resolution in orchestrator +* [3149](https://github.com/zeta-chain/node/pull/3149) - abort the cctx if dust amount is detected in the revert outbound ## v21.0.0 diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index 6dc466cef8..a752c0e388 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -436,7 +436,8 @@ func localE2ETest(cmd *cobra.Command, _ []string) { e2etests.TestSolanaDepositName, e2etests.TestSolanaWithdrawName, e2etests.TestSolanaDepositAndCallName, - e2etests.TestSolanaDepositAndCallRefundName, + e2etests.TestSolanaDepositAndCallRevertName, + e2etests.TestSolanaDepositAndCallRevertWithDustName, e2etests.TestSolanaDepositRestrictedName, e2etests.TestSolanaWithdrawRestrictedName, // TODO move under admin tests diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index ee4b8d92ce..52c7022be5 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -55,16 +55,17 @@ const ( /* * Solana tests */ - TestSolanaDepositName = "solana_deposit" - TestSolanaWithdrawName = "solana_withdraw" - TestSolanaDepositAndCallName = "solana_deposit_and_call" - TestSolanaDepositAndCallRefundName = "solana_deposit_and_call_refund" - TestSolanaDepositRestrictedName = "solana_deposit_restricted" - TestSolanaWithdrawRestrictedName = "solana_withdraw_restricted" - TestSPLDepositName = "spl_deposit" - TestSPLDepositAndCallName = "spl_deposit_and_call" - TestSPLWithdrawName = "spl_withdraw" - TestSPLWithdrawAndCreateReceiverAtaName = "spl_withdraw_and_create_receiver_ata" + TestSolanaDepositName = "solana_deposit" + TestSolanaWithdrawName = "solana_withdraw" + TestSolanaDepositAndCallName = "solana_deposit_and_call" + TestSolanaDepositAndCallRevertName = "solana_deposit_and_call_revert" + TestSolanaDepositAndCallRevertWithDustName = "solana_deposit_and_call_revert_with_dust" + TestSolanaDepositRestrictedName = "solana_deposit_restricted" + TestSolanaWithdrawRestrictedName = "solana_withdraw_restricted" + TestSPLDepositName = "spl_deposit" + TestSPLDepositAndCallName = "spl_deposit_and_call" + TestSPLWithdrawName = "spl_withdraw" + TestSPLWithdrawAndCreateReceiverAtaName = "spl_withdraw_and_create_receiver_ata" /** * TON tests @@ -453,12 +454,18 @@ var AllE2ETests = []runner.E2ETest{ TestSPLWithdrawAndCreateReceiverAta, ), runner.NewE2ETest( - TestSolanaDepositAndCallRefundName, - "deposit SOL into ZEVM and call a contract that reverts; should refund", + TestSolanaDepositAndCallRevertName, + "deposit SOL into ZEVM and call a contract that reverts", []runner.ArgDefinition{ {Description: "amount in lamport", DefaultValue: "1200000"}, }, - TestSolanaDepositAndCallRefund, + TestSolanaDepositAndCallRevert, + ), + runner.NewE2ETest( + TestSolanaDepositAndCallRevertWithDustName, + "deposit SOL into ZEVM; revert with dust amount that aborts the CCTX", + []runner.ArgDefinition{}, + TestSolanaDepositAndCallRevertWithDust, ), runner.NewE2ETest( TestSolanaDepositRestrictedName, diff --git a/e2e/e2etests/test_bitcoin_deposit_and_call_revert_with_dust.go b/e2e/e2etests/test_bitcoin_deposit_and_call_revert_with_dust.go index 1dec399a9b..cc5f5451dc 100644 --- a/e2e/e2etests/test_bitcoin_deposit_and_call_revert_with_dust.go +++ b/e2e/e2etests/test_bitcoin_deposit_and_call_revert_with_dust.go @@ -1,6 +1,8 @@ package e2etests import ( + "strings" + "github.com/stretchr/testify/require" "github.com/zeta-chain/node/e2e/runner" @@ -38,16 +40,14 @@ func TestBitcoinDepositAndCallRevertWithDust(r *runner.E2ERunner, args []string) // Send BTC to TSS address with a dummy memo // zetacore should revert cctx if call is made on a non-existing address nonExistReceiver := sample.EthAddress() - badMemo := append(nonExistReceiver.Bytes(), []byte("gibberish-memo")...) - txHash, err := r.SendToTSSFromDeployerWithMemo(amount, utxos, badMemo) + anyMemo := append(nonExistReceiver.Bytes(), []byte("gibberish-memo")...) + txHash, err := r.SendToTSSFromDeployerWithMemo(amount, utxos, anyMemo) require.NoError(r, err) require.NotEmpty(r, txHash) - // wait for the cctx to be mined - cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, txHash.String(), r.CctxClient, r.Logger, r.CctxTimeout) - r.Logger.CCTX(*cctx, "deposit_and_revert") - utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_Reverted) - - // check the test was effective: the revert outbound amount is less than the dust amount - require.Less(r, cctx.GetCurrentOutboundParam().Amount.Uint64(), uint64(constant.BTCWithdrawalDustAmount)) + // ASSERT + // Now we want to make sure the cctx is aborted with expected error message + cctx := utils.WaitCctxAbortedByInboundHash(r.Ctx, r, txHash.String(), r.CctxClient) + require.True(r, cctx.GetCurrentOutboundParam().Amount.Uint64() < constant.BTCWithdrawalDustAmount) + require.True(r, strings.Contains(cctx.CctxStatus.ErrorMessage, crosschaintypes.ErrInvalidWithdrawalAmount.Error())) } diff --git a/e2e/e2etests/test_solana_deposit_and_call_revert_with_dust.go b/e2e/e2etests/test_solana_deposit_and_call_revert_with_dust.go new file mode 100644 index 0000000000..2ffe5ce9c6 --- /dev/null +++ b/e2e/e2etests/test_solana_deposit_and_call_revert_with_dust.go @@ -0,0 +1,34 @@ +package e2etests + +import ( + "math/big" + "strings" + + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/node/e2e/runner" + "github.com/zeta-chain/node/e2e/utils" + "github.com/zeta-chain/node/pkg/constant" + "github.com/zeta-chain/node/testutil/sample" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" +) + +// TestSolanaDepositAndCallRevertWithDust tests Solana deposit and call that reverts with a dust amount in the revert outbound. +func TestSolanaDepositAndCallRevertWithDust(r *runner.E2ERunner, args []string) { + require.Len(r, args, 0) + + // deposit the rent exempt amount which will result in a dust amount (after fee deduction) in the revert outbound + depositAmount := big.NewInt(constant.SolanaWalletRentExempt) + + // ACT + // execute the deposit and call transaction + nonExistReceiver := sample.EthAddress() + data := []byte("dust lamports should abort cctx") + sig := r.SOLDepositAndCall(nil, nonExistReceiver, depositAmount, data) + + // ASSERT + // Now we want to make sure cctx is aborted. + cctx := utils.WaitCctxAbortedByInboundHash(r.Ctx, r, sig.String(), r.CctxClient) + require.True(r, cctx.GetCurrentOutboundParam().Amount.Uint64() < constant.SolanaWalletRentExempt) + require.True(r, strings.Contains(cctx.CctxStatus.ErrorMessage, crosschaintypes.ErrInvalidWithdrawalAmount.Error())) +} diff --git a/e2e/e2etests/test_solana_deposit_refund.go b/e2e/e2etests/test_solana_deposit_refund.go index 0a62b70ac6..c8724e72f8 100644 --- a/e2e/e2etests/test_solana_deposit_refund.go +++ b/e2e/e2etests/test_solana_deposit_refund.go @@ -9,8 +9,8 @@ import ( crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" ) -// TestSolanaDepositAndCallRefund tests deposit of lamports calling a example contract -func TestSolanaDepositAndCallRefund(r *runner.E2ERunner, args []string) { +// TestSolanaDepositAndCallRevert tests deposit of lamports calling a example contract that reverts. +func TestSolanaDepositAndCallRevert(r *runner.E2ERunner, args []string) { require.Len(r, args, 1) // parse deposit amount (in lamports) diff --git a/e2e/utils/zetacore.go b/e2e/utils/zetacore.go index 0b4d5217ed..34e53e61b0 100644 --- a/e2e/utils/zetacore.go +++ b/e2e/utils/zetacore.go @@ -227,6 +227,20 @@ func WaitCctxRevertedByInboundHash( return cctxs[0] } +// WaitCctxAbortedByInboundHash waits until cctx is aborted by inbound hash. +func WaitCctxAbortedByInboundHash( + ctx context.Context, + t require.TestingT, + hash string, + c CCTXClient, +) crosschaintypes.CrossChainTx { + // wait for cctx to be aborted + cctxs := WaitCctxByInboundHash(ctx, t, hash, c, MatchStatus(crosschaintypes.CctxStatus_Aborted)) + require.Len(t, cctxs, 1) + + return cctxs[0] +} + // WaitCctxByInboundHash waits until cctx appears by inbound hash. func WaitCctxByInboundHash( ctx context.Context, diff --git a/x/crosschain/keeper/cctx_orchestrator_validate_outbound.go b/x/crosschain/keeper/cctx_orchestrator_validate_outbound.go index 2f3acdd744..52c8c1e10d 100644 --- a/x/crosschain/keeper/cctx_orchestrator_validate_outbound.go +++ b/x/crosschain/keeper/cctx_orchestrator_validate_outbound.go @@ -182,6 +182,7 @@ func (k Keeper) processFailedOutboundOnExternalChain( return cosmoserrors.Wrap(err, "AddRevertOutbound") } + // pay revert outbound gas fee err = k.PayGasAndUpdateCctx( ctx, cctx.InboundParams.SenderChainId, @@ -192,6 +193,18 @@ func (k Keeper) processFailedOutboundOnExternalChain( if err != nil { return err } + + // validate data of the revert outbound + err = k.validateZRC20Withdrawal( + ctx, + cctx.GetCurrentOutboundParam().ReceiverChainId, + cctx.GetCurrentOutboundParam().Amount.BigInt(), + []byte(cctx.GetCurrentOutboundParam().Receiver), + ) + if err != nil { + return err + } + err = k.SetObserverOutboundInfo(ctx, cctx.InboundParams.SenderChainId, cctx) if err != nil { return err From 0b6addd2effd03c87d4edfd6b223ddbc8d199114 Mon Sep 17 00:00:00 2001 From: Christopher Fuka <97121270+CryptoFewka@users.noreply.github.com> Date: Thu, 14 Nov 2024 13:25:00 -0600 Subject: [PATCH 12/14] Bump download-artifact to v4 (#3104) --- .github/actions/upgrade-testing/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/upgrade-testing/action.yml b/.github/actions/upgrade-testing/action.yml index 55ba9f8524..9c3eeaf58d 100644 --- a/.github/actions/upgrade-testing/action.yml +++ b/.github/actions/upgrade-testing/action.yml @@ -14,7 +14,7 @@ runs: with: python-version: 'pypy3.9' - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 #v3 reaches deprecation on November 30, 2024 with: name: binaries-${{ github.sha }} path: ./ From 6fb64cafa758d8b3a1b0df9fb2625b8fefcefb99 Mon Sep 17 00:00:00 2001 From: Alex Gartner Date: Thu, 14 Nov 2024 11:59:19 -0800 Subject: [PATCH 13/14] refactor: use major version in release upgrade handlers (#3159) * refactor: use major version in release upgrade handlers * use + rather than - for semver compliance * fmt * docs * fix lint * more semver 2.0 compliance * remove check-upgrade-handler-updated --- .github/workflows/publish-release.yml | 33 ------------------- app/setup_handlers.go | 26 +++++++++++++-- cmd/zetacored/root.go | 1 + cmd/zetacored/version.go | 19 +++++++++++ .../scripts/start-upgrade-orchestrator.sh | 2 +- docs/cli/zetacored/cli.md | 29 ++++++++++++++++ version.sh | 4 +-- 7 files changed, 75 insertions(+), 39 deletions(-) create mode 100644 cmd/zetacored/version.go diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index a8b324de6b..924706af29 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -94,38 +94,6 @@ jobs: run: | echo "continue" - check-upgrade-handler-updated: - needs: - - check-branch - runs-on: ubuntu-22.04 - timeout-minutes: 10 - steps: - - - uses: actions/checkout@v4 - if: inputs.skip_checks != true - with: - fetch-depth: 0 - - - name: Major Version in Upgrade Handler Must Match Tag - if: inputs.skip_checks != true - run: | - UPGRADE_HANDLER_MAJOR_VERSION=$(cat app/setup_handlers.go | grep "const releaseVersion" | cut -d ' ' -f4 | tr -d '"' | cut -d '.' -f 1 | tr -d '\n') - USER_INPUT_VERSION=$(echo "${{ inputs.version }}" | cut -d '.' -f 1 | tr -d '\n') - echo "Upgrade Handler Major Version: ${UPGRADE_HANDLER_MAJOR_VERSION}" - echo "User Inputted Release Version: ${USER_INPUT_VERSION}" - if [ ${USER_INPUT_VERSION} != $UPGRADE_HANDLER_MAJOR_VERSION ]; then - echo "ERROR: The input version doesn't match the release handler for the branch selected. Please ensure the upgrade handler of the branch you selected when you ran the pipeline matches the input version." - echo "Did you forget to update the 'releaseVersion' in app/setup_handlers.go?" - exit 1 - fi - echo "The major version found in 'releaseVersion' in app/setup_handlers.go matches this tagged release - Moving Forward!" - - - name: Mark Job Complete Skipped - if: inputs.skip_checks == true - shell: bash - run: | - echo "continue" - publish-release: permissions: id-token: write @@ -134,7 +102,6 @@ jobs: if: inputs.skip_release != true needs: - check-changelog - - check-upgrade-handler-updated - check-branch - check-goreleaser runs-on: ${{ vars.RELEASE_RUNNER }} diff --git a/app/setup_handlers.go b/app/setup_handlers.go index 04a94da668..1468f83fe2 100644 --- a/app/setup_handlers.go +++ b/app/setup_handlers.go @@ -7,10 +7,28 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/upgrade/types" + "golang.org/x/mod/semver" "github.com/zeta-chain/node/pkg/constant" ) +// GetDefaultUpgradeHandlerVersion prints the default upgrade handler version +// +// There may be multiple upgrade handlers configured on some releases if different +// migrations needto be run in different environment +func GetDefaultUpgradeHandlerVersion() string { + // semver must have v prefix, but we store without prefix + vVersion := "v" + constant.Version + + // development builds always use the full version in the release handlers + if semver.Build(vVersion) != "" || semver.Prerelease(vVersion) != "" { + return constant.Version + } + + // release builds use just the major version (v22.0.0 -> v22) + return semver.Major(vVersion) +} + func SetupHandlers(app *App) { allUpgrades := upgradeTracker{ upgrades: []upgradeTrackerItem{ @@ -50,10 +68,12 @@ func SetupHandlers(app *App) { upgradeHandlerFns, storeUpgrades = allUpgrades.mergeAllUpgrades() } + upgradeHandlerVersion := GetDefaultUpgradeHandlerVersion() + app.UpgradeKeeper.SetUpgradeHandler( - constant.Version, + upgradeHandlerVersion, func(ctx sdk.Context, _ types.Plan, vm module.VersionMap) (module.VersionMap, error) { - app.Logger().Info("Running upgrade handler for " + constant.Version) + app.Logger().Info("Running upgrade handler for " + upgradeHandlerVersion) var err error for _, upgradeHandler := range upgradeHandlerFns { @@ -71,7 +91,7 @@ func SetupHandlers(app *App) { if err != nil { panic(err) } - if upgradeInfo.Name == constant.Version && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Height) { + if upgradeInfo.Name == upgradeHandlerVersion && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Height) { // Use upgrade store loader for the initial loading of all stores when app starts, // it checks if version == upgradeHeight and applies store upgrades before loading the stores, // so that new stores start with the correct version (the current height of chain), diff --git a/cmd/zetacored/root.go b/cmd/zetacored/root.go index 93ae4ba469..1f7115e57a 100644 --- a/cmd/zetacored/root.go +++ b/cmd/zetacored/root.go @@ -145,6 +145,7 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig types.EncodingConfig) { GetPubKeyCmd(), CollectObserverInfoCmd(), AddrConversionCmd(), + UpgradeHandlerVersionCmd(), tmcli.NewCompletionCmd(rootCmd, true), ethermintclient.NewTestnetCmd(app.ModuleBasics, banktypes.GenesisBalancesIterator{}), diff --git a/cmd/zetacored/version.go b/cmd/zetacored/version.go new file mode 100644 index 0000000000..d9da8ac484 --- /dev/null +++ b/cmd/zetacored/version.go @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/zeta-chain/node/app" +) + +func UpgradeHandlerVersionCmd() *cobra.Command { + return &cobra.Command{ + Use: "upgrade-handler-version", + Short: "Print the default upgrade handler version", + Run: func(_ *cobra.Command, _ []string) { + fmt.Println(app.GetDefaultUpgradeHandlerVersion()) + }, + } +} diff --git a/contrib/localnet/scripts/start-upgrade-orchestrator.sh b/contrib/localnet/scripts/start-upgrade-orchestrator.sh index 1f8089bc3d..13d09a447f 100755 --- a/contrib/localnet/scripts/start-upgrade-orchestrator.sh +++ b/contrib/localnet/scripts/start-upgrade-orchestrator.sh @@ -39,7 +39,7 @@ fi # get new zetacored version curl -L -o /tmp/zetacored.new "${ZETACORED_URL}" chmod +x /tmp/zetacored.new -UPGRADE_NAME=$(/tmp/zetacored.new version) +UPGRADE_NAME=$(/tmp/zetacored.new upgrade-handler-version) # if explicit upgrade height not provided, use dumb estimator if [[ -z $UPGRADE_HEIGHT ]]; then diff --git a/docs/cli/zetacored/cli.md b/docs/cli/zetacored/cli.md index d7a594d94f..96c6e71510 100644 --- a/docs/cli/zetacored/cli.md +++ b/docs/cli/zetacored/cli.md @@ -39,6 +39,7 @@ Zetacore Daemon (server) * [zetacored tendermint](#zetacored-tendermint) - Tendermint subcommands * [zetacored testnet](#zetacored-testnet) - subcommands for starting or configuring local testnets * [zetacored tx](#zetacored-tx) - Transactions subcommands +* [zetacored upgrade-handler-version](#zetacored-upgrade-handler-version) - Print the default upgrade handler version * [zetacored validate-genesis](#zetacored-validate-genesis) - validates the genesis file at the default location or at the location passed as an arg * [zetacored version](#zetacored-version) - Print the application binary version information @@ -14346,6 +14347,34 @@ zetacored tx vesting create-vesting-account [to_address] [amount] [end_time] [fl * [zetacored tx vesting](#zetacored-tx-vesting) - Vesting transaction subcommands +## zetacored upgrade-handler-version + +Print the default upgrade handler version + +``` +zetacored upgrade-handler-version [flags] +``` + +### Options + +``` + -h, --help help for upgrade-handler-version +``` + +### Options inherited from parent commands + +``` + --home string directory for config and data + --log_format string The logging format (json|plain) + --log_level string The logging level (trace|debug|info|warn|error|fatal|panic) + --log_no_color Disable colored logs + --trace print out full stack trace on errors +``` + +### SEE ALSO + +* [zetacored](#zetacored) - Zetacore Daemon (server) + ## zetacored validate-genesis validates the genesis file at the default location or at the location passed as an arg diff --git a/version.sh b/version.sh index aedbfa5eeb..002f5cc990 100755 --- a/version.sh +++ b/version.sh @@ -22,8 +22,8 @@ short_commit=$(git rev-parse --short HEAD) # append -dirty for dirty builds if ! git diff --no-ext-diff --quiet --exit-code ; then - echo "0.0.${commit_timestamp}-${short_commit}-dirty" + echo "0.0.${commit_timestamp}+${short_commit}.dirty" exit fi -echo "0.0.${commit_timestamp}-${short_commit}" \ No newline at end of file +echo "0.0.${commit_timestamp}+${short_commit}" \ No newline at end of file From 46f3d4472c9c2a61eda408550db666f1a81c9843 Mon Sep 17 00:00:00 2001 From: Charlie Chen <34498985+ws4charlie@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:54:38 -0600 Subject: [PATCH 14/14] fix: skip depositor fee calculation on irrelevant transactions (#3162) * skip depositor fee calculation on irrelevant txs * add changelog entry * correct PR number in changelog --- changelog.md | 1 + zetaclient/chains/bitcoin/fee.go | 3 + zetaclient/chains/bitcoin/observer/inbound.go | 40 ++++++----- .../chains/bitcoin/observer/inbound_test.go | 68 ++++++++++++++----- zetaclient/chains/bitcoin/observer/witness.go | 8 ++- .../chains/bitcoin/observer/witness_test.go | 34 ++++++++-- 6 files changed, 107 insertions(+), 47 deletions(-) diff --git a/changelog.md b/changelog.md index 4facc05f04..2ac17bbc71 100644 --- a/changelog.md +++ b/changelog.md @@ -27,6 +27,7 @@ * [3106](https://github.com/zeta-chain/node/pull/3106) - prevent blocked CCTX on out of gas during omnichain calls * [3139](https://github.com/zeta-chain/node/pull/3139) - fix config resolution in orchestrator * [3149](https://github.com/zeta-chain/node/pull/3149) - abort the cctx if dust amount is detected in the revert outbound +* [3162](https://github.com/zeta-chain/node/pull/3162) - skip depositor fee calculation if transaction does not involve TSS address ## v21.0.0 diff --git a/zetaclient/chains/bitcoin/fee.go b/zetaclient/chains/bitcoin/fee.go index f56a479364..7ce483a5bf 100644 --- a/zetaclient/chains/bitcoin/fee.go +++ b/zetaclient/chains/bitcoin/fee.go @@ -56,6 +56,9 @@ var ( DefaultDepositorFee = DepositorFee(defaultDepositorFeeRate) ) +// DepositorFeeCalculator is a function type to calculate the Bitcoin depositor fee +type DepositorFeeCalculator func(interfaces.BTCRPCClient, *btcjson.TxRawResult, *chaincfg.Params) (float64, error) + // FeeRateToSatPerByte converts a fee rate in BTC/KB to sat/byte. func FeeRateToSatPerByte(rate float64) *big.Int { // #nosec G115 always in range diff --git a/zetaclient/chains/bitcoin/observer/inbound.go b/zetaclient/chains/bitcoin/observer/inbound.go index 3cd1ab3945..aa4f5667ad 100644 --- a/zetaclient/chains/bitcoin/observer/inbound.go +++ b/zetaclient/chains/bitcoin/observer/inbound.go @@ -255,12 +255,6 @@ func (ob *Observer) CheckReceiptForBtcTxHash(ctx context.Context, txHash string, return "", fmt.Errorf("block %d is not confirmed yet", blockVb.Height) } - // calculate depositor fee - depositorFee, err := bitcoin.CalcDepositorFee(ob.btcClient, tx, ob.netParams) - if err != nil { - return "", errors.Wrapf(err, "error calculating depositor fee for inbound %s", tx.Txid) - } - // #nosec G115 always positive event, err := GetBtcEvent( ob.btcClient, @@ -269,7 +263,7 @@ func (ob *Observer) CheckReceiptForBtcTxHash(ctx context.Context, txHash string, uint64(blockVb.Height), ob.logger.Inbound, ob.netParams, - depositorFee, + bitcoin.CalcDepositorFee, ) if err != nil { return "", err @@ -323,13 +317,7 @@ func FilterAndParseIncomingTx( continue // the first tx is coinbase; we do not process coinbase tx } - // calculate depositor fee - depositorFee, err := bitcoin.CalcDepositorFee(rpcClient, &txs[idx], netParams) - if err != nil { - return nil, errors.Wrapf(err, "error calculating depositor fee for inbound %s", tx.Txid) - } - - event, err := GetBtcEvent(rpcClient, tx, tssAddress, blockNumber, logger, netParams, depositorFee) + event, err := GetBtcEvent(rpcClient, tx, tssAddress, blockNumber, logger, netParams, bitcoin.CalcDepositorFee) if err != nil { // unable to parse the tx, the caller should retry return nil, errors.Wrapf(err, "error getting btc event for tx %s in block %d", tx.Txid, blockNumber) @@ -391,12 +379,12 @@ func GetBtcEvent( blockNumber uint64, logger zerolog.Logger, netParams *chaincfg.Params, - depositorFee float64, + feeCalculator bitcoin.DepositorFeeCalculator, ) (*BTCInboundEvent, error) { if netParams.Name == chaincfg.MainNetParams.Name { - return GetBtcEventWithoutWitness(rpcClient, tx, tssAddress, blockNumber, logger, netParams, depositorFee) + return GetBtcEventWithoutWitness(rpcClient, tx, tssAddress, blockNumber, logger, netParams, feeCalculator) } - return GetBtcEventWithWitness(rpcClient, tx, tssAddress, blockNumber, logger, netParams, depositorFee) + return GetBtcEventWithWitness(rpcClient, tx, tssAddress, blockNumber, logger, netParams, feeCalculator) } // GetBtcEventWithoutWitness either returns a valid BTCInboundEvent or nil @@ -409,11 +397,15 @@ func GetBtcEventWithoutWitness( blockNumber uint64, logger zerolog.Logger, netParams *chaincfg.Params, - depositorFee float64, + feeCalculator bitcoin.DepositorFeeCalculator, ) (*BTCInboundEvent, error) { - found := false - var value float64 - var memo []byte + var ( + found bool + value float64 + depositorFee float64 + memo []byte + ) + if len(tx.Vout) >= 2 { // 1st vout must have tss address as receiver with p2wpkh scriptPubKey vout0 := tx.Vout[0] @@ -430,6 +422,12 @@ func GetBtcEventWithoutWitness( return nil, nil } + // calculate depositor fee + depositorFee, err = feeCalculator(rpcClient, &tx, netParams) + if err != nil { + return nil, errors.Wrapf(err, "error calculating depositor fee for inbound %s", tx.Txid) + } + // deposit amount has to be no less than the minimum depositor fee if vout0.Value < depositorFee { logger.Info(). diff --git a/zetaclient/chains/bitcoin/observer/inbound_test.go b/zetaclient/chains/bitcoin/observer/inbound_test.go index 838315b7b8..7ec938aab0 100644 --- a/zetaclient/chains/bitcoin/observer/inbound_test.go +++ b/zetaclient/chains/bitcoin/observer/inbound_test.go @@ -23,6 +23,7 @@ import ( "github.com/zeta-chain/node/testutil/sample" "github.com/zeta-chain/node/zetaclient/chains/bitcoin" "github.com/zeta-chain/node/zetaclient/chains/bitcoin/observer" + "github.com/zeta-chain/node/zetaclient/chains/interfaces" clientcommon "github.com/zeta-chain/node/zetaclient/common" "github.com/zeta-chain/node/zetaclient/keys" "github.com/zeta-chain/node/zetaclient/testutils" @@ -30,6 +31,13 @@ import ( "github.com/zeta-chain/node/zetaclient/testutils/testrpc" ) +// mockDepositFeeCalculator returns a mock depositor fee calculator that returns the given fee and error. +func mockDepositFeeCalculator(fee float64, err error) bitcoin.DepositorFeeCalculator { + return func(interfaces.BTCRPCClient, *btcjson.TxRawResult, *chaincfg.Params) (float64, error) { + return fee, err + } +} + func TestAvgFeeRateBlock828440(t *testing.T) { // load archived block 828440 var blockVb btcjson.GetBlockVerboseTxResult @@ -278,6 +286,7 @@ func TestGetBtcEventWithoutWitness(t *testing.T) { // fee rate of above tx is 28 sat/vB depositorFee := bitcoin.DepositorFee(28 * clientcommon.BTCOutboundGasPriceMultiplier) + feeCalculator := mockDepositFeeCalculator(depositorFee, nil) // expected result memo, err := hex.DecodeString(tx.Vout[1].ScriptPubKey.Hex[4:]) @@ -309,7 +318,7 @@ func TestGetBtcEventWithoutWitness(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.NoError(t, err) require.Equal(t, eventExpected, event) @@ -333,7 +342,7 @@ func TestGetBtcEventWithoutWitness(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.NoError(t, err) require.Equal(t, eventExpected, event) @@ -357,7 +366,7 @@ func TestGetBtcEventWithoutWitness(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.NoError(t, err) require.Equal(t, eventExpected, event) @@ -381,7 +390,7 @@ func TestGetBtcEventWithoutWitness(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.NoError(t, err) require.Equal(t, eventExpected, event) @@ -405,7 +414,7 @@ func TestGetBtcEventWithoutWitness(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.NoError(t, err) require.Equal(t, eventExpected, event) @@ -425,7 +434,7 @@ func TestGetBtcEventWithoutWitness(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.NoError(t, err) require.Nil(t, event) @@ -445,7 +454,7 @@ func TestGetBtcEventWithoutWitness(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.NoError(t, err) require.Nil(t, event) @@ -459,7 +468,7 @@ func TestGetBtcEventWithoutWitness(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.NoError(t, err) require.Nil(t, event) @@ -479,12 +488,31 @@ func TestGetBtcEventWithoutWitness(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.NoError(t, err) require.Nil(t, event) }) + t.Run("should return error if RPC failed to calculate depositor fee", func(t *testing.T) { + // load tx + tx := testutils.LoadBTCInboundRawResult(t, TestDataDir, chain.ChainId, txHash, false) + + // get BTC event + rpcClient := mocks.NewBTCRPCClient(t) + event, err := observer.GetBtcEventWithoutWitness( + rpcClient, + *tx, + tssAddress, + blockNumber, + log.Logger, + net, + mockDepositFeeCalculator(0.0, errors.New("rpc error")), + ) + require.ErrorContains(t, err, "rpc error") + require.Nil(t, event) + }) + t.Run("should skip tx if amount is less than depositor fee", func(t *testing.T) { // load tx and modify amount to less than depositor fee tx := testutils.LoadBTCInboundRawResult(t, TestDataDir, chain.ChainId, txHash, false) @@ -499,7 +527,7 @@ func TestGetBtcEventWithoutWitness(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.NoError(t, err) require.Nil(t, event) @@ -519,7 +547,7 @@ func TestGetBtcEventWithoutWitness(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.NoError(t, err) require.Nil(t, event) @@ -539,7 +567,7 @@ func TestGetBtcEventWithoutWitness(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.NoError(t, err) require.Nil(t, event) @@ -570,7 +598,7 @@ func TestGetBtcEventWithoutWitness(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.NoError(t, err) require.Nil(t, event) @@ -588,6 +616,7 @@ func TestGetBtcEventErrors(t *testing.T) { // fee rate of above tx is 28 sat/vB depositorFee := bitcoin.DepositorFee(28 * clientcommon.BTCOutboundGasPriceMultiplier) + feeCalculator := mockDepositFeeCalculator(depositorFee, nil) t.Run("should return error on invalid Vout[0] script", func(t *testing.T) { // load tx and modify Vout[0] script to invalid script @@ -603,7 +632,7 @@ func TestGetBtcEventErrors(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.Error(t, err) require.Nil(t, event) @@ -623,7 +652,7 @@ func TestGetBtcEventErrors(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.ErrorContains(t, err, "no input found") require.Nil(t, event) @@ -645,7 +674,7 @@ func TestGetBtcEventErrors(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.ErrorContains(t, err, "error getting sender address") require.Nil(t, event) @@ -662,6 +691,8 @@ func TestGetBtcEvent(t *testing.T) { net := &chaincfg.MainNetParams // 2.992e-05, see avgFeeRate https://mempool.space/api/v1/blocks/835640 depositorFee := bitcoin.DepositorFee(22 * clientcommon.BTCOutboundGasPriceMultiplier) + feeCalculator := mockDepositFeeCalculator(depositorFee, nil) + txHash2 := "37777defed8717c581b4c0509329550e344bdc14ac38f71fc050096887e535c8" tx := testutils.LoadBTCInboundRawResult(t, TestDataDir, chain.ChainId, txHash2, false) rpcClient := mocks.NewBTCRPCClient(t) @@ -673,7 +704,7 @@ func TestGetBtcEvent(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.NoError(t, err) require.Equal(t, (*observer.BTCInboundEvent)(nil), event) @@ -693,6 +724,7 @@ func TestGetBtcEvent(t *testing.T) { // fee rate of above tx is 28 sat/vB depositorFee := bitcoin.DepositorFee(28 * clientcommon.BTCOutboundGasPriceMultiplier) + feeCalculator := mockDepositFeeCalculator(depositorFee, nil) // expected result memo, err := hex.DecodeString(tx.Vout[1].ScriptPubKey.Hex[4:]) @@ -723,7 +755,7 @@ func TestGetBtcEvent(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.NoError(t, err) require.Equal(t, eventExpected, event) diff --git a/zetaclient/chains/bitcoin/observer/witness.go b/zetaclient/chains/bitcoin/observer/witness.go index 9625ad3caa..bb85bdc47b 100644 --- a/zetaclient/chains/bitcoin/observer/witness.go +++ b/zetaclient/chains/bitcoin/observer/witness.go @@ -24,7 +24,7 @@ func GetBtcEventWithWitness( blockNumber uint64, logger zerolog.Logger, netParams *chaincfg.Params, - depositorFee float64, + feeCalculator bitcoin.DepositorFeeCalculator, ) (*BTCInboundEvent, error) { if len(tx.Vout) < 1 { logger.Debug().Msgf("no output %s", tx.Txid) @@ -40,6 +40,12 @@ func GetBtcEventWithWitness( return nil, nil } + // calculate depositor fee + depositorFee, err := feeCalculator(client, &tx, netParams) + if err != nil { + return nil, errors.Wrapf(err, "error calculating depositor fee for inbound %s", tx.Txid) + } + isAmountValid, amount := isValidAmount(tx.Vout[0].Value, depositorFee) if !isAmountValid { logger.Info(). diff --git a/zetaclient/chains/bitcoin/observer/witness_test.go b/zetaclient/chains/bitcoin/observer/witness_test.go index af14427c6e..745f2003a9 100644 --- a/zetaclient/chains/bitcoin/observer/witness_test.go +++ b/zetaclient/chains/bitcoin/observer/witness_test.go @@ -61,6 +61,7 @@ func TestGetBtcEventWithWitness(t *testing.T) { // fee rate of above tx is 28 sat/vB depositorFee := bitcoin.DepositorFee(28 * clientcommon.BTCOutboundGasPriceMultiplier) + feeCalculator := mockDepositFeeCalculator(depositorFee, nil) t.Run("decode OP_RETURN ok", func(t *testing.T) { tx := testutils.LoadBTCInboundRawResult(t, TestDataDir, chain.ChainId, txHash, false) @@ -93,7 +94,7 @@ func TestGetBtcEventWithWitness(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.NoError(t, err) require.Equal(t, eventExpected, event) @@ -131,7 +132,7 @@ func TestGetBtcEventWithWitness(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.NoError(t, err) require.Equal(t, eventExpected, event) @@ -172,7 +173,7 @@ func TestGetBtcEventWithWitness(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.NoError(t, err) require.Equal(t, event, eventExpected) @@ -192,12 +193,31 @@ func TestGetBtcEventWithWitness(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.NoError(t, err) require.Nil(t, event) }) + t.Run("should return error if RPC failed to calculate depositor fee", func(t *testing.T) { + // load tx + tx := testutils.LoadBTCInboundRawResult(t, TestDataDir, chain.ChainId, txHash, false) + + // get BTC event + rpcClient := mocks.NewBTCRPCClient(t) + event, err := observer.GetBtcEventWithWitness( + rpcClient, + *tx, + tssAddress, + blockNumber, + log.Logger, + net, + mockDepositFeeCalculator(0.0, errors.New("rpc error")), + ) + require.ErrorContains(t, err, "rpc error") + require.Nil(t, event) + }) + t.Run("should skip tx if amount is less than depositor fee", func(t *testing.T) { // load tx and modify amount to less than depositor fee tx := testutils.LoadBTCInboundRawResult(t, TestDataDir, chain.ChainId, txHash, false) @@ -212,7 +232,7 @@ func TestGetBtcEventWithWitness(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.NoError(t, err) require.Nil(t, event) @@ -234,7 +254,7 @@ func TestGetBtcEventWithWitness(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.ErrorContains(t, err, "rpc error") require.Nil(t, event) @@ -268,7 +288,7 @@ func TestGetBtcEventWithWitness(t *testing.T) { blockNumber, log.Logger, net, - depositorFee, + feeCalculator, ) require.NoError(t, err) require.Nil(t, event)