From 1629523781a8bde0913b7176f127339667c109dd Mon Sep 17 00:00:00 2001 From: Dmitry S <11892559+swift1337@users.noreply.github.com> Date: Thu, 7 Nov 2024 12:07:20 +0100 Subject: [PATCH 01/10] refactor(zetaclient)!: cli improvements (#3122) * Consolidate files and logic within zetaclientd cli * Update `init` command * Consolidate TSS commands * Drop HSM cli (https://github.com/zeta-chain/node/pull/3118) * Move p2p diagnostic to tss package * Refactor relayer commands * Rename init config * Restructure inbound cmd * Update changelog * Drop p2p diagnostics * lint & pr comments [1] * Address pr comments [2] --- changelog.md | 3 + cmd/config.go | 12 -- cmd/cosmos.go | 36 +++++ cmd/zetaclientd/encrypt_tss.go | 47 ------ cmd/zetaclientd/gen_pre_params.go | 40 ----- cmd/zetaclientd/import_relayer_keys.go | 153 ------------------ cmd/zetaclientd/{debug.go => inbound.go} | 39 ++--- cmd/zetaclientd/init.go | 110 ------------- cmd/zetaclientd/initconfig.go | 103 ++++++++++++ cmd/zetaclientd/main.go | 131 +++++++++++---- cmd/zetaclientd/relayer.go | 121 ++++++++++++++ cmd/zetaclientd/root.go | 22 --- cmd/zetaclientd/start.go | 48 +++--- cmd/zetaclientd/start_utils.go | 53 ------ cmd/zetaclientd/tss.go | 69 ++++++++ cmd/zetaclientd/utils.go | 67 +++++++- cmd/zetaclientd/version.go | 20 --- contrib/localnet/scripts/start-zetaclientd.sh | 2 +- zetaclient/config/types.go | 4 +- .../tss/generate.go | 12 +- zetaclient/tss/tss_signer.go | 6 +- 21 files changed, 540 insertions(+), 558 deletions(-) delete mode 100644 cmd/config.go create mode 100644 cmd/cosmos.go delete mode 100644 cmd/zetaclientd/encrypt_tss.go delete mode 100644 cmd/zetaclientd/gen_pre_params.go delete mode 100644 cmd/zetaclientd/import_relayer_keys.go rename cmd/zetaclientd/{debug.go => inbound.go} (84%) delete mode 100644 cmd/zetaclientd/init.go create mode 100644 cmd/zetaclientd/initconfig.go create mode 100644 cmd/zetaclientd/relayer.go delete mode 100644 cmd/zetaclientd/root.go delete mode 100644 cmd/zetaclientd/start_utils.go create mode 100644 cmd/zetaclientd/tss.go delete mode 100644 cmd/zetaclientd/version.go rename cmd/zetaclientd/keygen_tss.go => zetaclient/tss/generate.go (97%) diff --git a/changelog.md b/changelog.md index 805c6d4a26..8e53bb366b 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,9 @@ * [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. +### Refactor +* [3122](https://github.com/zeta-chain/node/pull/3122) - improve & refactor zetaclientd cli + ### Tests * [3075](https://github.com/zeta-chain/node/pull/3075) - ton: withdraw concurrent, deposit & revert. diff --git a/cmd/config.go b/cmd/config.go deleted file mode 100644 index 978d90c433..0000000000 --- a/cmd/config.go +++ /dev/null @@ -1,12 +0,0 @@ -package cmd - -const ( - Bech32PrefixAccAddr = "zeta" - Bech32PrefixAccPub = "zetapub" - Bech32PrefixValAddr = "zetav" - Bech32PrefixValPub = "zetavpub" - Bech32PrefixConsAddr = "zetac" - Bech32PrefixConsPub = "zetacpub" - DenomRegex = `[a-zA-Z][a-zA-Z0-9:\\/\\\-\\_\\.]{2,127}` - ZetaChainHDPath string = `m/44'/60'/0'/0/0` -) diff --git a/cmd/cosmos.go b/cmd/cosmos.go new file mode 100644 index 0000000000..9c0b3b8e3d --- /dev/null +++ b/cmd/cosmos.go @@ -0,0 +1,36 @@ +// Package cmd provides cosmos constants for ZetaClient. +package cmd + +import ( + "sync" + + cosmos "github.com/cosmos/cosmos-sdk/types" +) + +const ( + Bech32PrefixAccAddr = "zeta" + Bech32PrefixAccPub = "zetapub" + Bech32PrefixValAddr = "zetav" + Bech32PrefixValPub = "zetavpub" + Bech32PrefixConsAddr = "zetac" + Bech32PrefixConsPub = "zetacpub" + DenomRegex = `[a-zA-Z][a-zA-Z0-9:\\/\\\-\\_\\.]{2,127}` + ZetaChainHDPath string = `m/44'/60'/0'/0/0` +) + +var setupConfig sync.Once + +// SetupCosmosConfig configures basic Cosmos parameters. +// This function is required because some parts of ZetaClient rely on these constants. +func SetupCosmosConfig() { + setupConfig.Do(setupCosmosConfig) +} + +func setupCosmosConfig() { + config := cosmos.GetConfig() + config.SetBech32PrefixForAccount(Bech32PrefixAccAddr, Bech32PrefixAccPub) + config.SetBech32PrefixForValidator(Bech32PrefixValAddr, Bech32PrefixValPub) + config.SetBech32PrefixForConsensusNode(Bech32PrefixConsAddr, Bech32PrefixConsPub) + config.SetFullFundraiserPath(ZetaChainHDPath) + cosmos.SetCoinDenomRegex(func() string { return DenomRegex }) +} diff --git a/cmd/zetaclientd/encrypt_tss.go b/cmd/zetaclientd/encrypt_tss.go deleted file mode 100644 index 92be57a190..0000000000 --- a/cmd/zetaclientd/encrypt_tss.go +++ /dev/null @@ -1,47 +0,0 @@ -package main - -import ( - "encoding/json" - "os" - "path/filepath" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - - "github.com/zeta-chain/node/pkg/crypto" -) - -var encTssCmd = &cobra.Command{ - Use: "tss-encrypt [file-path] [secret-key]", - Short: "Utility command to encrypt existing tss key-share file", - Args: cobra.ExactArgs(2), - RunE: EncryptTSSFile, -} - -func init() { - RootCmd.AddCommand(encTssCmd) -} - -// EncryptTSSFile encrypts the given file with the given secret key -func EncryptTSSFile(_ *cobra.Command, args []string) error { - filePath := args[0] - password := args[1] - - filePath = filepath.Clean(filePath) - data, err := os.ReadFile(filePath) - if err != nil { - return err - } - - if !json.Valid(data) { - return errors.New("file does not contain valid json, may already be encrypted") - } - - // encrypt the data - cipherText, err := crypto.EncryptAES256GCM(data, password) - if err != nil { - return errors.Wrap(err, "failed to encrypt data") - } - - return os.WriteFile(filePath, cipherText, 0o600) -} diff --git a/cmd/zetaclientd/gen_pre_params.go b/cmd/zetaclientd/gen_pre_params.go deleted file mode 100644 index c61f31ec2b..0000000000 --- a/cmd/zetaclientd/gen_pre_params.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "os" - "time" - - "github.com/bnb-chain/tss-lib/ecdsa/keygen" - "github.com/spf13/cobra" -) - -func init() { - RootCmd.AddCommand(GenPrePramsCmd) -} - -var GenPrePramsCmd = &cobra.Command{ - Use: "gen-pre-params ", - Short: "Generate pre parameters for TSS", - Args: cobra.ExactArgs(1), - RunE: func(_ *cobra.Command, args []string) error { - startTime := time.Now() - preParams, err := keygen.GeneratePreParams(time.Second * 300) - if err != nil { - return err - } - - file, err := os.OpenFile(args[0], os.O_RDWR|os.O_CREATE, 0600) - if err != nil { - return err - } - defer file.Close() - err = json.NewEncoder(file).Encode(preParams) - if err != nil { - return err - } - fmt.Printf("Generated new pre-parameters in %v\n", time.Since(startTime)) - return nil - }, -} diff --git a/cmd/zetaclientd/import_relayer_keys.go b/cmd/zetaclientd/import_relayer_keys.go deleted file mode 100644 index ecd71a6f5c..0000000000 --- a/cmd/zetaclientd/import_relayer_keys.go +++ /dev/null @@ -1,153 +0,0 @@ -package main - -import ( - "fmt" - "os" - "path/filepath" - - "github.com/pkg/errors" - "github.com/rs/zerolog/log" - "github.com/spf13/cobra" - - "github.com/zeta-chain/node/pkg/chains" - "github.com/zeta-chain/node/pkg/crypto" - zetaos "github.com/zeta-chain/node/pkg/os" - "github.com/zeta-chain/node/zetaclient/config" - "github.com/zeta-chain/node/zetaclient/keys" -) - -var CmdImportRelayerKey = &cobra.Command{ - Use: "import-relayer-key --network= --private-key= --password= --relayer-key-path=", - Short: "Import a relayer private key", - Example: `zetaclientd import-relayer-key --network=7 --private-key= --password=`, - RunE: ImportRelayerKey, -} - -var CmdRelayerAddress = &cobra.Command{ - Use: "relayer-address --network= --password= --relayer-key-path=", - Short: "Show the relayer address", - Example: `zetaclientd relayer-address --network=7 --password=my_password`, - RunE: ShowRelayerAddress, -} - -var importArgs = importRelayerKeyArguments{} -var addressArgs = relayerAddressArguments{} - -// importRelayerKeyArguments is the struct that holds the arguments for the import command -type importRelayerKeyArguments struct { - network int32 - privateKey string - password string - relayerKeyPath string -} - -// relayerAddressArguments is the struct that holds the arguments for the show command -type relayerAddressArguments struct { - network int32 - password string - relayerKeyPath string -} - -func init() { - RootCmd.AddCommand(CmdImportRelayerKey) - RootCmd.AddCommand(CmdRelayerAddress) - - // resolve default relayer key path - defaultRelayerKeyPath, err := zetaos.ExpandHomeDir(config.DefaultRelayerKeyPath) - if err != nil { - log.Fatal().Err(err).Msg("failed to resolve default relayer key path") - } - - CmdImportRelayerKey.Flags().Int32Var(&importArgs.network, "network", 7, "network id, (7: solana)") - CmdImportRelayerKey.Flags(). - StringVar(&importArgs.privateKey, "private-key", "", "the relayer private key to import") - CmdImportRelayerKey.Flags(). - StringVar(&importArgs.password, "password", "", "the password to encrypt the relayer private key") - CmdImportRelayerKey.Flags(). - StringVar(&importArgs.relayerKeyPath, "relayer-key-path", defaultRelayerKeyPath, "path to relayer keys") - - CmdRelayerAddress.Flags().Int32Var(&addressArgs.network, "network", 7, "network id, (7:solana)") - CmdRelayerAddress.Flags(). - StringVar(&addressArgs.password, "password", "", "the password to decrypt the relayer private key") - CmdRelayerAddress.Flags(). - StringVar(&addressArgs.relayerKeyPath, "relayer-key-path", defaultRelayerKeyPath, "path to relayer keys") -} - -// ImportRelayerKey imports a relayer private key -func ImportRelayerKey(_ *cobra.Command, _ []string) error { - // validate private key and password - if importArgs.privateKey == "" { - return errors.New("must provide a private key") - } - if importArgs.password == "" { - return errors.New("must provide a password") - } - if !keys.IsRelayerPrivateKeyValid(importArgs.privateKey, chains.Network(importArgs.network)) { - return errors.New("invalid private key") - } - - // resolve the relayer key file path - fileName, err := keys.ResolveRelayerKeyFile(importArgs.relayerKeyPath, chains.Network(importArgs.network)) - if err != nil { - return errors.Wrap(err, "failed to resolve relayer key file path") - } - - // create path (owner `rwx` permissions) if it does not exist - keyPath := filepath.Dir(fileName) - if _, err := os.Stat(keyPath); os.IsNotExist(err) { - if err := os.MkdirAll(keyPath, 0o700); err != nil { - return errors.Wrapf(err, "failed to create relayer key path: %s", keyPath) - } - } - - // avoid overwriting existing key file - if zetaos.FileExists(fileName) { - return errors.Errorf( - "relayer key %s already exists, please backup and remove it before importing a new key", - fileName, - ) - } - - // encrypt the private key - ciphertext, err := crypto.EncryptAES256GCMBase64(importArgs.privateKey, importArgs.password) - if err != nil { - return errors.Wrap(err, "private key encryption failed") - } - - // create the relayer key file - err = keys.WriteRelayerKeyToFile(fileName, keys.RelayerKey{PrivateKey: ciphertext}) - if err != nil { - return errors.Wrapf(err, "failed to create relayer key file: %s", fileName) - } - fmt.Printf("successfully imported relayer key: %s\n", fileName) - - return nil -} - -// ShowRelayerAddress shows the relayer address -func ShowRelayerAddress(_ *cobra.Command, _ []string) error { - // try loading the relayer key if present - network := chains.Network(addressArgs.network) - relayerKey, err := keys.LoadRelayerKey(addressArgs.relayerKeyPath, network, addressArgs.password) - if err != nil { - return errors.Wrap(err, "failed to load relayer key") - } - - // relayer key does not exist, return error - if relayerKey == nil { - return fmt.Errorf( - "relayer key not found for network %d in path: %s", - addressArgs.network, - addressArgs.relayerKeyPath, - ) - } - - // resolve the relayer address - networkName, address, err := relayerKey.ResolveAddress(network) - if err != nil { - return errors.Wrap(err, "failed to resolve relayer address") - } - fmt.Printf("relayer address (%s): %s\n", networkName, address) - - return nil -} diff --git a/cmd/zetaclientd/debug.go b/cmd/zetaclientd/inbound.go similarity index 84% rename from cmd/zetaclientd/debug.go rename to cmd/zetaclientd/inbound.go index c0abb69db6..bac3c96725 100644 --- a/cmd/zetaclientd/debug.go +++ b/cmd/zetaclientd/inbound.go @@ -3,7 +3,6 @@ package main import ( "context" "fmt" - "os" "strconv" "strings" @@ -26,36 +25,24 @@ import ( "github.com/zeta-chain/node/zetaclient/zetacore" ) -var debugArgs = debugArguments{} - -type debugArguments struct { - zetaCoreHome string - zetaNode string - zetaChainID string +type inboundOptions struct { + Node string + ChainID string } -func init() { - defaultHomeDir := os.ExpandEnv("$HOME/.zetacored") +var inboundOpts inboundOptions - cmd := DebugCmd() - cmd.Flags().StringVar(&debugArgs.zetaCoreHome, "core-home", defaultHomeDir, "zetacore home directory") - cmd.Flags().StringVar(&debugArgs.zetaNode, "node", "46.4.15.110", "public ip address") - cmd.Flags().StringVar(&debugArgs.zetaChainID, "chain-id", "athens_7001-1", "pre-params file path") +func setupInboundOptions() { + f, cfg := InboundCmd.PersistentFlags(), &inboundOpts - RootCmd.AddCommand(cmd) + f.StringVar(&cfg.Node, "node", "46.4.15.110", "zeta public ip address") + f.StringVar(&cfg.ChainID, "chain-id", "athens_7001-1", "zeta chain id") } -func DebugCmd() *cobra.Command { - return &cobra.Command{ - Use: "get-inbound-ballot [inboundHash] [chainID]", - Short: "provide txHash and chainID to get the ballot status for the txHash", - RunE: debugCmd, - } -} - -func debugCmd(_ *cobra.Command, args []string) error { +func InboundGetBallot(_ *cobra.Command, args []string) error { cobra.ExactArgs(2) - cfg, err := config.Load(debugArgs.zetaCoreHome) + + cfg, err := config.Load(globalOpts.ZetacoreHome) if err != nil { return errors.Wrap(err, "failed to load config") } @@ -70,9 +57,9 @@ func debugCmd(_ *cobra.Command, args []string) error { // create a new zetacore client client, err := zetacore.NewClient( &keys.Keys{OperatorAddress: sdk.MustAccAddressFromBech32(sample.AccAddress())}, - debugArgs.zetaNode, + inboundOpts.Node, "", - debugArgs.zetaChainID, + inboundOpts.ChainID, zerolog.Nop(), ) if err != nil { diff --git a/cmd/zetaclientd/init.go b/cmd/zetaclientd/init.go deleted file mode 100644 index c8fef11552..0000000000 --- a/cmd/zetaclientd/init.go +++ /dev/null @@ -1,110 +0,0 @@ -package main - -import ( - "github.com/rs/zerolog" - "github.com/spf13/cobra" - - "github.com/zeta-chain/node/testutil/sample" - "github.com/zeta-chain/node/zetaclient/config" -) - -var InitCmd = &cobra.Command{ - Use: "init", - Short: "Initialize Configuration", - RunE: Initialize, -} - -var initArgs = initArguments{} - -type initArguments struct { - peer string - publicIP string - logFormat string - logSampler bool - preParamsPath string - chainID string - zetacoreURL string - authzGranter string - authzHotkey string - level int8 - configUpdateTicker uint64 - - p2pDiagnostic bool - p2pDiagnosticTicker uint64 - TssPath string - TestTssKeysign bool - KeyringBackend string - RelayerKeyPath string -} - -func init() { - RootCmd.AddCommand(InitCmd) - RootCmd.AddCommand(VersionCmd) - - InitCmd.Flags(). - StringVar(&initArgs.peer, "peer", "", "peer address, e.g. /dns/tss1/tcp/6668/ipfs/16Uiu2HAmACG5DtqmQsHtXg4G2sLS65ttv84e7MrL4kapkjfmhxAp") - InitCmd.Flags().StringVar(&initArgs.publicIP, "public-ip", "", "public ip address") - InitCmd.Flags().StringVar(&initArgs.preParamsPath, "pre-params", "~/preParams.json", "pre-params file path") - InitCmd.Flags().StringVar(&initArgs.chainID, "chain-id", "athens_7001-1", "chain id") - InitCmd.Flags().StringVar(&initArgs.zetacoreURL, "zetacore-url", "127.0.0.1", "zetacore node URL") - InitCmd.Flags(). - StringVar(&initArgs.authzGranter, "operator", "", "granter for the authorization , this should be operator address") - InitCmd.Flags(). - StringVar(&initArgs.authzHotkey, "hotkey", "hotkey", "hotkey for zetaclient this key is used for TSS and ZetaClient operations") - InitCmd.Flags(). - Int8Var(&initArgs.level, "log-level", int8(zerolog.InfoLevel), "log level (0:debug, 1:info, 2:warn, 3:error, 4:fatal, 5:panic , 6: NoLevel , 7: Disable)") - InitCmd.Flags().StringVar(&initArgs.logFormat, "log-format", "json", "log format (json, test)") - InitCmd.Flags().BoolVar(&initArgs.logSampler, "log-sampler", false, "set to to true to turn on log sampling") - InitCmd.Flags().BoolVar(&initArgs.p2pDiagnostic, "p2p-diagnostic", false, "enable p2p diagnostic") - InitCmd.Flags(). - Uint64Var(&initArgs.p2pDiagnosticTicker, "p2p-diagnostic-ticker", 30, "p2p diagnostic ticker (default: 0 means no ticker)") - InitCmd.Flags(). - Uint64Var(&initArgs.configUpdateTicker, "config-update-ticker", 5, "config update ticker (default: 0 means no ticker)") - InitCmd.Flags().StringVar(&initArgs.TssPath, "tss-path", "~/.tss", "path to tss location") - InitCmd.Flags(). - BoolVar(&initArgs.TestTssKeysign, "test-tss", false, "set to to true to run a check for TSS keysign on startup") - InitCmd.Flags(). - StringVar(&initArgs.KeyringBackend, "keyring-backend", string(config.KeyringBackendTest), "keyring backend to use (test, file)") - InitCmd.Flags(). - StringVar(&initArgs.RelayerKeyPath, "relayer-key-path", "~/.zetacored/relayer-keys", "path to relayer keys") -} - -func Initialize(_ *cobra.Command, _ []string) error { - err := setHomeDir() - if err != nil { - return err - } - - //Create new config struct - configData := config.New(true) - - //Validate Peer eg. /ip4/172.0.2.1/tcp/6668/p2p/16Uiu2HAmACG5DtqmQsHtXg4G2sLS65ttv84e7MrL4kapkjfmhxAp - if len(initArgs.peer) != 0 { - err := validatePeer(initArgs.peer) - if err != nil { - return err - } - } - - //Populate new struct with cli arguments - configData.Peer = initArgs.peer - configData.PublicIP = initArgs.publicIP - configData.PreParamsPath = initArgs.preParamsPath - configData.ChainID = initArgs.chainID - configData.ZetaCoreURL = initArgs.zetacoreURL - configData.AuthzHotkey = initArgs.authzHotkey - configData.AuthzGranter = initArgs.authzGranter - configData.LogLevel = initArgs.level - configData.LogFormat = initArgs.logFormat - configData.LogSampler = initArgs.logSampler - configData.P2PDiagnostic = initArgs.p2pDiagnostic - configData.TssPath = initArgs.TssPath - configData.P2PDiagnosticTicker = initArgs.p2pDiagnosticTicker - configData.ConfigUpdateTicker = initArgs.configUpdateTicker - configData.KeyringBackend = config.KeyringBackend(initArgs.KeyringBackend) - configData.RelayerKeyPath = initArgs.RelayerKeyPath - configData.ComplianceConfig = sample.ComplianceConfig() - - // Save config file - return config.Save(&configData, rootArgs.zetaCoreHome) -} diff --git a/cmd/zetaclientd/initconfig.go b/cmd/zetaclientd/initconfig.go new file mode 100644 index 0000000000..6619ce279a --- /dev/null +++ b/cmd/zetaclientd/initconfig.go @@ -0,0 +1,103 @@ +package main + +import ( + "cosmossdk.io/errors" + "github.com/rs/zerolog" + "github.com/spf13/cobra" + + "github.com/zeta-chain/node/testutil/sample" + "github.com/zeta-chain/node/zetaclient/config" +) + +// initializeConfigOptions is a set of CLI options for `init` command. +type initializeConfigOptions struct { + peer string + publicIP string + logFormat string + logSampler bool + preParamsPath string + chainID string + zetacoreURL string + authzGranter string + authzHotkey string + level int8 + configUpdateTicker uint64 + + p2pDiagnostic bool + p2pDiagnosticTicker uint64 + TSSPath string + TestTSSKeySign bool + KeyringBackend string + RelayerKeyPath string +} + +var initializeConfigOpts initializeConfigOptions + +func setupInitializeConfigOptions() { + f, cfg := InitializeConfigCmd.Flags(), &initializeConfigOpts + + const ( + usagePeer = "peer address e.g. /dns/tss1/tcp/6668/ipfs/16Uiu2HAmACG5DtqmQsH..." + usageHotKey = "hotkey for zetaclient this key is used for TSS and ZetaClient operations" + usageLogLevel = "log level (0:debug, 1:info, 2:warn, 3:error, 4:fatal, 5:panic)" + usageP2PDiag = "p2p diagnostic ticker (default: 0 means no ticker)" + usageTicker = "config update ticker (default: 0 means no ticker)" + usageKeyring = "keyring backend to use (test, file)" + ) + + f.StringVar(&cfg.peer, "peer", "", usagePeer) + f.StringVar(&cfg.publicIP, "public-ip", "", "public ip address") + f.StringVar(&cfg.preParamsPath, "pre-params", "~/preParams.json", "pre-params file path") + f.StringVar(&cfg.chainID, "chain-id", "athens_7001-1", "chain id") + f.StringVar(&cfg.zetacoreURL, "zetacore-url", "127.0.0.1", "zetacore node URL") + f.StringVar(&cfg.authzGranter, "operator", "", "granter for the authorization , this should be operator address") + f.StringVar(&cfg.authzHotkey, "hotkey", "hotkey", usageHotKey) + f.Int8Var(&cfg.level, "log-level", int8(zerolog.InfoLevel), usageLogLevel) + f.StringVar(&cfg.logFormat, "log-format", "json", "log format (json, test)") + f.BoolVar(&cfg.logSampler, "log-sampler", false, "set to to true to turn on log sampling") + f.BoolVar(&cfg.p2pDiagnostic, "p2p-diagnostic", false, "enable p2p diagnostic") + f.Uint64Var(&cfg.p2pDiagnosticTicker, "p2p-diagnostic-ticker", 30, usageP2PDiag) + f.Uint64Var(&cfg.configUpdateTicker, "config-update-ticker", 5, usageTicker) + f.StringVar(&cfg.TSSPath, "tss-path", "~/.tss", "path to tss location") + f.BoolVar(&cfg.TestTSSKeySign, "test-tss", false, "set to to true to run a check for TSS keysign on startup") + f.StringVar(&cfg.KeyringBackend, "keyring-backend", string(config.KeyringBackendTest), usageKeyring) + f.StringVar(&cfg.RelayerKeyPath, "relayer-key-path", "~/.zetacored/relayer-keys", "path to relayer keys") +} + +// InitializeConfig creates new config for zetaclientd and saves it to the config file. +func InitializeConfig(_ *cobra.Command, _ []string) error { + // Create new config struct + configData := config.New(true) + opts := &initializeConfigOpts + + // Validate Peer + // e.g. /ip4/172.0.2.1/tcp/6668/p2p/16Uiu2HAmACG5DtqmQsHtXg4G2sLS65ttv84e7MrL4kapkjfmhxAp + if opts.peer != "" { + if err := validatePeer(opts.peer); err != nil { + return errors.Wrap(err, "invalid peer address") + } + } + + // Populate new struct with cli arguments + configData.Peer = initializeConfigOpts.peer + configData.PublicIP = opts.publicIP + configData.PreParamsPath = opts.preParamsPath + configData.ChainID = opts.chainID + configData.ZetaCoreURL = opts.zetacoreURL + configData.AuthzHotkey = opts.authzHotkey + configData.AuthzGranter = opts.authzGranter + configData.LogLevel = opts.level + configData.LogFormat = opts.logFormat + configData.LogSampler = opts.logSampler + configData.P2PDiagnostic = opts.p2pDiagnostic + configData.TssPath = opts.TSSPath + configData.TestTssKeysign = opts.TestTSSKeySign + configData.P2PDiagnosticTicker = opts.p2pDiagnosticTicker + configData.ConfigUpdateTicker = opts.configUpdateTicker + configData.KeyringBackend = config.KeyringBackend(initializeConfigOpts.KeyringBackend) + configData.RelayerKeyPath = opts.RelayerKeyPath + configData.ComplianceConfig = sample.ComplianceConfig() + + // Save config file + return config.Save(&configData, globalOpts.ZetacoreHome) +} diff --git a/cmd/zetaclientd/main.go b/cmd/zetaclientd/main.go index 2ad829d352..b3113f4035 100644 --- a/cmd/zetaclientd/main.go +++ b/cmd/zetaclientd/main.go @@ -1,45 +1,120 @@ package main import ( - "math/rand" + "context" + "fmt" "os" - "time" - ecdsakeygen "github.com/bnb-chain/tss-lib/ecdsa/keygen" - "github.com/cosmos/cosmos-sdk/server" - svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" - "github.com/cosmos/cosmos-sdk/types" + tmcli "github.com/cometbft/cometbft/libs/cli" + "github.com/spf13/cobra" "github.com/zeta-chain/node/app" "github.com/zeta-chain/node/cmd" + "github.com/zeta-chain/node/pkg/constant" ) var ( - preParams *ecdsakeygen.LocalPreParams -) + RootCmd = &cobra.Command{ + Use: "zetaclientd", + Short: "zetaclient cli & server", + } + VersionCmd = &cobra.Command{ + Use: "version", + Short: "prints version", + Run: func(_ *cobra.Command, _ []string) { fmt.Print(constant.Version) }, + } -func main() { - if err := svrcmd.Execute(RootCmd, "", app.DefaultNodeHome); err != nil { - switch e := err.(type) { - case server.ErrorCode: - os.Exit(e.Code) + InitializeConfigCmd = &cobra.Command{ + Use: "init-config", + Aliases: []string{"init"}, + Short: "Initialize Zetaclient Configuration file", + RunE: InitializeConfig, + } + StartCmd = &cobra.Command{ + Use: "start", + Short: "Start ZetaClient Observer", + RunE: Start, + } + + TSSCmd = &cobra.Command{Use: "tss", Short: "TSS commands"} + TSSEncryptCmd = &cobra.Command{ + Use: "encrypt [file-path] [secret-key]", + Short: "Utility command to encrypt existing tss key-share file", + Args: cobra.ExactArgs(2), + RunE: TSSEncryptFile, + } + TSSGeneratePreParamsCmd = &cobra.Command{ + Use: "gen-pre-params [path]", + Short: "Generate pre parameters for TSS", + Args: cobra.ExactArgs(1), + RunE: TSSGeneratePreParams, + } - default: - os.Exit(1) - } + RelayerCmd = &cobra.Command{Use: "relayer", Short: "Relayer commands"} + RelayerImportKeyCmd = &cobra.Command{ + Use: "import-key --network= --private-key= --password= --relayer-key-path=", + Short: "Import a relayer private key", + RunE: RelayerImportKey, } + RelayerShowAddressCmd = &cobra.Command{ + Use: "show-address --network= --password= --relayer-key-path=", + Short: "Show relayer address", + RunE: RelayerShowAddress, + } + + InboundCmd = &cobra.Command{Use: "inbound", Short: "Inbound transactions"} + InboundGetBallotCmd = &cobra.Command{ + Use: "get-ballot [inboundHash] [chainID]", + Short: "Get the ballot status for the tx hash", + RunE: InboundGetBallot, + } +) + +// globalOptions defines the global options for all commands. +type globalOptions struct { + ZetacoreHome string } -func SetupConfigForTest() { - config := types.GetConfig() - config.SetBech32PrefixForAccount(cmd.Bech32PrefixAccAddr, cmd.Bech32PrefixAccPub) - config.SetBech32PrefixForValidator(cmd.Bech32PrefixValAddr, cmd.Bech32PrefixValPub) - config.SetBech32PrefixForConsensusNode(cmd.Bech32PrefixConsAddr, cmd.Bech32PrefixConsPub) - //config.SetCoinType(cmd.MetaChainCoinType) - config.SetFullFundraiserPath(cmd.ZetaChainHDPath) - types.SetCoinDenomRegex(func() string { - return cmd.DenomRegex - }) - - rand.Seed(time.Now().UnixNano()) +var globalOpts globalOptions + +func setupGlobalOptions() { + globals := RootCmd.PersistentFlags() + + globals.StringVar(&globalOpts.ZetacoreHome, tmcli.HomeFlag, app.DefaultNodeHome, "home path") + // add more options here (e.g. verbosity, etc...) +} + +func init() { + cmd.SetupCosmosConfig() + + // Setup options + setupGlobalOptions() + setupInitializeConfigOptions() + setupRelayerOptions() + setupInboundOptions() + + // Define commands + RootCmd.AddCommand(VersionCmd) + RootCmd.AddCommand(StartCmd) + RootCmd.AddCommand(InitializeConfigCmd) + + RootCmd.AddCommand(TSSCmd) + TSSCmd.AddCommand(TSSEncryptCmd) + TSSCmd.AddCommand(TSSGeneratePreParamsCmd) + + RootCmd.AddCommand(RelayerCmd) + RelayerCmd.AddCommand(RelayerImportKeyCmd) + RelayerCmd.AddCommand(RelayerShowAddressCmd) + + RootCmd.AddCommand(InboundCmd) + InboundCmd.AddCommand(InboundGetBallotCmd) +} + +func main() { + ctx := context.Background() + + if err := RootCmd.ExecuteContext(ctx); err != nil { + fmt.Printf("Error: %s. Exit code 1\n", err) + os.Exit(1) + } } diff --git a/cmd/zetaclientd/relayer.go b/cmd/zetaclientd/relayer.go new file mode 100644 index 0000000000..bd2596bceb --- /dev/null +++ b/cmd/zetaclientd/relayer.go @@ -0,0 +1,121 @@ +package main + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/zeta-chain/node/app" + "github.com/zeta-chain/node/pkg/chains" + "github.com/zeta-chain/node/pkg/crypto" + zetaos "github.com/zeta-chain/node/pkg/os" + "github.com/zeta-chain/node/zetaclient/config" + "github.com/zeta-chain/node/zetaclient/keys" +) + +// relayerOptions is the struct that holds arguments for the relayer commands +type relayerOptions struct { + privateKey string + network int32 + password string + relayerKeyPath string +} + +var relayerOpts relayerOptions + +func setupRelayerOptions() { + f, cfg := RelayerCmd.PersistentFlags(), &relayerOpts + + // resolve default relayer key path + defaultKeyPath := fmt.Sprintf("%s/%s", app.DefaultNodeHome, config.DefaultRelayerDir) + + f.Int32Var(&cfg.network, "network", 7, "network id, (7:solana)") + f.StringVar(&cfg.password, "password", "", "the password to decrypt the relayer private key") + f.StringVar(&cfg.relayerKeyPath, "key-path", defaultKeyPath, "path to relayer keys") + + // import command in addition has the private key option + f = RelayerImportKeyCmd.Flags() + f.StringVar(&cfg.privateKey, "private-key", "", "the relayer private key to import") +} + +// RelayerShowAddress shows the relayer address +func RelayerShowAddress(_ *cobra.Command, _ []string) error { + // try loading the relayer key if present + network := chains.Network(relayerOpts.network) + relayerKey, err := keys.LoadRelayerKey(relayerOpts.relayerKeyPath, network, relayerOpts.password) + if err != nil { + return errors.Wrap(err, "failed to load relayer key") + } + + // relayer key does not exist, return error + if relayerKey == nil { + return fmt.Errorf( + "relayer key not found for network %d in path: %s", + relayerOpts.network, + relayerOpts.relayerKeyPath, + ) + } + + // resolve the relayer address + networkName, address, err := relayerKey.ResolveAddress(network) + if err != nil { + return errors.Wrap(err, "failed to resolve relayer address") + } + + fmt.Printf("relayer address (%s): %s\n", networkName, address) + + return nil +} + +// RelayerImportKey imports a relayer private key +func RelayerImportKey(_ *cobra.Command, _ []string) error { + // validate private key and password + switch { + case relayerOpts.privateKey == "": + return errors.New("must provide a private key") + case relayerOpts.password == "": + return errors.New("must provide a password") + case !keys.IsRelayerPrivateKeyValid(relayerOpts.privateKey, chains.Network(relayerOpts.network)): + return errors.New("invalid private key") + } + + // resolve the relayer key file path + fileName, err := keys.ResolveRelayerKeyFile(relayerOpts.relayerKeyPath, chains.Network(relayerOpts.network)) + if err != nil { + return errors.Wrap(err, "failed to resolve relayer key file path") + } + + // create path (owner `rwx` permissions) if it does not exist + keyPath := filepath.Dir(fileName) + if _, err := os.Stat(keyPath); os.IsNotExist(err) { + if err := os.MkdirAll(keyPath, 0o700); err != nil { + return errors.Wrapf(err, "failed to create relayer key path: %s", keyPath) + } + } + + // avoid overwriting existing key file + if zetaos.FileExists(fileName) { + return errors.Errorf( + "relayer key %s already exists, please backup and remove it before importing a new key", + fileName, + ) + } + + // encrypt the private key + ciphertext, err := crypto.EncryptAES256GCMBase64(relayerOpts.privateKey, relayerOpts.password) + if err != nil { + return errors.Wrap(err, "private key encryption failed") + } + + // create the relayer key file + err = keys.WriteRelayerKeyToFile(fileName, keys.RelayerKey{PrivateKey: ciphertext}) + if err != nil { + return errors.Wrapf(err, "failed to create relayer key file: %s", fileName) + } + fmt.Printf("successfully imported relayer key: %s\n", fileName) + + return nil +} diff --git a/cmd/zetaclientd/root.go b/cmd/zetaclientd/root.go deleted file mode 100644 index 479ea2afef..0000000000 --- a/cmd/zetaclientd/root.go +++ /dev/null @@ -1,22 +0,0 @@ -package main - -import ( - tmcli "github.com/cometbft/cometbft/libs/cli" - "github.com/spf13/cobra" -) - -var RootCmd = &cobra.Command{ - Use: "zetaclientd", - Short: "ZetaClient CLI", -} -var rootArgs = rootArguments{} - -type rootArguments struct { - zetaCoreHome string -} - -func setHomeDir() error { - var err error - rootArgs.zetaCoreHome, err = RootCmd.Flags().GetString(tmcli.HomeFlag) - return err -} diff --git a/cmd/zetaclientd/start.go b/cmd/zetaclientd/start.go index f86a5fa2ee..903e6d1735 100644 --- a/cmd/zetaclientd/start.go +++ b/cmd/zetaclientd/start.go @@ -13,6 +13,7 @@ import ( "syscall" "time" + ecdsakeygen "github.com/bnb-chain/tss-lib/ecdsa/keygen" "github.com/cometbft/cometbft/crypto/secp256k1" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/p2p/protocol/ping" @@ -37,23 +38,12 @@ import ( "github.com/zeta-chain/node/zetaclient/zetacore" ) -var StartCmd = &cobra.Command{ - Use: "start", - Short: "Start ZetaClient Observer", - RunE: start, -} - -func init() { - RootCmd.AddCommand(StartCmd) -} - -func start(_ *cobra.Command, _ []string) error { - if err := setHomeDir(); err != nil { - return err - } - - SetupConfigForTest() +// todo revamp +// https://github.com/zeta-chain/node/issues/3119 +// https://github.com/zeta-chain/node/issues/3112 +var preParams *ecdsakeygen.LocalPreParams +func Start(_ *cobra.Command, _ []string) error { // Prompt for Hotkey, TSS key-share and relayer key passwords titles := []string{"HotKey", "TSS", "Solana Relayer Key"} passwords, err := zetaos.PromptPasswords(titles) @@ -66,7 +56,7 @@ func start(_ *cobra.Command, _ []string) error { } //Load Config file given path - cfg, err := config.Load(rootArgs.zetaCoreHome) + cfg, err := config.Load(globalOpts.ZetacoreHome) if err != nil { return err } @@ -77,7 +67,7 @@ func start(_ *cobra.Command, _ []string) error { } // Wait until zetacore has started - if len(cfg.Peer) != 0 { + if cfg.Peer != "" { if err := validatePeer(cfg.Peer); err != nil { return errors.Wrap(err, "unable to validate peer") } @@ -104,10 +94,9 @@ func start(_ *cobra.Command, _ []string) error { // CreateZetacoreClient: zetacore client is used for all communication to zetacore , which this client connects to. // Zetacore accumulates votes , and provides a centralized source of truth for all clients - zetacoreClient, err := CreateZetacoreClient(cfg, hotkeyPass, masterLogger) + zetacoreClient, err := createZetacoreClient(cfg, hotkeyPass, masterLogger) if err != nil { - startLogger.Error().Err(err).Msg("CreateZetacoreClient error") - return err + return errors.Wrap(err, "unable to create zetacore client") } // Wait until zetacore is ready to create blocks @@ -145,16 +134,15 @@ func start(_ *cobra.Command, _ []string) error { // This is to ensure that the user does not need to keep their operator key online , and can use a cold key to sign votes signerAddress, err := zetacoreClient.GetKeys().GetAddress() if err != nil { - startLogger.Error().Err(err).Msg("error getting signer address") - return err + return errors.Wrap(err, "error getting signer address") } - CreateAuthzSigner(zetacoreClient.GetKeys().GetOperatorAddress().String(), signerAddress) - startLogger.Debug().Msgf("CreateAuthzSigner is ready") + + createAuthzSigner(zetacoreClient.GetKeys().GetOperatorAddress().String(), signerAddress) + startLogger.Debug().Msgf("createAuthzSigner is ready") // Initialize core parameters from zetacore if err = zetacoreClient.UpdateAppContext(ctx, appContext, startLogger); err != nil { - startLogger.Error().Err(err).Msg("Error getting core parameters") - return err + return errors.Wrap(err, "unable to update app context") } startLogger.Info().Msgf("Config is updated from zetacore\n %s", cfg.StringMasked()) @@ -266,12 +254,12 @@ 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 = GenerateTSS(ctx, masterLogger, zetacoreClient, server) + err = mc.Generate(ctx, masterLogger, zetacoreClient, server) if err != nil { return err } - tss, err := mc.NewTSS( + tss, err := mc.New( ctx, zetacoreClient, tssHistoricalList, @@ -283,7 +271,7 @@ func start(_ *cobra.Command, _ []string) error { return err } if cfg.TestTssKeysign { - err = TestTSS(tss.CurrentPubkey, *tss.Server, masterLogger) + err = mc.TestTSS(tss.CurrentPubkey, *tss.Server, masterLogger) if err != nil { startLogger.Error().Err(err).Msgf("TestTSS error : %s", tss.CurrentPubkey) } diff --git a/cmd/zetaclientd/start_utils.go b/cmd/zetaclientd/start_utils.go deleted file mode 100644 index df80814bb8..0000000000 --- a/cmd/zetaclientd/start_utils.go +++ /dev/null @@ -1,53 +0,0 @@ -package main - -import ( - "fmt" - "net" - "strings" - "time" - - "github.com/pkg/errors" - "github.com/rs/zerolog" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - - "github.com/zeta-chain/node/zetaclient/config" -) - -func waitForZetaCore(config config.Config, logger zerolog.Logger) { - // wait until zetacore is up - logger.Debug().Msg("Waiting for zetacore to open 9090 port...") - for { - _, err := grpc.Dial( - fmt.Sprintf("%s:9090", config.ZetaCoreURL), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - if err != nil { - logger.Warn().Err(err).Msg("grpc dial fail") - time.Sleep(5 * time.Second) - } else { - break - } - } -} - -func validatePeer(seedPeer string) error { - parsedPeer := strings.Split(seedPeer, "/") - - if len(parsedPeer) < 7 { - return errors.New("seed peer missing IP or ID or both, seed: " + seedPeer) - } - - seedIP := parsedPeer[2] - seedID := parsedPeer[6] - - if net.ParseIP(seedIP) == nil { - return errors.New("invalid seed IP address format, seed: " + seedPeer) - } - - if len(seedID) == 0 { - return errors.New("seed id is empty, seed: " + seedPeer) - } - - return nil -} diff --git a/cmd/zetaclientd/tss.go b/cmd/zetaclientd/tss.go new file mode 100644 index 0000000000..cf16131100 --- /dev/null +++ b/cmd/zetaclientd/tss.go @@ -0,0 +1,69 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "time" + + "github.com/bnb-chain/tss-lib/ecdsa/keygen" + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/zeta-chain/node/pkg/crypto" +) + +// TSSEncryptFile encrypts the given file with the given secret key +func TSSEncryptFile(_ *cobra.Command, args []string) error { + var ( + filePath = filepath.Clean(args[0]) + password = args[1] + ) + + // #nosec G304 -- this is a config file + data, err := os.ReadFile(filePath) + if err != nil { + return err + } + + if !json.Valid(data) { + return fmt.Errorf("file %s is not a valid json, may already be encrypted", filePath) + } + + // encrypt the data + cipherText, err := crypto.EncryptAES256GCM(data, password) + if err != nil { + return errors.Wrap(err, "failed to encrypt data") + } + + if err := os.WriteFile(filePath, cipherText, 0o600); err != nil { + return errors.Wrap(err, "failed to write encrypted data to file") + } + + fmt.Printf("File %s successfully encrypted\n", filePath) + + return nil +} + +func TSSGeneratePreParams(_ *cobra.Command, args []string) error { + startTime := time.Now() + preParams, err := keygen.GeneratePreParams(time.Second * 300) + if err != nil { + return err + } + + file, err := os.OpenFile(args[0], os.O_RDWR|os.O_CREATE, 0600) + if err != nil { + return err + } + defer file.Close() + + if err = json.NewEncoder(file).Encode(preParams); err != nil { + return err + } + + fmt.Printf("Generated new pre-parameters in %s\n", time.Since(startTime).String()) + + return nil +} diff --git a/cmd/zetaclientd/utils.go b/cmd/zetaclientd/utils.go index 35fa41f950..011a1bc0ab 100644 --- a/cmd/zetaclientd/utils.go +++ b/cmd/zetaclientd/utils.go @@ -1,8 +1,16 @@ package main import ( + "fmt" + "net" + "strings" + "time" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/pkg/errors" "github.com/rs/zerolog" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" "github.com/zeta-chain/node/zetaclient/authz" "github.com/zeta-chain/node/zetaclient/config" @@ -10,31 +18,76 @@ import ( "github.com/zeta-chain/node/zetaclient/zetacore" ) -func CreateAuthzSigner(granter string, grantee sdk.AccAddress) { +func createAuthzSigner(granter string, grantee sdk.AccAddress) { authz.SetupAuthZSignerList(granter, grantee) } -func CreateZetacoreClient(cfg config.Config, hotkeyPassword string, logger zerolog.Logger) (*zetacore.Client, error) { +func createZetacoreClient(cfg config.Config, hotkeyPassword string, logger zerolog.Logger) (*zetacore.Client, error) { hotKey := cfg.AuthzHotkey chainIP := cfg.ZetaCoreURL kb, _, err := keys.GetKeyringKeybase(cfg, hotkeyPassword) if err != nil { - return nil, err + return nil, errors.Wrap(err, "failed to get keyring base") } - granterAddreess, err := sdk.AccAddressFromBech32(cfg.AuthzGranter) + granterAddress, err := sdk.AccAddressFromBech32(cfg.AuthzGranter) if err != nil { - return nil, err + return nil, errors.Wrap(err, "failed to get granter address") } - k := keys.NewKeysWithKeybase(kb, granterAddreess, cfg.AuthzHotkey, hotkeyPassword) + k := keys.NewKeysWithKeybase(kb, granterAddress, cfg.AuthzHotkey, hotkeyPassword) client, err := zetacore.NewClient(k, chainIP, hotKey, cfg.ChainID, logger) if err != nil { - return nil, err + return nil, errors.Wrap(err, "failed to create zetacore client") } return client, nil } + +func waitForZetaCore(config config.Config, logger zerolog.Logger) { + const ( + port = 9090 + retry = 5 * time.Second + ) + + var ( + url = fmt.Sprintf("%s:%d", config.ZetaCoreURL, port) + opt = grpc.WithTransportCredentials(insecure.NewCredentials()) + ) + + // wait until zetacore is up + logger.Debug().Msgf("Waiting for zetacore to open %d port...", port) + + for { + if _, err := grpc.Dial(url, opt); err != nil { + logger.Warn().Err(err).Msg("grpc dial fail") + time.Sleep(retry) + } else { + break + } + } +} + +func validatePeer(seedPeer string) error { + parsedPeer := strings.Split(seedPeer, "/") + + if len(parsedPeer) < 7 { + return errors.New("seed peer missing IP or ID or both, seed: " + seedPeer) + } + + seedIP := parsedPeer[2] + seedID := parsedPeer[6] + + if net.ParseIP(seedIP) == nil { + return errors.New("invalid seed IP address format, seed: " + seedPeer) + } + + if len(seedID) == 0 { + return errors.New("seed id is empty, seed: " + seedPeer) + } + + return nil +} diff --git a/cmd/zetaclientd/version.go b/cmd/zetaclientd/version.go deleted file mode 100644 index cadba46095..0000000000 --- a/cmd/zetaclientd/version.go +++ /dev/null @@ -1,20 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/spf13/cobra" - - "github.com/zeta-chain/node/pkg/constant" -) - -var VersionCmd = &cobra.Command{ - Use: "version", - Short: "version description from git describe --tags", - RunE: Version, -} - -func Version(_ *cobra.Command, _ []string) error { - fmt.Print(constant.Version) - return nil -} diff --git a/contrib/localnet/scripts/start-zetaclientd.sh b/contrib/localnet/scripts/start-zetaclientd.sh index e403e2d599..93fa5c2af6 100755 --- a/contrib/localnet/scripts/start-zetaclientd.sh +++ b/contrib/localnet/scripts/start-zetaclientd.sh @@ -20,7 +20,7 @@ import_relayer_key() { # import solana (network=7) relayer private key privkey_solana=$(yq -r ".observer_relayer_accounts.relayer_accounts[${num}].solana_private_key" /root/config.yml) - zetaclientd import-relayer-key --network=7 --private-key="$privkey_solana" --password=pass_relayerkey + zetaclientd relayer import-key --network=7 --private-key="$privkey_solana" --password=pass_relayerkey } PREPARAMS_PATH="/root/preparams/${HOSTNAME}.json" diff --git a/zetaclient/config/types.go b/zetaclient/config/types.go index 6b666a8081..d3a1e4b3a0 100644 --- a/zetaclient/config/types.go +++ b/zetaclient/config/types.go @@ -23,8 +23,10 @@ const ( // KeyringBackendFile is the file Cosmos keyring backend KeyringBackendFile KeyringBackend = "file" + DefaultRelayerDir = "relayer-keys" + // DefaultRelayerKeyPath is the default path that relayer keys are stored - DefaultRelayerKeyPath = "~/.zetacored/relayer-keys" + DefaultRelayerKeyPath = "~/.zetacored/" + DefaultRelayerDir ) // ClientConfiguration is a subset of zetaclient config that is used by zetacore client diff --git a/cmd/zetaclientd/keygen_tss.go b/zetaclient/tss/generate.go similarity index 97% rename from cmd/zetaclientd/keygen_tss.go rename to zetaclient/tss/generate.go index 9928a9436c..5334212332 100644 --- a/cmd/zetaclientd/keygen_tss.go +++ b/zetaclient/tss/generate.go @@ -1,4 +1,4 @@ -package main +package tss import ( "context" @@ -19,20 +19,20 @@ import ( "github.com/zeta-chain/node/zetaclient/chains/interfaces" zctx "github.com/zeta-chain/node/zetaclient/context" "github.com/zeta-chain/node/zetaclient/metrics" - mc "github.com/zeta-chain/node/zetaclient/tss" "github.com/zeta-chain/node/zetaclient/zetacore" ) -// GenerateTSS generates a new TSS if keygen is set. +// Generate generates a new TSS if keygen is set. // If a TSS was generated successfully in the past,and the keygen was successful, the function will return without doing anything. // If a keygen has been set the functions will wait for the correct block to arrive and generate a new TSS. // In case of a successful keygen a TSS success vote is broadcasted to zetacore and the newly generate TSS is tested. The generated keyshares are stored in the correct directory // In case of a failed keygen a TSS failed vote is broadcasted to zetacore. -func GenerateTSS( +func Generate( ctx context.Context, logger zerolog.Logger, zetaCoreClient *zetacore.Client, - keygenTssServer *tss.TssServer) error { + keygenTssServer *tss.TssServer, +) error { keygenLogger := logger.With().Str("module", "keygen").Logger() app, err := zctx.FromContext(ctx) if err != nil { @@ -176,7 +176,7 @@ func TestTSS(pubkey string, tssServer tss.TssServer, logger zerolog.Logger) erro keygenLogger := logger.With().Str("module", "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 := mc.TestKeysign(pubkey, tssServer) + err := TestKeysign(pubkey, tssServer) if err != nil { return err } diff --git a/zetaclient/tss/tss_signer.go b/zetaclient/tss/tss_signer.go index e3df81d7ad..83ff502f4a 100644 --- a/zetaclient/tss/tss_signer.go +++ b/zetaclient/tss/tss_signer.go @@ -1,4 +1,6 @@ // Package tss provides the TSS signer functionalities for the zetaclient to sign transactions on external chains +// TODO revamp the whole package +// https://github.com/zeta-chain/node/issues/3119 package tss import ( @@ -85,8 +87,8 @@ type TSS struct { KeysignsTracker *ConcurrentKeysignsTracker } -// NewTSS creates a new TSS instance which can be used to sign transactions -func NewTSS( +// New TSS constructor +func New( ctx context.Context, client interfaces.ZetacoreClient, tssHistoricalList []observertypes.TSS, From 7c475be8e3db1d938c270875c8ac973a09ddff86 Mon Sep 17 00:00:00 2001 From: Alex Gartner Date: Thu, 7 Nov 2024 08:19:21 -0800 Subject: [PATCH 02/10] refactor(e2e): only build zetanode once (#3121) * refactor(e2e): only build zetanode once add docker login back to reusable flow * debug * fix * remove apt install from orchestrator build and just use zetanode as base * use zetanode:latest * fix fork build * ghcr_username * cannot use env in env * try packages write permission * fall back to slow build on forks * have to run matrix always() * more always() * only check failed status (fork will be skipped) --- .github/workflows/e2e.yml | 87 ++++++++++++++++++- .github/workflows/reusable-e2e.yml | 55 ++---------- Dockerfile-localnet | 8 +- Makefile | 39 ++++++--- .../orchestrator/Dockerfile.fastbuild | 8 +- 5 files changed, 124 insertions(+), 73 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 773775e816..4aadf4900e 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -28,7 +28,83 @@ concurrency: cancel-in-progress: true jobs: + # this cannot run on forks as forks cannot push packages in pull request context + # forked pull request will fall back to slow build + build-zetanode: + runs-on: ubuntu-22.04 + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'zeta-chain/node' + env: + DOCKER_IMAGE: ghcr.io/${{ github.repository_owner }}/zetanode + DOCKER_TAG: ${{ github.ref == 'refs/heads/develop' && 'develop' || github.sha }} + outputs: + image: ${{ fromJson(steps.build.outputs.metadata)['image.name'] }} + steps: + - uses: actions/checkout@v4 + + # configure docker to use the containerd snapshotter + # so that we can use the buildkit cache + - uses: depot/use-containerd-snapshotter-action@v1 + + - name: Login to Docker Hub registry + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_READ_ONLY }} + + - name: Login to github docker registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Restore go cache + uses: actions/cache@v4 + id: restore-go-cache + with: + path: | + go-cache + key: cache-${{ hashFiles('go.sum') }} + + - name: Inject go cache into docker + uses: reproducible-containers/buildkit-cache-dance@v3.1.2 + with: + cache-map: | + { + "go-cache": "/root/.cache/go-build" + } + skip-extraction: ${{ steps.restore-go-cache.outputs.cache-hit || github.event_name != 'push' }} + + # this ensures that the version is consistent between cache build and make build + - name: Set version for cache + run: | + NODE_VERSION=$(./version.sh) + echo "NODE_VERSION=$NODE_VERSION" >> $GITHUB_ENV + NODE_COMMIT=$(git log -1 --format='%H') + echo "NODE_COMMIT=$NODE_COMMIT" >> $GITHUB_ENV + + # build zetanode with cache options + - name: Build zetanode for cache + id: build + uses: docker/build-push-action@v6 + env: + CACHE_FROM_CONFIG: "type=registry,ref=ghcr.io/${{ github.repository }}:buildcache" + CACHE_TO_CONFIG: "type=registry,ref=ghcr.io/${{ github.repository }}:buildcache,mode=max" + with: + context: . + file: ./Dockerfile-localnet + push: true + tags: ${{ env.DOCKER_IMAGE }}:${{ env.DOCKER_TAG }} + cache-from: ${{ env.CACHE_FROM_CONFIG }} + cache-to: ${{ github.event_name == 'push' && env.CACHE_TO_CONFIG || '' }} + target: latest-runtime + build-args: | + NODE_VERSION=${{ env.NODE_VERSION }} + NODE_COMMIT=${{ env.NODE_COMMIT }} + matrix-conditionals: + needs: build-zetanode + if: always() runs-on: ubuntu-22.04 env: GH_TOKEN: ${{ github.token }} @@ -119,7 +195,10 @@ jobs: } e2e: - needs: matrix-conditionals + needs: + - build-zetanode + - matrix-conditionals + if: always() strategy: fail-fast: false matrix: @@ -170,12 +249,14 @@ jobs: runs-on: ${{ matrix.runs-on}} run: ${{ matrix.run }} timeout-minutes: "${{ matrix.timeout-minutes || 25 }}" + zetanode-image: ${{ needs.build-zetanode.outputs.image }} enable-monitoring: ${{ needs.matrix-conditionals.outputs.ENABLE_MONITORING == 'true' }} secrets: inherit # this allows you to set a required status check e2e-ok: runs-on: ubuntu-22.04 needs: + - build-zetanode - matrix-conditionals - e2e if: always() @@ -224,6 +305,10 @@ jobs: - run: | + result="${{ needs.build-zetanode.result }}" + if [[ $result == "failed" ]]; then + exit 1 + fi result="${{ needs.e2e.result }}" if [[ $result == "success" || $result == "skipped" ]]; then exit 0 diff --git a/.github/workflows/reusable-e2e.yml b/.github/workflows/reusable-e2e.yml index 88c42466a5..a4aa35c2ec 100644 --- a/.github/workflows/reusable-e2e.yml +++ b/.github/workflows/reusable-e2e.yml @@ -19,6 +19,10 @@ on: required: true type: string default: 'ubuntu-20.04' + zetanode-image: + description: 'docker image to use for zetanode' + required: true + type: string enable-monitoring: description: 'Enable the monitoring stack for this run' type: boolean @@ -31,12 +35,10 @@ jobs: timeout-minutes: ${{ inputs.timeout-minutes }} strategy: fail-fast: false + env: + ZETANODE_IMAGE: ${{ inputs.zetanode-image }} steps: - uses: actions/checkout@v4 - - # configure docker to use the containerd snapshotter - # so that we can use the buildkit cache - - uses: depot/use-containerd-snapshotter-action@v1 - name: Login to Docker Hub registry uses: docker/login-action@v3 @@ -51,50 +53,7 @@ jobs: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - - - name: Restore go cache - uses: actions/cache@v4 - id: restore-go-cache - with: - path: | - go-cache - key: cache-${{ hashFiles('go.sum') }} - - - name: Inject go cache into docker - uses: reproducible-containers/buildkit-cache-dance@v3.1.2 - with: - cache-map: | - { - "go-cache": "/root/.cache/go-build" - } - skip-extraction: ${{ steps.restore-go-cache.outputs.cache-hit || github.event_name != 'push' }} - - # this ensures that the version is consistent between cache build and make build - - name: Set version for cache - run: | - NODE_VERSION=$(./version.sh) - echo "NODE_VERSION=$NODE_VERSION" >> $GITHUB_ENV - NODE_COMMIT=$(git log -1 --format='%H') - echo "NODE_COMMIT=$NODE_COMMIT" >> $GITHUB_ENV - - # build zetanode with cache options - - name: Build zetanode for cache - uses: docker/build-push-action@v6 - env: - CACHE_FROM_CONFIG: "type=registry,ref=ghcr.io/${{ github.repository }}:buildcache" - CACHE_TO_CONFIG: "type=registry,ref=ghcr.io/${{ github.repository }}:buildcache,mode=max" - with: - context: . - file: ./Dockerfile-localnet - push: false - tags: zetanode:latest - cache-from: ${{ env.CACHE_FROM_CONFIG }} - cache-to: ${{ github.event_name == 'push' && env.CACHE_TO_CONFIG || '' }} - target: latest-runtime - build-args: | - NODE_VERSION=${{ env.NODE_VERSION }} - NODE_COMMIT=${{ env.NODE_COMMIT }} - + - name: Enable monitoring if: inputs.enable-monitoring run: | diff --git a/Dockerfile-localnet b/Dockerfile-localnet index 49247d6be4..09e8c15a10 100644 --- a/Dockerfile-localnet +++ b/Dockerfile-localnet @@ -67,7 +67,8 @@ COPY --from=latest-build /go/bin/zetacored /go/bin/zetaclientd /go/bin/zetaclien # Optional old version build (from source). This old build is used as the genesis version in the upgrade tests. # Use --target latest-runtime to skip. -FROM base-build AS old-build-source +# you must have already built the latest image (which the Makefile does) +FROM zetanode:latest AS old-build-source ARG OLD_VERSION RUN git clone https://github.com/zeta-chain/node.git @@ -84,13 +85,12 @@ COPY --from=latest-build /go/bin/zetaclientd-supervisor /usr/local/bin # Optional old version build (from binary). # Use --target latest-runtime to skip. -FROM base-runtime AS old-runtime +# you must have already built the latest image (which the Makefile does) +FROM zetanode:latest AS old-runtime ARG OLD_VERSION ARG BUILDARCH -COPY --from=cosmovisor-build /go/bin/cosmovisor /usr/local/bin -COPY --from=latest-build /go/bin/zetaclientd-supervisor /usr/local/bin RUN curl -Lo /usr/local/bin/zetacored ${OLD_VERSION}/zetacored-linux-${BUILDARCH} && \ chmod 755 /usr/local/bin/zetacored && \ curl -Lo /usr/local/bin/zetaclientd ${OLD_VERSION}/zetaclientd-linux-${BUILDARCH} && \ diff --git a/Makefile b/Makefile index cb191cecb6..38cd6d3be7 100644 --- a/Makefile +++ b/Makefile @@ -223,7 +223,8 @@ generate: proto-gen openapi specs typescript docs-zetacored mocks precompiles fm ############################################################################### ### Localnet ### ############################################################################### -start-localnet: zetanode start-localnet-skip-build +e2e-images: zetanode orchestrator +start-localnet: e2e-images start-localnet-skip-build start-localnet-skip-build: @echo "--> Starting localnet" @@ -238,11 +239,23 @@ stop-localnet: ### E2E tests ### ############################################################################### +ifdef ZETANODE_IMAGE +zetanode: + @echo "Pulling zetanode image" + $(DOCKER) pull $(ZETANODE_IMAGE) + $(DOCKER) tag $(ZETANODE_IMAGE) zetanode:latest +.PHONY: zetanode +else zetanode: @echo "Building zetanode" $(DOCKER) build -t zetanode --build-arg NODE_VERSION=$(NODE_VERSION) --build-arg NODE_COMMIT=$(NODE_COMMIT) --target latest-runtime -f ./Dockerfile-localnet . - $(DOCKER) build -t orchestrator -f contrib/localnet/orchestrator/Dockerfile.fastbuild . .PHONY: zetanode +endif + +orchestrator: + @echo "Building e2e orchestrator" + $(DOCKER) build -t orchestrator -f contrib/localnet/orchestrator/Dockerfile.fastbuild . +.PHONY: orchestrator install-zetae2e: go.sum @echo "--> Installing zetae2e" @@ -253,47 +266,47 @@ solana: @echo "Building solana docker image" $(DOCKER) build -t solana-local -f contrib/localnet/solana/Dockerfile contrib/localnet/solana/ -start-e2e-test: zetanode +start-e2e-test: e2e-images @echo "--> Starting e2e test" cd contrib/localnet/ && $(DOCKER_COMPOSE) up -d -start-e2e-admin-test: zetanode +start-e2e-admin-test: e2e-images @echo "--> Starting e2e admin test" export E2E_ARGS="--skip-regular --test-admin" && \ cd contrib/localnet/ && $(DOCKER_COMPOSE) --profile eth2 up -d -start-e2e-performance-test: zetanode +start-e2e-performance-test: e2e-images @echo "--> Starting e2e performance test" export E2E_ARGS="--test-performance" && \ cd contrib/localnet/ && $(DOCKER_COMPOSE) --profile stress up -d -start-e2e-import-mainnet-test: zetanode +start-e2e-import-mainnet-test: e2e-images @echo "--> Starting e2e import-data test" export ZETACORED_IMPORT_GENESIS_DATA=true && \ export ZETACORED_START_PERIOD=15m && \ cd contrib/localnet/ && ./scripts/import-data.sh mainnet && $(DOCKER_COMPOSE) up -d -start-stress-test: zetanode +start-stress-test: e2e-images @echo "--> Starting stress test" cd contrib/localnet/ && $(DOCKER_COMPOSE) --profile stress up -d -start-tss-migration-test: zetanode +start-tss-migration-test: e2e-images @echo "--> Starting tss migration test" export LOCALNET_MODE=tss-migrate && \ export E2E_ARGS="--test-tss-migration" && \ cd contrib/localnet/ && $(DOCKER_COMPOSE) up -d -start-solana-test: zetanode solana +start-solana-test: e2e-images solana @echo "--> Starting solana test" export E2E_ARGS="--skip-regular --test-solana" && \ cd contrib/localnet/ && $(DOCKER_COMPOSE) --profile solana up -d -start-ton-test: zetanode +start-ton-test: e2e-images @echo "--> Starting TON test" export E2E_ARGS="--skip-regular --test-ton" && \ cd contrib/localnet/ && $(DOCKER_COMPOSE) --profile ton up -d -start-v2-test: zetanode +start-v2-test: e2e-images @echo "--> Starting e2e smart contracts v2 test" export E2E_ARGS="--skip-regular --test-v2" && \ cd contrib/localnet/ && $(DOCKER_COMPOSE) up -d @@ -305,7 +318,7 @@ start-v2-test: zetanode # build from source only if requested # NODE_VERSION and NODE_COMMIT must be set as old-runtime depends on lastest-runtime ifdef UPGRADE_TEST_FROM_SOURCE -zetanode-upgrade: zetanode +zetanode-upgrade: e2e-images @echo "Building zetanode-upgrade from source" $(DOCKER) build -t zetanode:old -f Dockerfile-localnet --target old-runtime-source \ --build-arg OLD_VERSION='release/v21' \ @@ -314,7 +327,7 @@ zetanode-upgrade: zetanode . .PHONY: zetanode-upgrade else -zetanode-upgrade: zetanode +zetanode-upgrade: e2e-images @echo "Building zetanode-upgrade from binaries" $(DOCKER) build -t zetanode:old -f Dockerfile-localnet --target old-runtime \ --build-arg OLD_VERSION='https://github.com/zeta-chain/node/releases/download/v21.0.0' \ diff --git a/contrib/localnet/orchestrator/Dockerfile.fastbuild b/contrib/localnet/orchestrator/Dockerfile.fastbuild index cbab881a65..308e59a146 100644 --- a/contrib/localnet/orchestrator/Dockerfile.fastbuild +++ b/contrib/localnet/orchestrator/Dockerfile.fastbuild @@ -1,17 +1,11 @@ # syntax=ghcr.io/zeta-chain/docker-dockerfile:1.9-labs # check=error=true -FROM zetanode:latest AS zeta FROM ghcr.io/zeta-chain/ethereum-client-go:v1.10.26 AS geth FROM ghcr.io/zeta-chain/solana-docker:1.18.15 AS solana -FROM ghcr.io/zeta-chain/golang:1.22.7-bookworm AS orchestrator - -RUN apt update && \ - apt install -yq jq yq curl tmux python3 openssh-server iputils-ping iproute2 bind9-host && \ - rm -rf /var/lib/apt/lists/* +FROM zetanode:latest COPY --from=geth /usr/local/bin/geth /usr/local/bin/ COPY --from=solana /usr/bin/solana /usr/local/bin/ -COPY --from=zeta /usr/local/bin/zetacored /usr/local/bin/zetaclientd /usr/local/bin/zetae2e /usr/local/bin/ COPY contrib/localnet/orchestrator/start-zetae2e.sh /work/ COPY contrib/localnet/scripts/wait-for-ton.sh /work/ From 946eeda41c0892d50276a219403f7dd67dbb1694 Mon Sep 17 00:00:00 2001 From: Alex Gartner Date: Thu, 7 Nov 2024 11:13:53 -0800 Subject: [PATCH 03/10] chore: add coderabbit instructions for tests (#3127) --- .coderabbit.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.coderabbit.yaml b/.coderabbit.yaml index 7b7fa07ba7..dea07286ff 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -33,6 +33,10 @@ reviews: instructions: >- Review the shell scripts, point out issues relative to security, performance, and maintainability. + - path: '{*e2e*,*_test.go}' + instructions: >- + Errors in tests may be handled via require.NoError(t, err) rather + than explicitly returning error. auto_review: drafts: false From 0e864946e185df133480386b2fcbc5e02a3fe893 Mon Sep 17 00:00:00 2001 From: Alex Gartner Date: Thu, 7 Nov 2024 14:54:58 -0800 Subject: [PATCH 04/10] test: add e2e consensus test (#3126) * test: add e2e consensus test * move to merge queue * fix * add e2e block production monitor --- .github/workflows/e2e.yml | 36 +++++++++--- Makefile | 8 ++- cmd/zetae2e/local/local.go | 4 ++ cmd/zetae2e/local/monitor_block_production.go | 55 +++++++++++++++++++ contrib/localnet/docker-compose.yml | 3 +- 5 files changed, 97 insertions(+), 9 deletions(-) create mode 100644 cmd/zetae2e/local/monitor_block_production.go diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 4aadf4900e..cebedde240 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -111,6 +111,7 @@ jobs: outputs: DEFAULT_TESTS: ${{ steps.matrix-conditionals.outputs.DEFAULT_TESTS }} UPGRADE_TESTS: ${{ steps.matrix-conditionals.outputs.UPGRADE_TESTS }} + CONSENSUS_TESTS: ${{ steps.matrix-conditionals.outputs.CONSENSUS_TESTS }} UPGRADE_LIGHT_TESTS: ${{ steps.matrix-conditionals.outputs.UPGRADE_LIGHT_TESTS }} UPGRADE_IMPORT_MAINNET_TESTS: ${{ steps.matrix-conditionals.outputs.UPGRADE_IMPORT_MAINNET_TESTS }} ADMIN_TESTS: ${{ steps.matrix-conditionals.outputs.ADMIN_TESTS }} @@ -128,16 +129,22 @@ jobs: uses: actions/github-script@v7 with: script: | - if (context.eventName === 'pull_request') { + const getPrLabels = async (pull_number) => { const { data: pr } = await github.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.payload.pull_request.number, - }); - const labels = pr.labels.map(label => label.name); - console.log("labels:", labels); + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pull_number, + }); + const labels = pr.labels.map(label => label.name); + console.log(`labels for ${pull_number}:`, labels); + return labels; + } + + if (context.eventName === 'pull_request') { + const labels = await getPrLabels(context.payload.pull_request.number); core.setOutput('DEFAULT_TESTS', true); core.setOutput('UPGRADE_TESTS', labels.includes('UPGRADE_TESTS')); + core.setOutput('CONSENSUS_TESTS', labels.includes('CONSENSUS_TESTS')); core.setOutput('UPGRADE_LIGHT_TESTS', labels.includes('UPGRADE_LIGHT_TESTS')); core.setOutput('UPGRADE_IMPORT_MAINNET_TESTS', labels.includes('UPGRADE_IMPORT_MAINNET_TESTS')); core.setOutput('ADMIN_TESTS', labels.includes('ADMIN_TESTS')); @@ -150,8 +157,20 @@ jobs: core.setOutput('V2_MIGRATION_TESTS', labels.includes('V2_MIGRATION_TESTS')); // for v2 tests, TODO: remove this once we fully migrate to v2 (https://github.com/zeta-chain/node/issues/2627) core.setOutput('ENABLE_MONITORING', labels.includes('ENABLE_MONITORING')); } else if (context.eventName === 'merge_group') { + // default mergequeue tests core.setOutput('DEFAULT_TESTS', true); core.setOutput('UPGRADE_LIGHT_TESTS', true); + + // conditional tests based on PR labels + const commit_message = context.payload.merge_group.head_commit.message; + const pr_match = commit_message.split('\n')[0].match(/\(#(\d+)\)$/); + if (!pr_match) { + console.error("unable to extract PR number from mergequeue commit message"); + return; + } + const pr_number = pr_match[1]; + const pr_labels = await getPrLabels(pr_number); + core.setOutput('CONSENSUS_TESTS', !pr_labels.includes('CONSENSUS_BREAKING_ACK')); } else if (context.eventName === 'push' && context.ref === 'refs/heads/develop') { core.setOutput('DEFAULT_TESTS', true); } else if (context.eventName === 'push' && context.ref.startsWith('refs/heads/release/')) { @@ -206,6 +225,9 @@ jobs: - make-target: "start-e2e-test" runs-on: ubuntu-20.04 run: ${{ needs.matrix-conditionals.outputs.DEFAULT_TESTS == 'true' }} + - make-target: "start-e2e-consensus-test" + runs-on: ubuntu-20.04 + run: ${{ needs.matrix-conditionals.outputs.CONSENSUS_TESTS == 'true' }} - make-target: "start-upgrade-test" runs-on: ubuntu-20.04 run: ${{ needs.matrix-conditionals.outputs.UPGRADE_TESTS == 'true' }} diff --git a/Makefile b/Makefile index 38cd6d3be7..ebeacdb26e 100644 --- a/Makefile +++ b/Makefile @@ -268,7 +268,7 @@ solana: start-e2e-test: e2e-images @echo "--> Starting e2e test" - cd contrib/localnet/ && $(DOCKER_COMPOSE) up -d + cd contrib/localnet/ && $(DOCKER_COMPOSE) up -d start-e2e-admin-test: e2e-images @echo "--> Starting e2e admin test" @@ -286,6 +286,12 @@ start-e2e-import-mainnet-test: e2e-images export ZETACORED_START_PERIOD=15m && \ cd contrib/localnet/ && ./scripts/import-data.sh mainnet && $(DOCKER_COMPOSE) up -d +start-e2e-consensus-test: e2e-images + @echo "--> Starting e2e consensus test" + export ZETACORE1_IMAGE=ghcr.io/zeta-chain/zetanode:develop && \ + export ZETACORE1_PLATFORM=linux/amd64 && \ + cd contrib/localnet/ && $(DOCKER_COMPOSE) up -d + start-stress-test: e2e-images @echo "--> Starting stress test" cd contrib/localnet/ && $(DOCKER_COMPOSE) --profile stress up -d diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index 81779faef9..e7b1ee3e29 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -190,6 +190,10 @@ 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 { diff --git a/cmd/zetae2e/local/monitor_block_production.go b/cmd/zetae2e/local/monitor_block_production.go new file mode 100644 index 0000000000..89de03012e --- /dev/null +++ b/cmd/zetae2e/local/monitor_block_production.go @@ -0,0 +1,55 @@ +package local + +import ( + "context" + "fmt" + "os" + "time" + + rpchttp "github.com/cometbft/cometbft/rpc/client/http" + cometbft_types "github.com/cometbft/cometbft/types" + + "github.com/zeta-chain/node/e2e/config" +) + +// monitorBlockProductionExit calls monitorBlockProduction and exits upon any error +func monitorBlockProductionExit(ctx context.Context, conf config.Config) { + err := monitorBlockProduction(ctx, conf) + if err != nil { + fmt.Printf("❌ block monitor: %v\n", err) + os.Exit(2) + } +} + +// monitorBlockProduction subscribes to new block events to monitor if blocks are being produced +// at least every four seconds +func monitorBlockProduction(ctx context.Context, conf config.Config) error { + rpcClient, err := rpchttp.New(conf.RPCs.ZetaCoreRPC, "/websocket") + if err != nil { + return fmt.Errorf("new zetacore rpc: %w", err) + } + + err = rpcClient.WSEvents.Start() + if err != nil { + return fmt.Errorf("start ws events: %w", err) + } + blockEventChan, err := rpcClient.WSEvents.Subscribe(ctx, "", "tm.event='NewBlock'") + if err != nil { + return fmt.Errorf("subscribe: %w", err) + } + latestNewBlockEvent := cometbft_types.EventDataNewBlock{} + for { + select { + case event := <-blockEventChan: + newBlockEvent, ok := event.Data.(cometbft_types.EventDataNewBlock) + if !ok { + return fmt.Errorf("expecting new block event, got %T", event.Data) + } + latestNewBlockEvent = newBlockEvent + case <-time.After(4 * time.Second): + return fmt.Errorf("timed out waiting for new block (last block %d)", latestNewBlockEvent.Block.Height) + case <-ctx.Done(): + return nil + } + } +} diff --git a/contrib/localnet/docker-compose.yml b/contrib/localnet/docker-compose.yml index 4f36583d91..670befa7b6 100644 --- a/contrib/localnet/docker-compose.yml +++ b/contrib/localnet/docker-compose.yml @@ -67,7 +67,8 @@ services: - ~/.zetacored/genesis_data:/root/genesis_data zetacore1: - image: zetanode:latest + image: ${ZETACORE1_IMAGE-zetanode:latest} + platform: ${ZETACORE1_PLATFORM-} container_name: zetacore1 hostname: zetacore1 networks: From b5985bc1872a6d12fd408043015829c5df96a38d Mon Sep 17 00:00:00 2001 From: Lucas Bertrand Date: Fri, 8 Nov 2024 10:32:41 +0100 Subject: [PATCH 05/10] fix: blocked gateway cctx for high-gas deposit (#3106) * test high gas consumption * increase gas use * increase gas use * add gas limit * create test for out of gas * generate and lint * set gas limit * changelog and add test back * handle error * Update e2e/e2etests/test_deposit_and_call_out_of_gas.go Co-authored-by: Dmitry S <11892559+swift1337@users.noreply.github.com> * fix --------- Co-authored-by: Dmitry S <11892559+swift1337@users.noreply.github.com> --- changelog.md | 4 +- cmd/zetae2e/local/v2.go | 1 + .../testgasconsumer/TestGasConsumer.abi | 47 ++++ .../testgasconsumer/TestGasConsumer.bin | 1 + .../testgasconsumer/TestGasConsumer.go | 231 ++++++++++++++++++ .../testgasconsumer/TestGasConsumer.json | 50 ++++ .../testgasconsumer/TestGasConsumer.sol | 42 ++++ e2e/contracts/testgasconsumer/bindings.go | 8 + e2e/e2etests/e2etests.go | 9 + .../test_deposit_and_call_out_of_gas.go | 37 +++ x/fungible/keeper/v2_evm.go | 13 +- 11 files changed, 435 insertions(+), 8 deletions(-) create mode 100644 e2e/contracts/testgasconsumer/TestGasConsumer.abi create mode 100644 e2e/contracts/testgasconsumer/TestGasConsumer.bin create mode 100644 e2e/contracts/testgasconsumer/TestGasConsumer.go create mode 100644 e2e/contracts/testgasconsumer/TestGasConsumer.json create mode 100644 e2e/contracts/testgasconsumer/TestGasConsumer.sol create mode 100644 e2e/contracts/testgasconsumer/bindings.go create mode 100644 e2e/e2etests/test_deposit_and_call_out_of_gas.go diff --git a/changelog.md b/changelog.md index 8e53bb366b..846a76f2e7 100644 --- a/changelog.md +++ b/changelog.md @@ -15,11 +15,9 @@ ### Refactor * [3118](https://github.com/zeta-chain/node/pull/3118) - zetaclient: remove hsm signer -### Tests - ### 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 ## v21.0.0 diff --git a/cmd/zetae2e/local/v2.go b/cmd/zetae2e/local/v2.go index 22de8a6c51..f4dac2a534 100644 --- a/cmd/zetae2e/local/v2.go +++ b/cmd/zetae2e/local/v2.go @@ -55,6 +55,7 @@ func startV2Tests(eg *errgroup.Group, conf config.Config, deployerRunner *runner e2etests.TestV2ETHDepositAndCallRevertWithCallName, e2etests.TestV2ETHWithdrawAndCallRevertName, e2etests.TestV2ETHWithdrawAndCallRevertWithCallName, + e2etests.TestDepositAndCallOutOfGasName, ), ) diff --git a/e2e/contracts/testgasconsumer/TestGasConsumer.abi b/e2e/contracts/testgasconsumer/TestGasConsumer.abi new file mode 100644 index 0000000000..3406cf3e88 --- /dev/null +++ b/e2e/contracts/testgasconsumer/TestGasConsumer.abi @@ -0,0 +1,47 @@ +[ + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes", + "name": "origin", + "type": "bytes" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "chainID", + "type": "uint256" + } + ], + "internalType": "struct TestGasConsumer.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" + } +] diff --git a/e2e/contracts/testgasconsumer/TestGasConsumer.bin b/e2e/contracts/testgasconsumer/TestGasConsumer.bin new file mode 100644 index 0000000000..590b637cc1 --- /dev/null +++ b/e2e/contracts/testgasconsumer/TestGasConsumer.bin @@ -0,0 +1 @@ +6080604052348015600f57600080fd5b5061036d8061001f6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80635bcfd61614610030575b600080fd5b61004a60048036038101906100459190610233565b61004c565b005b61005461005b565b5050505050565b6000624c4b4090506000614e209050600081836100789190610306565b905060005b818110156100bb576000819080600181540180825580915050600190039060005260206000200160009091909190915055808060010191505061007d565b506000806100c991906100ce565b505050565b50805460008255906000526020600020908101906100ec91906100ef565b50565b5b808211156101085760008160009055506001016100f0565b5090565b600080fd5b600080fd5b600080fd5b60006060828403121561013157610130610116565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101658261013a565b9050919050565b6101758161015a565b811461018057600080fd5b50565b6000813590506101928161016c565b92915050565b6000819050919050565b6101ab81610198565b81146101b657600080fd5b50565b6000813590506101c8816101a2565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126101f3576101f26101ce565b5b8235905067ffffffffffffffff8111156102105761020f6101d3565b5b60208301915083600182028301111561022c5761022b6101d8565b5b9250929050565b60008060008060006080868803121561024f5761024e61010c565b5b600086013567ffffffffffffffff81111561026d5761026c610111565b5b6102798882890161011b565b955050602061028a88828901610183565b945050604061029b888289016101b9565b935050606086013567ffffffffffffffff8111156102bc576102bb610111565b5b6102c8888289016101dd565b92509250509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061031182610198565b915061031c83610198565b92508261032c5761032b6102d7565b5b82820490509291505056fea2646970667358221220e1d03a34090a8a647a128849d9f9434831ba3b1e4d28a514d9c9dc922068351e64736f6c634300081a0033 diff --git a/e2e/contracts/testgasconsumer/TestGasConsumer.go b/e2e/contracts/testgasconsumer/TestGasConsumer.go new file mode 100644 index 0000000000..b40770c40b --- /dev/null +++ b/e2e/contracts/testgasconsumer/TestGasConsumer.go @@ -0,0 +1,231 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package testgasconsumer + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// TestGasConsumerzContext is an auto generated low-level Go binding around an user-defined struct. +type TestGasConsumerzContext struct { + Origin []byte + Sender common.Address + ChainID *big.Int +} + +// TestGasConsumerMetaData contains all meta data concerning the TestGasConsumer contract. +var TestGasConsumerMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"origin\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainID\",\"type\":\"uint256\"}],\"internalType\":\"structTestGasConsumer.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\"}]", + Bin: "0x6080604052348015600f57600080fd5b5061036d8061001f6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80635bcfd61614610030575b600080fd5b61004a60048036038101906100459190610233565b61004c565b005b61005461005b565b5050505050565b6000624c4b4090506000614e209050600081836100789190610306565b905060005b818110156100bb576000819080600181540180825580915050600190039060005260206000200160009091909190915055808060010191505061007d565b506000806100c991906100ce565b505050565b50805460008255906000526020600020908101906100ec91906100ef565b50565b5b808211156101085760008160009055506001016100f0565b5090565b600080fd5b600080fd5b600080fd5b60006060828403121561013157610130610116565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101658261013a565b9050919050565b6101758161015a565b811461018057600080fd5b50565b6000813590506101928161016c565b92915050565b6000819050919050565b6101ab81610198565b81146101b657600080fd5b50565b6000813590506101c8816101a2565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126101f3576101f26101ce565b5b8235905067ffffffffffffffff8111156102105761020f6101d3565b5b60208301915083600182028301111561022c5761022b6101d8565b5b9250929050565b60008060008060006080868803121561024f5761024e61010c565b5b600086013567ffffffffffffffff81111561026d5761026c610111565b5b6102798882890161011b565b955050602061028a88828901610183565b945050604061029b888289016101b9565b935050606086013567ffffffffffffffff8111156102bc576102bb610111565b5b6102c8888289016101dd565b92509250509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061031182610198565b915061031c83610198565b92508261032c5761032b6102d7565b5b82820490509291505056fea2646970667358221220e1d03a34090a8a647a128849d9f9434831ba3b1e4d28a514d9c9dc922068351e64736f6c634300081a0033", +} + +// TestGasConsumerABI is the input ABI used to generate the binding from. +// Deprecated: Use TestGasConsumerMetaData.ABI instead. +var TestGasConsumerABI = TestGasConsumerMetaData.ABI + +// TestGasConsumerBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use TestGasConsumerMetaData.Bin instead. +var TestGasConsumerBin = TestGasConsumerMetaData.Bin + +// DeployTestGasConsumer deploys a new Ethereum contract, binding an instance of TestGasConsumer to it. +func DeployTestGasConsumer(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *TestGasConsumer, error) { + parsed, err := TestGasConsumerMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(TestGasConsumerBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &TestGasConsumer{TestGasConsumerCaller: TestGasConsumerCaller{contract: contract}, TestGasConsumerTransactor: TestGasConsumerTransactor{contract: contract}, TestGasConsumerFilterer: TestGasConsumerFilterer{contract: contract}}, nil +} + +// TestGasConsumer is an auto generated Go binding around an Ethereum contract. +type TestGasConsumer struct { + TestGasConsumerCaller // Read-only binding to the contract + TestGasConsumerTransactor // Write-only binding to the contract + TestGasConsumerFilterer // Log filterer for contract events +} + +// TestGasConsumerCaller is an auto generated read-only Go binding around an Ethereum contract. +type TestGasConsumerCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// TestGasConsumerTransactor is an auto generated write-only Go binding around an Ethereum contract. +type TestGasConsumerTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// TestGasConsumerFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type TestGasConsumerFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// TestGasConsumerSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type TestGasConsumerSession struct { + Contract *TestGasConsumer // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// TestGasConsumerCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type TestGasConsumerCallerSession struct { + Contract *TestGasConsumerCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// TestGasConsumerTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type TestGasConsumerTransactorSession struct { + Contract *TestGasConsumerTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// TestGasConsumerRaw is an auto generated low-level Go binding around an Ethereum contract. +type TestGasConsumerRaw struct { + Contract *TestGasConsumer // Generic contract binding to access the raw methods on +} + +// TestGasConsumerCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type TestGasConsumerCallerRaw struct { + Contract *TestGasConsumerCaller // Generic read-only contract binding to access the raw methods on +} + +// TestGasConsumerTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type TestGasConsumerTransactorRaw struct { + Contract *TestGasConsumerTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewTestGasConsumer creates a new instance of TestGasConsumer, bound to a specific deployed contract. +func NewTestGasConsumer(address common.Address, backend bind.ContractBackend) (*TestGasConsumer, error) { + contract, err := bindTestGasConsumer(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &TestGasConsumer{TestGasConsumerCaller: TestGasConsumerCaller{contract: contract}, TestGasConsumerTransactor: TestGasConsumerTransactor{contract: contract}, TestGasConsumerFilterer: TestGasConsumerFilterer{contract: contract}}, nil +} + +// NewTestGasConsumerCaller creates a new read-only instance of TestGasConsumer, bound to a specific deployed contract. +func NewTestGasConsumerCaller(address common.Address, caller bind.ContractCaller) (*TestGasConsumerCaller, error) { + contract, err := bindTestGasConsumer(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &TestGasConsumerCaller{contract: contract}, nil +} + +// NewTestGasConsumerTransactor creates a new write-only instance of TestGasConsumer, bound to a specific deployed contract. +func NewTestGasConsumerTransactor(address common.Address, transactor bind.ContractTransactor) (*TestGasConsumerTransactor, error) { + contract, err := bindTestGasConsumer(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &TestGasConsumerTransactor{contract: contract}, nil +} + +// NewTestGasConsumerFilterer creates a new log filterer instance of TestGasConsumer, bound to a specific deployed contract. +func NewTestGasConsumerFilterer(address common.Address, filterer bind.ContractFilterer) (*TestGasConsumerFilterer, error) { + contract, err := bindTestGasConsumer(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &TestGasConsumerFilterer{contract: contract}, nil +} + +// bindTestGasConsumer binds a generic wrapper to an already deployed contract. +func bindTestGasConsumer(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := TestGasConsumerMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_TestGasConsumer *TestGasConsumerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _TestGasConsumer.Contract.TestGasConsumerCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_TestGasConsumer *TestGasConsumerRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _TestGasConsumer.Contract.TestGasConsumerTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_TestGasConsumer *TestGasConsumerRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _TestGasConsumer.Contract.TestGasConsumerTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_TestGasConsumer *TestGasConsumerCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _TestGasConsumer.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_TestGasConsumer *TestGasConsumerTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _TestGasConsumer.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_TestGasConsumer *TestGasConsumerTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _TestGasConsumer.Contract.contract.Transact(opts, method, params...) +} + +// OnCall is a paid mutator transaction binding the contract method 0x5bcfd616. +// +// Solidity: function onCall((bytes,address,uint256) _context, address _zrc20, uint256 _amount, bytes _message) returns() +func (_TestGasConsumer *TestGasConsumerTransactor) OnCall(opts *bind.TransactOpts, _context TestGasConsumerzContext, _zrc20 common.Address, _amount *big.Int, _message []byte) (*types.Transaction, error) { + return _TestGasConsumer.contract.Transact(opts, "onCall", _context, _zrc20, _amount, _message) +} + +// OnCall is a paid mutator transaction binding the contract method 0x5bcfd616. +// +// Solidity: function onCall((bytes,address,uint256) _context, address _zrc20, uint256 _amount, bytes _message) returns() +func (_TestGasConsumer *TestGasConsumerSession) OnCall(_context TestGasConsumerzContext, _zrc20 common.Address, _amount *big.Int, _message []byte) (*types.Transaction, error) { + return _TestGasConsumer.Contract.OnCall(&_TestGasConsumer.TransactOpts, _context, _zrc20, _amount, _message) +} + +// OnCall is a paid mutator transaction binding the contract method 0x5bcfd616. +// +// Solidity: function onCall((bytes,address,uint256) _context, address _zrc20, uint256 _amount, bytes _message) returns() +func (_TestGasConsumer *TestGasConsumerTransactorSession) OnCall(_context TestGasConsumerzContext, _zrc20 common.Address, _amount *big.Int, _message []byte) (*types.Transaction, error) { + return _TestGasConsumer.Contract.OnCall(&_TestGasConsumer.TransactOpts, _context, _zrc20, _amount, _message) +} diff --git a/e2e/contracts/testgasconsumer/TestGasConsumer.json b/e2e/contracts/testgasconsumer/TestGasConsumer.json new file mode 100644 index 0000000000..9f80d60d5f --- /dev/null +++ b/e2e/contracts/testgasconsumer/TestGasConsumer.json @@ -0,0 +1,50 @@ +{ + "abi": [ + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes", + "name": "origin", + "type": "bytes" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "chainID", + "type": "uint256" + } + ], + "internalType": "struct TestGasConsumer.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" + } + ], + "bin": "6080604052348015600f57600080fd5b5061036d8061001f6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80635bcfd61614610030575b600080fd5b61004a60048036038101906100459190610233565b61004c565b005b61005461005b565b5050505050565b6000624c4b4090506000614e209050600081836100789190610306565b905060005b818110156100bb576000819080600181540180825580915050600190039060005260206000200160009091909190915055808060010191505061007d565b506000806100c991906100ce565b505050565b50805460008255906000526020600020908101906100ec91906100ef565b50565b5b808211156101085760008160009055506001016100f0565b5090565b600080fd5b600080fd5b600080fd5b60006060828403121561013157610130610116565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101658261013a565b9050919050565b6101758161015a565b811461018057600080fd5b50565b6000813590506101928161016c565b92915050565b6000819050919050565b6101ab81610198565b81146101b657600080fd5b50565b6000813590506101c8816101a2565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126101f3576101f26101ce565b5b8235905067ffffffffffffffff8111156102105761020f6101d3565b5b60208301915083600182028301111561022c5761022b6101d8565b5b9250929050565b60008060008060006080868803121561024f5761024e61010c565b5b600086013567ffffffffffffffff81111561026d5761026c610111565b5b6102798882890161011b565b955050602061028a88828901610183565b945050604061029b888289016101b9565b935050606086013567ffffffffffffffff8111156102bc576102bb610111565b5b6102c8888289016101dd565b92509250509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061031182610198565b915061031c83610198565b92508261032c5761032b6102d7565b5b82820490509291505056fea2646970667358221220e1d03a34090a8a647a128849d9f9434831ba3b1e4d28a514d9c9dc922068351e64736f6c634300081a0033" +} diff --git a/e2e/contracts/testgasconsumer/TestGasConsumer.sol b/e2e/contracts/testgasconsumer/TestGasConsumer.sol new file mode 100644 index 0000000000..4d1d322cd2 --- /dev/null +++ b/e2e/contracts/testgasconsumer/TestGasConsumer.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +// TestGasConsumer is a contract used to simulate high gas consumption +contract TestGasConsumer { + // used to simulate gas consumption + uint256[] private storageArray; + + struct zContext { + bytes origin; + address sender; + uint256 chainID; + } + + // Universal contract interface on ZEVM + function onCall( + zContext calldata _context, + address _zrc20, + uint256 _amount, + bytes calldata _message + ) + external + { + consumeGas(); + } + + function consumeGas() internal { + // Approximate target gas consumption + uint256 targetGas = 5000000; + // 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; + } +} \ No newline at end of file diff --git a/e2e/contracts/testgasconsumer/bindings.go b/e2e/contracts/testgasconsumer/bindings.go new file mode 100644 index 0000000000..baa99020ba --- /dev/null +++ b/e2e/contracts/testgasconsumer/bindings.go @@ -0,0 +1,8 @@ +//go:generate sh -c "solc TestGasConsumer.sol --evm-version london --combined-json abi,bin | jq '.contracts.\"TestGasConsumer.sol:TestGasConsumer\"' > TestGasConsumer.json" +//go:generate sh -c "cat TestGasConsumer.json | jq .abi > TestGasConsumer.abi" +//go:generate sh -c "cat TestGasConsumer.json | jq .bin | tr -d '\"' > TestGasConsumer.bin" +//go:generate sh -c "abigen --abi TestGasConsumer.abi --bin TestGasConsumer.bin --pkg testgasconsumer --type TestGasConsumer --out TestGasConsumer.go" + +package testgasconsumer + +var _ TestGasConsumer diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index 82764cb537..c1c5d8b533 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -149,6 +149,7 @@ const ( TestV2ETHWithdrawAndCallThroughContractName = "v2_eth_withdraw_and_call_through_contract" TestV2ETHWithdrawAndCallRevertName = "v2_eth_withdraw_and_call_revert" TestV2ETHWithdrawAndCallRevertWithCallName = "v2_eth_withdraw_and_call_revert_with_call" + TestDepositAndCallOutOfGasName = "deposit_and_call_out_of_gas" TestV2ERC20DepositName = "v2_erc20_deposit" TestV2ERC20DepositAndCallName = "v2_erc20_deposit_and_call" TestV2ERC20DepositAndCallNoMessageName = "v2_erc20_deposit_and_call_no_message" @@ -896,6 +897,14 @@ var AllE2ETests = []runner.E2ETest{ }, TestV2ETHWithdrawAndCallRevertWithCall, ), + runner.NewE2ETest( + TestDepositAndCallOutOfGasName, + "deposit Ether into ZEVM and call a contract that runs out of gas", + []runner.ArgDefinition{ + {Description: "amount in wei", DefaultValue: "10000000000000000"}, + }, + TestDepositAndCallOutOfGas, + ), runner.NewE2ETest( TestV2ERC20DepositName, "deposit ERC20 into ZEVM using V2 contract", diff --git a/e2e/e2etests/test_deposit_and_call_out_of_gas.go b/e2e/e2etests/test_deposit_and_call_out_of_gas.go new file mode 100644 index 0000000000..4b42995979 --- /dev/null +++ b/e2e/e2etests/test_deposit_and_call_out_of_gas.go @@ -0,0 +1,37 @@ +package e2etests + +import ( + "math/big" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + + "github.com/zeta-chain/node/e2e/contracts/testgasconsumer" + "github.com/zeta-chain/node/e2e/runner" + "github.com/zeta-chain/node/e2e/utils" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" +) + +// TestDepositAndCallOutOfGas tests that a deposit and call that consumer all gas will revert +func TestDepositAndCallOutOfGas(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount := parseBigInt(r, args[0]) + + // Deploy the GasConsumer contract + gasConsumerAddress, _, _, err := testgasconsumer.DeployTestGasConsumer(r.ZEVMAuth, r.ZEVMClient) + require.NoError(r, err) + + // perform the deposit and call to the GasConsumer contract + tx := r.V2ETHDepositAndCall( + gasConsumerAddress, + amount, + []byte(randomPayload(r)), + gatewayevm.RevertOptions{OnRevertGasLimit: big.NewInt(0)}, + ) + + // wait for the cctx to be reverted + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "deposit_and_call") + require.Equal(r, crosschaintypes.CctxStatus_Reverted, cctx.CctxStatus.Status) +} diff --git a/x/fungible/keeper/v2_evm.go b/x/fungible/keeper/v2_evm.go index b2b76dc428..c92116ca4b 100644 --- a/x/fungible/keeper/v2_evm.go +++ b/x/fungible/keeper/v2_evm.go @@ -15,6 +15,9 @@ import ( "github.com/zeta-chain/node/x/fungible/types" ) +// gatewayGasLimit is the gas limit for the gateway functions +var gatewayGasLimit = big.NewInt(1_000_000) + // CallUpdateGatewayAddress calls the updateGatewayAddress function on the ZRC20 contract // function updateGatewayAddress(address addr) func (k Keeper) CallUpdateGatewayAddress( @@ -33,7 +36,7 @@ func (k Keeper) CallUpdateGatewayAddress( types.ModuleAddressEVM, zrc20Address, BigIntZero, - nil, + gatewayGasLimit, true, false, "updateGatewayAddress", @@ -83,7 +86,7 @@ func (k Keeper) CallDepositAndCallZRC20( types.ModuleAddressEVM, gatewayAddr, BigIntZero, - nil, + gatewayGasLimit, true, false, "depositAndCall0", @@ -133,7 +136,7 @@ func (k Keeper) CallExecute( types.ModuleAddressEVM, gatewayAddr, BigIntZero, - nil, + gatewayGasLimit, true, false, "execute", @@ -179,7 +182,7 @@ func (k Keeper) CallExecuteRevert( types.ModuleAddressEVM, gatewayAddr, BigIntZero, - nil, + gatewayGasLimit, true, false, "executeRevert", @@ -231,7 +234,7 @@ func (k Keeper) CallDepositAndRevert( types.ModuleAddressEVM, gatewayAddr, BigIntZero, - nil, + gatewayGasLimit, true, false, "depositAndRevert", From 6d7b91d526efe975e9ebe6e534f2735524d62b97 Mon Sep 17 00:00:00 2001 From: Lucas Bertrand Date: Fri, 8 Nov 2024 11:13:26 +0100 Subject: [PATCH 06/10] test(`e2e`): add amount args for ERC20 withdraw tests (#3113) * update test * set args * add missing test * fix test --- cmd/zetae2e/local/v2.go | 1 + e2e/e2etests/e2etests.go | 8 ++++++-- e2e/e2etests/test_v2_erc20_withdraw_and_call.go | 9 +++++---- .../test_v2_erc20_withdraw_and_call_no_message.go | 2 +- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/cmd/zetae2e/local/v2.go b/cmd/zetae2e/local/v2.go index f4dac2a534..770d7201ef 100644 --- a/cmd/zetae2e/local/v2.go +++ b/cmd/zetae2e/local/v2.go @@ -39,6 +39,7 @@ func startV2Tests(eg *errgroup.Group, conf config.Config, deployerRunner *runner e2etests.TestV2ERC20WithdrawAndArbitraryCallName, e2etests.TestV2ERC20WithdrawAndCallName, e2etests.TestV2ERC20DepositAndCallNoMessageName, + e2etests.TestV2ERC20WithdrawAndCallNoMessageName, )) // Test revert cases for gas token workflow diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index c1c5d8b533..842abecbe4 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -964,13 +964,17 @@ var AllE2ETests = []runner.E2ETest{ runner.NewE2ETest( TestV2ERC20WithdrawAndCallName, "withdraw ERC20 from ZEVM and authenticated call a contract using V2 contract", - []runner.ArgDefinition{}, + []runner.ArgDefinition{ + {Description: "amount", DefaultValue: "1000"}, + }, TestV2ERC20WithdrawAndCall, ), runner.NewE2ETest( TestV2ERC20WithdrawAndCallNoMessageName, "withdraw ERC20 from ZEVM and authenticated call a contract using V2 contract with no message", - []runner.ArgDefinition{}, + []runner.ArgDefinition{ + {Description: "amount", DefaultValue: "1000"}, + }, TestV2ERC20WithdrawAndCallNoMessage, ), runner.NewE2ETest( diff --git a/e2e/e2etests/test_v2_erc20_withdraw_and_call.go b/e2e/e2etests/test_v2_erc20_withdraw_and_call.go index fbc9e2ae7a..8d8f916576 100644 --- a/e2e/e2etests/test_v2_erc20_withdraw_and_call.go +++ b/e2e/e2etests/test_v2_erc20_withdraw_and_call.go @@ -12,16 +12,17 @@ import ( crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" ) -func TestV2ERC20WithdrawAndCall(r *runner.E2ERunner, _ []string) { +func TestV2ERC20WithdrawAndCall(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + previousGasLimit := r.ZEVMAuth.GasLimit r.ZEVMAuth.GasLimit = 10000000 defer func() { r.ZEVMAuth.GasLimit = previousGasLimit }() - // called with fixed amount without arg since onCall implementation is for TestDappV2 is simple and generic - // without decoding the payload and amount handling for erc20, purpose of test is to verify correct sender and payload are used - amount := big.NewInt(10000) + amount, ok := big.NewInt(0).SetString(args[0], 10) + require.True(r, ok, "Invalid amount specified for TestV2ERC20WithdrawAndCall") payload := randomPayload(r) diff --git a/e2e/e2etests/test_v2_erc20_withdraw_and_call_no_message.go b/e2e/e2etests/test_v2_erc20_withdraw_and_call_no_message.go index 50396ee58c..09d884f94a 100644 --- a/e2e/e2etests/test_v2_erc20_withdraw_and_call_no_message.go +++ b/e2e/e2etests/test_v2_erc20_withdraw_and_call_no_message.go @@ -43,7 +43,7 @@ func TestV2ERC20WithdrawAndCallNoMessage(r *runner.E2ERunner, args []string) { // check called messageIndex, err := r.TestDAppV2EVM.GetNoMessageIndex(&bind.CallOpts{}, r.EVMAddress()) require.NoError(r, err) - r.AssertTestDAppEVMCalled(true, messageIndex, amount) + r.AssertTestDAppEVMCalled(true, messageIndex, big.NewInt(0)) // check expected sender was used senderForMsg, err := r.TestDAppV2EVM.SenderWithMessage( From 1f2c1e16c1c2b89d5292154b6d603820430916db Mon Sep 17 00:00:00 2001 From: Dmitry S <11892559+swift1337@users.noreply.github.com> Date: Fri, 8 Nov 2024 14:37:31 +0100 Subject: [PATCH 07/10] refactor(votes)!: drop support for header proofs (#3125) * Revome block header proofs from zetaclient * Remove proofs from NewMsgAddOutboundTracker * Removed block header proofs from cctx cosmos module * Removed block header proof for vote inbound tracker * Update changelog * Update codegen * Address PR comments --- changelog.md | 5 +- docs/openapi/openapi.swagger.yaml | 2 - docs/spec/crosschain/messages.md | 6 - .../crosschain/outbound_tracker.proto | 4 +- proto/zetachain/zetacore/crosschain/tx.proto | 12 +- .../crosschain/outbound_tracker_pb.d.ts | 5 - .../zetachain/zetacore/crosschain/tx_pb.d.ts | 31 - .../client/cli/tx_add_outbound_tracker.go | 11 +- .../keeper/msg_server_add_inbound_tracker.go | 55 +- .../msg_server_add_inbound_tracker_test.go | 245 +------- .../keeper/msg_server_add_outbound_tracker.go | 74 +-- .../msg_server_add_outbound_tracker_test.go | 320 ++-------- .../types/message_add_outbound_tracker.go | 23 +- .../message_add_outbound_tracker_test.go | 34 +- x/crosschain/types/outbound_tracker.pb.go | 81 +-- x/crosschain/types/tx.pb.go | 545 ++++-------------- zetaclient/chains/bitcoin/signer/signer.go | 5 +- .../evm/signer/outbound_tracker_reporter.go | 2 +- zetaclient/chains/interfaces/interfaces.go | 22 +- .../signer/outbound_tracker_reporter.go | 2 +- .../chains/ton/observer/observer_test.go | 5 +- zetaclient/chains/ton/observer/outbound.go | 4 +- zetaclient/chains/ton/signer/signer_test.go | 5 +- .../chains/ton/signer/signer_tracker.go | 2 +- zetaclient/testutils/mocks/zetacore_client.go | 102 +--- zetaclient/zetacore/broadcast_test.go | 22 +- zetaclient/zetacore/client_vote.go | 29 - zetaclient/zetacore/tx.go | 16 +- zetaclient/zetacore/tx_test.go | 53 +- 29 files changed, 269 insertions(+), 1453 deletions(-) diff --git a/changelog.md b/changelog.md index 846a76f2e7..074a2877aa 100644 --- a/changelog.md +++ b/changelog.md @@ -6,14 +6,13 @@ * [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. -### Refactor -* [3122](https://github.com/zeta-chain/node/pull/3122) - improve & refactor zetaclientd cli - ### Tests * [3075](https://github.com/zeta-chain/node/pull/3075) - ton: withdraw concurrent, deposit & revert. ### Refactor * [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 ### 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/docs/openapi/openapi.swagger.yaml b/docs/openapi/openapi.swagger.yaml index 9dc72eabb9..110426210b 100644 --- a/docs/openapi/openapi.swagger.yaml +++ b/docs/openapi/openapi.swagger.yaml @@ -57603,8 +57603,6 @@ definitions: type: string tx_signer: type: string - proved: - type: boolean cryptoPubKeySet: type: object properties: diff --git a/docs/spec/crosschain/messages.md b/docs/spec/crosschain/messages.md index 3182bec082..1a2a8629e5 100644 --- a/docs/spec/crosschain/messages.md +++ b/docs/spec/crosschain/messages.md @@ -12,9 +12,6 @@ message MsgAddOutboundTracker { int64 chain_id = 2; uint64 nonce = 3; string tx_hash = 4; - pkg.proofs.Proof proof = 5; - string block_hash = 6; - int64 tx_index = 7; } ``` @@ -28,9 +25,6 @@ message MsgAddInboundTracker { int64 chain_id = 2; string tx_hash = 3; pkg.coin.CoinType coin_type = 4; - pkg.proofs.Proof proof = 5; - string block_hash = 6; - int64 tx_index = 7; } ``` diff --git a/proto/zetachain/zetacore/crosschain/outbound_tracker.proto b/proto/zetachain/zetacore/crosschain/outbound_tracker.proto index 4f5b6108b8..bca9a89ca8 100644 --- a/proto/zetachain/zetacore/crosschain/outbound_tracker.proto +++ b/proto/zetachain/zetacore/crosschain/outbound_tracker.proto @@ -6,7 +6,9 @@ option go_package = "github.com/zeta-chain/node/x/crosschain/types"; message TxHash { string tx_hash = 1; string tx_signer = 2; - bool proved = 3; + + // used to be `bool proven` (for block header verification) + reserved 3; } message OutboundTracker { string index = 1; // format: "chain-nonce" diff --git a/proto/zetachain/zetacore/crosschain/tx.proto b/proto/zetachain/zetacore/crosschain/tx.proto index ed8b6b6f59..283f42a3c1 100644 --- a/proto/zetachain/zetacore/crosschain/tx.proto +++ b/proto/zetachain/zetacore/crosschain/tx.proto @@ -65,9 +65,9 @@ message MsgAddInboundTracker { int64 chain_id = 2; string tx_hash = 3; pkg.coin.CoinType coin_type = 4; - pkg.proofs.Proof proof = 5; - string block_hash = 6; - int64 tx_index = 7; + + // used to be block header profs properties + reserved 5, 6, 7; } message MsgAddInboundTrackerResponse {} @@ -92,9 +92,9 @@ message MsgAddOutboundTracker { int64 chain_id = 2; uint64 nonce = 3; string tx_hash = 4; - pkg.proofs.Proof proof = 5; - string block_hash = 6; - int64 tx_index = 7; + + // used to be block header profs properties + reserved 5, 6, 7; } message MsgAddOutboundTrackerResponse { diff --git a/typescript/zetachain/zetacore/crosschain/outbound_tracker_pb.d.ts b/typescript/zetachain/zetacore/crosschain/outbound_tracker_pb.d.ts index ddf1798fc8..cf91859db6 100644 --- a/typescript/zetachain/zetacore/crosschain/outbound_tracker_pb.d.ts +++ b/typescript/zetachain/zetacore/crosschain/outbound_tracker_pb.d.ts @@ -20,11 +20,6 @@ export declare class TxHash extends Message { */ txSigner: string; - /** - * @generated from field: bool proved = 3; - */ - proved: boolean; - constructor(data?: PartialMessage); static readonly runtime: typeof proto3; diff --git a/typescript/zetachain/zetacore/crosschain/tx_pb.d.ts b/typescript/zetachain/zetacore/crosschain/tx_pb.d.ts index db3e7073ea..ecef50f7a9 100644 --- a/typescript/zetachain/zetacore/crosschain/tx_pb.d.ts +++ b/typescript/zetachain/zetacore/crosschain/tx_pb.d.ts @@ -6,7 +6,6 @@ import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; import { Message, proto3 } from "@bufbuild/protobuf"; import type { CoinType } from "../pkg/coin/coin_pb.js"; -import type { Proof } from "../pkg/proofs/proofs_pb.js"; import type { ReceiveStatus } from "../pkg/chains/chains_pb.js"; import type { CallOptions, ProtocolContractVersion, RevertOptions } from "./cross_chain_tx_pb.js"; import type { RateLimiterFlags } from "./rate_limiter_flags_pb.js"; @@ -136,21 +135,6 @@ export declare class MsgAddInboundTracker extends Message */ coinType: CoinType; - /** - * @generated from field: zetachain.zetacore.pkg.proofs.Proof proof = 5; - */ - proof?: Proof; - - /** - * @generated from field: string block_hash = 6; - */ - blockHash: string; - - /** - * @generated from field: int64 tx_index = 7; - */ - txIndex: bigint; - constructor(data?: PartialMessage); static readonly runtime: typeof proto3; @@ -294,21 +278,6 @@ export declare class MsgAddOutboundTracker extends Message); static readonly runtime: typeof proto3; diff --git a/x/crosschain/client/cli/tx_add_outbound_tracker.go b/x/crosschain/client/cli/tx_add_outbound_tracker.go index a7a07ece29..83ecc824e0 100644 --- a/x/crosschain/client/cli/tx_add_outbound_tracker.go +++ b/x/crosschain/client/cli/tx_add_outbound_tracker.go @@ -32,15 +32,8 @@ func CmdAddOutboundTracker() *cobra.Command { return err } - msg := types.NewMsgAddOutboundTracker( - clientCtx.GetFromAddress().String(), - argChain, - argNonce, - argTxHash, - nil, // TODO: add option to provide a proof from CLI arguments https://github.com/zeta-chain/node/issues/1134 - "", - -1, - ) + creator := clientCtx.GetFromAddress().String() + msg := types.NewMsgAddOutboundTracker(creator, argChain, argNonce, argTxHash) return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, diff --git a/x/crosschain/keeper/msg_server_add_inbound_tracker.go b/x/crosschain/keeper/msg_server_add_inbound_tracker.go index bec2eb2abf..499f6edfc9 100644 --- a/x/crosschain/keeper/msg_server_add_inbound_tracker.go +++ b/x/crosschain/keeper/msg_server_add_inbound_tracker.go @@ -2,7 +2,6 @@ package keeper import ( "context" - "fmt" errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" @@ -23,27 +22,14 @@ func (k msgServer) AddInboundTracker( return nil, observertypes.ErrSupportedChains } - // check if the msg signer is from the emergency group policy address.It is okay to ignore the error as the sender can also be an observer - isAuthorizedPolicy := false - err := k.GetAuthorityKeeper().CheckAuthorization(ctx, msg) - if err == nil { - isAuthorizedPolicy = true - } - - // check if the msg signer is an observer - isObserver := k.GetObserverKeeper().IsNonTombstonedObserver(ctx, msg.Creator) + // only emergency group and observer can submit a tracker + var ( + isAuthorizedPolicy = k.GetAuthorityKeeper().CheckAuthorization(ctx, msg) == nil + isObserver = k.GetObserverKeeper().IsNonTombstonedObserver(ctx, msg.Creator) + ) - // only emergency group and observer can submit tracker without proof - // if the sender is not from the emergency group or observer, the inbound proof must be provided if !(isAuthorizedPolicy || isObserver) { - if msg.Proof == nil { - return nil, errorsmod.Wrap(authoritytypes.ErrUnauthorized, fmt.Sprintf("Creator %s", msg.Creator)) - } - - // verify the proof and tx body - if err := verifyProofAndInboundBody(ctx, k, msg); err != nil { - return nil, err - } + return nil, errorsmod.Wrapf(authoritytypes.ErrUnauthorized, "Creator %s", msg.Creator) } // add the inTx tracker @@ -55,32 +41,3 @@ func (k msgServer) AddInboundTracker( return &types.MsgAddInboundTrackerResponse{}, nil } - -// verifyProofAndInboundBody verifies the proof and inbound tx body -func verifyProofAndInboundBody(ctx sdk.Context, k msgServer, msg *types.MsgAddInboundTracker) error { - txBytes, err := k.GetLightclientKeeper().VerifyProof(ctx, msg.Proof, msg.ChainId, msg.BlockHash, msg.TxIndex) - if err != nil { - return types.ErrProofVerificationFail.Wrap(err.Error()) - } - - // get chain params and tss addresses to verify the inTx body - chainParams, found := k.GetObserverKeeper().GetChainParamsByChainID(ctx, msg.ChainId) - if !found || chainParams == nil { - return types.ErrUnsupportedChain.Wrapf("chain params not found for chain %d", msg.ChainId) - } - tss, err := k.GetObserverKeeper().GetTssAddress(ctx, &observertypes.QueryGetTssAddressRequest{ - BitcoinChainId: msg.ChainId, - }) - if err != nil { - return observertypes.ErrTssNotFound.Wrap(err.Error()) - } - if tss == nil { - return observertypes.ErrTssNotFound.Wrapf("tss address nil") - } - - if err := types.VerifyInboundBody(*msg, txBytes, *chainParams, *tss); err != nil { - return types.ErrTxBodyVerificationFail.Wrap(err.Error()) - } - - return nil -} diff --git a/x/crosschain/keeper/msg_server_add_inbound_tracker_test.go b/x/crosschain/keeper/msg_server_add_inbound_tracker_test.go index e20cb21f69..ecaf21d495 100644 --- a/x/crosschain/keeper/msg_server_add_inbound_tracker_test.go +++ b/x/crosschain/keeper/msg_server_add_inbound_tracker_test.go @@ -1,7 +1,6 @@ package keeper_test import ( - "errors" "testing" "github.com/stretchr/testify/mock" @@ -9,7 +8,6 @@ import ( "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/coin" - "github.com/zeta-chain/node/pkg/proofs" keepertest "github.com/zeta-chain/node/testutil/keeper" "github.com/zeta-chain/node/testutil/sample" authoritytypes "github.com/zeta-chain/node/x/authority/types" @@ -19,7 +17,7 @@ import ( ) func TestMsgServer_AddToInboundTracker(t *testing.T) { - t.Run("fail normal user submit without proof", func(t *testing.T) { + t.Run("fail normal user submit", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ UseAuthorityMock: true, UseObserverMock: true, @@ -37,13 +35,10 @@ func TestMsgServer_AddToInboundTracker(t *testing.T) { observerMock.On("IsNonTombstonedObserver", mock.Anything, mock.Anything).Return(false) msg := types.MsgAddInboundTracker{ - Creator: nonAdmin, - ChainId: chainID, - TxHash: txHash, - CoinType: coin.CoinType_Zeta, - Proof: nil, - BlockHash: "", - TxIndex: 0, + Creator: nonAdmin, + ChainId: chainID, + TxHash: txHash, + CoinType: coin.CoinType_Zeta, } keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, authoritytypes.ErrUnauthorized) _, err := msgServer.AddInboundTracker(ctx, &msg) @@ -65,13 +60,10 @@ func TestMsgServer_AddToInboundTracker(t *testing.T) { observerMock.On("GetSupportedChainFromChainID", mock.Anything, mock.Anything).Return(chains.Chain{}, false) msg := types.MsgAddInboundTracker{ - Creator: sample.AccAddress(), - ChainId: chainID + 1, - TxHash: txHash, - CoinType: coin.CoinType_Zeta, - Proof: nil, - BlockHash: "", - TxIndex: 0, + Creator: sample.AccAddress(), + ChainId: chainID + 1, + TxHash: txHash, + CoinType: coin.CoinType_Zeta, } _, err := msgServer.AddInboundTracker(ctx, &msg) require.ErrorIs(t, err, observertypes.ErrSupportedChains) @@ -98,13 +90,10 @@ func TestMsgServer_AddToInboundTracker(t *testing.T) { setSupportedChain(ctx, zk, chainID) msg := types.MsgAddInboundTracker{ - Creator: admin, - ChainId: chainID, - TxHash: txHash, - CoinType: coin.CoinType_Zeta, - Proof: nil, - BlockHash: "", - TxIndex: 0, + Creator: admin, + ChainId: chainID, + TxHash: txHash, + CoinType: coin.CoinType_Zeta, } keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, nil) _, err := msgServer.AddInboundTracker(ctx, &msg) @@ -131,210 +120,10 @@ func TestMsgServer_AddToInboundTracker(t *testing.T) { observerMock.On("IsNonTombstonedObserver", mock.Anything, mock.Anything).Return(true) msg := types.MsgAddInboundTracker{ - Creator: admin, - ChainId: chainID, - TxHash: txHash, - CoinType: coin.CoinType_Zeta, - Proof: nil, - BlockHash: "", - TxIndex: 0, - } - keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, authoritytypes.ErrUnauthorized) - _, err := msgServer.AddInboundTracker(ctx, &msg) - require.NoError(t, err) - _, found := k.GetInboundTracker(ctx, chainID, txHash) - require.True(t, found) - }) - - t.Run("fail if proof is provided but not verified", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ - UseAuthorityMock: true, - UseLightclientMock: true, - UseObserverMock: true, - }) - msgServer := keeper.NewMsgServerImpl(*k) - authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) - observerMock := keepertest.GetCrosschainObserverMock(t, k) - lightclientMock := keepertest.GetCrosschainLightclientMock(t, k) - - admin := sample.AccAddress() - txHash := "string" - chainID := getValidEthChainID() - - observerMock.On("GetSupportedChainFromChainID", mock.Anything, mock.Anything).Return(chains.Chain{}, true) - observerMock.On("IsNonTombstonedObserver", mock.Anything, mock.Anything).Return(false) - lightclientMock.On("VerifyProof", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). - Return(nil, errors.New("error")) - - msg := types.MsgAddInboundTracker{ - Creator: admin, - ChainId: chainID, - TxHash: txHash, - CoinType: coin.CoinType_Zeta, - Proof: &proofs.Proof{}, - BlockHash: "", - TxIndex: 0, - } - keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, authoritytypes.ErrUnauthorized) - _, err := msgServer.AddInboundTracker(ctx, &msg) - require.ErrorIs(t, err, types.ErrProofVerificationFail) - }) - - t.Run("fail if proof is provided but can't find chain params to verify body", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ - UseAuthorityMock: true, - UseLightclientMock: true, - UseObserverMock: true, - }) - msgServer := keeper.NewMsgServerImpl(*k) - - admin := sample.AccAddress() - authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) - observerMock := keepertest.GetCrosschainObserverMock(t, k) - lightclientMock := keepertest.GetCrosschainLightclientMock(t, k) - txHash := "string" - chainID := getValidEthChainID() - - observerMock.On("GetSupportedChainFromChainID", mock.Anything, mock.Anything).Return(chains.Chain{}, true) - observerMock.On("IsNonTombstonedObserver", mock.Anything, mock.Anything).Return(false) - lightclientMock.On("VerifyProof", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). - Return(sample.Bytes(), nil) - observerMock.On("GetChainParamsByChainID", mock.Anything, mock.Anything).Return(nil, false) - - msg := types.MsgAddInboundTracker{ - Creator: admin, - ChainId: chainID, - TxHash: txHash, - CoinType: coin.CoinType_Zeta, - Proof: &proofs.Proof{}, - BlockHash: "", - TxIndex: 0, - } - keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, authoritytypes.ErrUnauthorized) - _, err := msgServer.AddInboundTracker(ctx, &msg) - require.ErrorIs(t, err, types.ErrUnsupportedChain) - }) - - t.Run("fail if proof is provided but can't find tss to verify body", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ - UseAuthorityMock: true, - UseLightclientMock: true, - UseObserverMock: true, - }) - msgServer := keeper.NewMsgServerImpl(*k) - - authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) - observerMock := keepertest.GetCrosschainObserverMock(t, k) - lightclientMock := keepertest.GetCrosschainLightclientMock(t, k) - - admin := sample.AccAddress() - txHash := "string" - chainID := getValidEthChainID() - - observerMock.On("GetSupportedChainFromChainID", mock.Anything, mock.Anything).Return(chains.Chain{}, true) - observerMock.On("IsNonTombstonedObserver", mock.Anything, mock.Anything).Return(false) - lightclientMock.On("VerifyProof", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). - Return(sample.Bytes(), nil) - observerMock.On("GetChainParamsByChainID", mock.Anything, mock.Anything). - Return(sample.ChainParams(chains.Ethereum.ChainId), true) - observerMock.On("GetTssAddress", mock.Anything, mock.Anything).Return(nil, errors.New("error")) - - setSupportedChain(ctx, zk, chainID) - - msg := types.MsgAddInboundTracker{ - Creator: admin, - ChainId: chainID, - TxHash: txHash, - CoinType: coin.CoinType_Zeta, - Proof: &proofs.Proof{}, - BlockHash: "", - TxIndex: 0, - } - keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, authoritytypes.ErrUnauthorized) - _, err := msgServer.AddInboundTracker(ctx, &msg) - require.ErrorIs(t, err, observertypes.ErrTssNotFound) - }) - - t.Run("fail if proof is provided but error while verifying tx body", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ - UseAuthorityMock: true, - UseLightclientMock: true, - UseObserverMock: true, - }) - msgServer := keeper.NewMsgServerImpl(*k) - - authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) - observerMock := keepertest.GetCrosschainObserverMock(t, k) - lightclientMock := keepertest.GetCrosschainLightclientMock(t, k) - - admin := sample.AccAddress() - txHash := "string" - chainID := getValidEthChainID() - - observerMock.On("GetSupportedChainFromChainID", mock.Anything, mock.Anything).Return(chains.Chain{}, true) - observerMock.On("IsNonTombstonedObserver", mock.Anything, mock.Anything).Return(false) - observerMock.On("GetChainParamsByChainID", mock.Anything, mock.Anything). - Return(sample.ChainParams(chains.Ethereum.ChainId), true) - observerMock.On("GetTssAddress", mock.Anything, mock.Anything).Return(&observertypes.QueryGetTssAddressResponse{ - Eth: sample.EthAddress().Hex(), - }, nil) - - // verifying the body will fail because the bytes are tried to be unmarshaled but they are not valid - lightclientMock.On("VerifyProof", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). - Return([]byte("invalid"), nil) - - setSupportedChain(ctx, zk, chainID) - - msg := types.MsgAddInboundTracker{ - Creator: admin, - ChainId: chainID, - TxHash: txHash, - CoinType: coin.CoinType_Zeta, - Proof: &proofs.Proof{}, - BlockHash: "", - TxIndex: 0, - } - keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, authoritytypes.ErrUnauthorized) - _, err := msgServer.AddInboundTracker(ctx, &msg) - require.ErrorIs(t, err, types.ErrTxBodyVerificationFail) - }) - - t.Run("can add a in tx tracker with a proof", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ - UseAuthorityMock: true, - UseLightclientMock: true, - UseObserverMock: true, - }) - msgServer := keeper.NewMsgServerImpl(*k) - - chainID := chains.Ethereum.ChainId - tssAddress := sample.EthAddress() - ethTx, ethTxBytes := sample.EthTx(t, chainID, tssAddress, 42) - admin := sample.AccAddress() - txHash := ethTx.Hash().Hex() - - authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) - observerMock := keepertest.GetCrosschainObserverMock(t, k) - lightclientMock := keepertest.GetCrosschainLightclientMock(t, k) - - observerMock.On("GetSupportedChainFromChainID", mock.Anything, mock.Anything).Return(chains.Chain{}, true) - observerMock.On("IsNonTombstonedObserver", mock.Anything, mock.Anything).Return(false) - observerMock.On("GetChainParamsByChainID", mock.Anything, mock.Anything). - Return(sample.ChainParams(chains.Ethereum.ChainId), true) - observerMock.On("GetTssAddress", mock.Anything, mock.Anything).Return(&observertypes.QueryGetTssAddressResponse{ - Eth: tssAddress.Hex(), - }, nil) - lightclientMock.On("VerifyProof", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). - Return(ethTxBytes, nil) - - msg := types.MsgAddInboundTracker{ - Creator: admin, - ChainId: chainID, - TxHash: txHash, - CoinType: coin.CoinType_Gas, // use coin types gas: the receiver must be the tss address - Proof: &proofs.Proof{}, - BlockHash: "", - TxIndex: 0, + Creator: admin, + ChainId: chainID, + TxHash: txHash, + CoinType: coin.CoinType_Zeta, } keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, authoritytypes.ErrUnauthorized) _, err := msgServer.AddInboundTracker(ctx, &msg) diff --git a/x/crosschain/keeper/msg_server_add_outbound_tracker.go b/x/crosschain/keeper/msg_server_add_outbound_tracker.go index 39002d3155..9beffffd1e 100644 --- a/x/crosschain/keeper/msg_server_add_outbound_tracker.go +++ b/x/crosschain/keeper/msg_server_add_outbound_tracker.go @@ -2,13 +2,11 @@ package keeper import ( "context" - "fmt" "strings" cosmoserrors "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/zeta-chain/node/pkg/chains" authoritytypes "github.com/zeta-chain/node/x/authority/types" "github.com/zeta-chain/node/x/crosschain/types" observertypes "github.com/zeta-chain/node/x/observer/types" @@ -56,38 +54,21 @@ func (k msgServer) AddOutboundTracker( return &types.MsgAddOutboundTrackerResponse{IsRemoved: true}, nil } - // check if the msg signer is from the emergency group policy address.It is okay to ignore the error as the sender can also be an observer - isAuthorizedPolicy := false - if k.GetAuthorityKeeper().CheckAuthorization(ctx, msg) == nil { - isAuthorizedPolicy = true - } - - // check if the msg signer is an observer - isObserver := k.GetObserverKeeper().IsNonTombstonedObserver(ctx, msg.Creator) - isProven := false + // check if the msg signer is from the emergency group policy address. + // or an observer + var ( + isAuthorizedPolicy = k.GetAuthorityKeeper().CheckAuthorization(ctx, msg) == nil + isObserver = k.GetObserverKeeper().IsNonTombstonedObserver(ctx, msg.Creator) + ) - // only emergency group and observer can submit tracker without proof - // if the sender is not from the emergency group or observer, the outbound proof must be provided if !(isAuthorizedPolicy || isObserver) { - if msg.Proof == nil { - return nil, cosmoserrors.Wrap(authoritytypes.ErrUnauthorized, fmt.Sprintf("Creator %s", msg.Creator)) - } - // verify proof when it is provided - if err := verifyProofAndOutboundBody(ctx, k, msg); err != nil { - return nil, err - } - - isProven = true + return nil, cosmoserrors.Wrapf(authoritytypes.ErrUnauthorized, "Creator %s", msg.Creator) } // fetch the tracker // if the tracker does not exist, initialize a new one tracker, found := k.GetOutboundTracker(ctx, msg.ChainId, msg.Nonce) - hash := types.TxHash{ - TxHash: msg.TxHash, - TxSigner: msg.Creator, - Proved: isProven, - } + hash := types.TxHash{TxHash: msg.TxHash, TxSigner: msg.Creator} if !found { k.SetOutboundTracker(ctx, types.OutboundTracker{ Index: "", @@ -99,14 +80,8 @@ func (k msgServer) AddOutboundTracker( } // check if the hash is already in the tracker - for i, hash := range tracker.HashList { - hash := hash + for _, hash := range tracker.HashList { if strings.EqualFold(hash.TxHash, msg.TxHash) { - // if the hash is already in the tracker but we have a proof, mark it as proven and only keep this one in the list - if isProven { - tracker.HashList[i].Proved = true - k.SetOutboundTracker(ctx, tracker) - } return &types.MsgAddOutboundTrackerResponse{}, nil } } @@ -126,34 +101,3 @@ func (k msgServer) AddOutboundTracker( k.SetOutboundTracker(ctx, tracker) return &types.MsgAddOutboundTrackerResponse{}, nil } - -// verifyProofAndOutboundBody verifies the proof and outbound tx body -// Precondition: the proof must be non-nil -func verifyProofAndOutboundBody(ctx sdk.Context, k msgServer, msg *types.MsgAddOutboundTracker) error { - txBytes, err := k.lightclientKeeper.VerifyProof(ctx, msg.Proof, msg.ChainId, msg.BlockHash, msg.TxIndex) - if err != nil { - return types.ErrProofVerificationFail.Wrap(err.Error()) - } - - // get tss address - var bitcoinChainID int64 - if chains.IsBitcoinChain(msg.ChainId, k.GetAuthorityKeeper().GetAdditionalChainList(ctx)) { - bitcoinChainID = msg.ChainId - } - - tss, err := k.GetObserverKeeper().GetTssAddress(ctx, &observertypes.QueryGetTssAddressRequest{ - BitcoinChainId: bitcoinChainID, - }) - if err != nil { - return observertypes.ErrTssNotFound.Wrap(err.Error()) - } - if tss == nil { - return observertypes.ErrTssNotFound.Wrapf("tss address nil") - } - - if err := types.VerifyOutboundBody(*msg, txBytes, *tss); err != nil { - return types.ErrTxBodyVerificationFail.Wrap(err.Error()) - } - - return nil -} diff --git a/x/crosschain/keeper/msg_server_add_outbound_tracker_test.go b/x/crosschain/keeper/msg_server_add_outbound_tracker_test.go index d243a3a7e6..fb477c9920 100644 --- a/x/crosschain/keeper/msg_server_add_outbound_tracker_test.go +++ b/x/crosschain/keeper/msg_server_add_outbound_tracker_test.go @@ -1,14 +1,12 @@ package keeper_test import ( - "errors" "testing" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/zeta-chain/node/pkg/chains" - "github.com/zeta-chain/node/pkg/proofs" keepertest "github.com/zeta-chain/node/testutil/keeper" "github.com/zeta-chain/node/testutil/sample" authoritytypes "github.com/zeta-chain/node/x/authority/types" @@ -21,9 +19,6 @@ func getEthereumChainID() int64 { return 5 // Goerli } -// TODO: Add a test case with proof and Bitcoin chain -// https://github.com/zeta-chain/node/issues/1994 - func TestMsgServer_AddToOutboundTracker(t *testing.T) { t.Run("admin can add tracker", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ @@ -44,13 +39,10 @@ func TestMsgServer_AddToOutboundTracker(t *testing.T) { keepertest.MockCctxByNonce(t, ctx, *k, observerMock, types.CctxStatus_PendingOutbound, false) msg := types.MsgAddOutboundTracker{ - Creator: admin, - ChainId: chainID, - TxHash: hash, - Proof: nil, - BlockHash: "", - TxIndex: 0, - Nonce: 0, + Creator: admin, + ChainId: chainID, + TxHash: hash, + Nonce: 0, } keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, nil) _, err := msgServer.AddOutboundTracker(ctx, &msg) @@ -79,13 +71,10 @@ func TestMsgServer_AddToOutboundTracker(t *testing.T) { keepertest.MockCctxByNonce(t, ctx, *k, observerMock, types.CctxStatus_PendingOutbound, false) msg := types.MsgAddOutboundTracker{ - Creator: admin, - ChainId: chainID, - TxHash: hash, - Proof: nil, - BlockHash: "", - TxIndex: 0, - Nonce: 0, + Creator: admin, + ChainId: chainID, + TxHash: hash, + Nonce: 0, } keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, authoritytypes.ErrUnauthorized) _, err := msgServer.AddOutboundTracker(ctx, &msg) @@ -125,13 +114,10 @@ func TestMsgServer_AddToOutboundTracker(t *testing.T) { }) msg := types.MsgAddOutboundTracker{ - Creator: admin, - ChainId: chainID, - TxHash: newHash, - Proof: nil, - BlockHash: "", - TxIndex: 0, - Nonce: 42, + Creator: admin, + ChainId: chainID, + TxHash: newHash, + Nonce: 42, } keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, nil) _, err := msgServer.AddOutboundTracker(ctx, &msg) @@ -153,13 +139,10 @@ func TestMsgServer_AddToOutboundTracker(t *testing.T) { admin := sample.AccAddress() chainID := getEthereumChainID() msg := types.MsgAddOutboundTracker{ - Creator: admin, - ChainId: chainID, - TxHash: sample.Hash().Hex(), - Proof: nil, - BlockHash: "", - TxIndex: 0, - Nonce: 0, + Creator: admin, + ChainId: chainID, + TxHash: sample.Hash().Hex(), + Nonce: 0, } observerMock := keepertest.GetCrosschainObserverMock(t, k) @@ -192,13 +175,10 @@ func TestMsgServer_AddToOutboundTracker(t *testing.T) { chainID := getEthereumChainID() _, err := msgServer.AddOutboundTracker(ctx, &types.MsgAddOutboundTracker{ - Creator: admin, - ChainId: chainID, - TxHash: sample.Hash().Hex(), - Proof: nil, - BlockHash: "", - TxIndex: 0, - Nonce: 0, + Creator: admin, + ChainId: chainID, + TxHash: sample.Hash().Hex(), + Nonce: 0, }) require.ErrorIs(t, err, observertypes.ErrSupportedChains) }) @@ -220,13 +200,10 @@ func TestMsgServer_AddToOutboundTracker(t *testing.T) { chainID := getEthereumChainID() _, err := msgServer.AddOutboundTracker(ctx, &types.MsgAddOutboundTracker{ - Creator: admin, - ChainId: chainID, - TxHash: sample.Hash().Hex(), - Proof: nil, - BlockHash: "", - TxIndex: 0, - Nonce: 0, + Creator: admin, + ChainId: chainID, + TxHash: sample.Hash().Hex(), + Nonce: 0, }) require.ErrorIs(t, err, types.ErrCannotFindCctx) }) @@ -263,13 +240,11 @@ func TestMsgServer_AddToOutboundTracker(t *testing.T) { }) msg := types.MsgAddOutboundTracker{ - Creator: admin, - ChainId: chainID, - TxHash: newHash, - Proof: nil, - BlockHash: "", - TxIndex: 0, - Nonce: 42, + Creator: admin, + ChainId: chainID, + TxHash: newHash, + + Nonce: 42, } keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, nil) _, err := msgServer.AddOutboundTracker(ctx, &msg) @@ -306,13 +281,10 @@ func TestMsgServer_AddToOutboundTracker(t *testing.T) { }) msg := types.MsgAddOutboundTracker{ - Creator: admin, - ChainId: chainID, - TxHash: existinghHash, - Proof: nil, - BlockHash: "", - TxIndex: 0, - Nonce: 42, + Creator: admin, + ChainId: chainID, + TxHash: existinghHash, + Nonce: 42, } keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, nil) _, err := msgServer.AddOutboundTracker(ctx, &msg) @@ -322,230 +294,4 @@ func TestMsgServer_AddToOutboundTracker(t *testing.T) { require.Len(t, tracker.HashList, 1) require.EqualValues(t, existinghHash, tracker.HashList[0].TxHash) }) - - t.Run("can add tracker with proof", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ - UseAuthorityMock: true, - UseObserverMock: true, - UseLightclientMock: true, - }) - msgServer := keeper.NewMsgServerImpl(*k) - - admin := sample.AccAddress() - chainID := getEthereumChainID() - ethTx, ethTxBytes, tssAddress := sample.EthTxSigned(t, chainID, sample.EthAddress(), 42) - txHash := ethTx.Hash().Hex() - - authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) - observerMock := keepertest.GetCrosschainObserverMock(t, k) - lightclientMock := keepertest.GetCrosschainLightclientMock(t, k) - - observerMock.On("GetSupportedChainFromChainID", mock.Anything, mock.Anything).Return(chains.Chain{}, true) - observerMock.On("IsNonTombstonedObserver", mock.Anything, mock.Anything).Return(false) - keepertest.MockCctxByNonce(t, ctx, *k, observerMock, types.CctxStatus_PendingOutbound, false) - observerMock.On("GetTssAddress", mock.Anything, mock.Anything).Return(&observertypes.QueryGetTssAddressResponse{ - Eth: tssAddress.Hex(), - }, nil) - lightclientMock.On("VerifyProof", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). - Return(ethTxBytes, nil) - - msg := types.MsgAddOutboundTracker{ - Creator: admin, - ChainId: chainID, - TxHash: txHash, - Proof: &proofs.Proof{}, - BlockHash: "", - TxIndex: 0, - Nonce: 42, - } - keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, authoritytypes.ErrUnauthorized) - keepertest.MockGetChainListEmpty(&authorityMock.Mock) - _, err := msgServer.AddOutboundTracker(ctx, &msg) - require.NoError(t, err) - tracker, found := k.GetOutboundTracker(ctx, chainID, 42) - require.True(t, found) - require.EqualValues(t, txHash, tracker.HashList[0].TxHash) - require.True(t, tracker.HashList[0].Proved) - }) - - t.Run("adding existing hash with proof make it proven", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ - UseAuthorityMock: true, - UseObserverMock: true, - UseLightclientMock: true, - }) - msgServer := keeper.NewMsgServerImpl(*k) - - admin := sample.AccAddress() - chainID := getEthereumChainID() - ethTx, ethTxBytes, tssAddress := sample.EthTxSigned(t, chainID, sample.EthAddress(), 42) - txHash := ethTx.Hash().Hex() - - authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) - observerMock := keepertest.GetCrosschainObserverMock(t, k) - lightclientMock := keepertest.GetCrosschainLightclientMock(t, k) - - observerMock.On("GetSupportedChainFromChainID", mock.Anything, mock.Anything).Return(chains.Chain{}, true) - observerMock.On("IsNonTombstonedObserver", mock.Anything, mock.Anything).Return(false) - keepertest.MockCctxByNonce(t, ctx, *k, observerMock, types.CctxStatus_PendingOutbound, false) - observerMock.On("GetTssAddress", mock.Anything, mock.Anything).Return(&observertypes.QueryGetTssAddressResponse{ - Eth: tssAddress.Hex(), - }, nil) - lightclientMock.On("VerifyProof", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). - Return(ethTxBytes, nil) - - k.SetOutboundTracker(ctx, types.OutboundTracker{ - ChainId: chainID, - Nonce: 42, - HashList: []*types.TxHash{ - { - TxHash: sample.Hash().Hex(), - Proved: false, - }, - { - TxHash: txHash, - Proved: false, - }, - }, - }) - - msg := types.MsgAddOutboundTracker{ - Creator: admin, - ChainId: chainID, - TxHash: txHash, - Proof: &proofs.Proof{}, - BlockHash: "", - TxIndex: 0, - Nonce: 42, - } - keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, authoritytypes.ErrUnauthorized) - keepertest.MockGetChainListEmpty(&authorityMock.Mock) - _, err := msgServer.AddOutboundTracker(ctx, &msg) - require.NoError(t, err) - tracker, found := k.GetOutboundTracker(ctx, chainID, 42) - require.True(t, found) - require.Len(t, tracker.HashList, 2) - require.EqualValues(t, txHash, tracker.HashList[1].TxHash) - require.True(t, tracker.HashList[1].Proved) - }) - - t.Run("should fail if verify proof fail", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ - UseAuthorityMock: true, - UseObserverMock: true, - UseLightclientMock: true, - }) - msgServer := keeper.NewMsgServerImpl(*k) - - admin := sample.AccAddress() - chainID := getEthereumChainID() - ethTx, ethTxBytes, _ := sample.EthTxSigned(t, chainID, sample.EthAddress(), 42) - txHash := ethTx.Hash().Hex() - - authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) - observerMock := keepertest.GetCrosschainObserverMock(t, k) - lightclientMock := keepertest.GetCrosschainLightclientMock(t, k) - - observerMock.On("GetSupportedChainFromChainID", mock.Anything, mock.Anything).Return(chains.Chain{}, true) - observerMock.On("IsNonTombstonedObserver", mock.Anything, mock.Anything).Return(false) - keepertest.MockCctxByNonce(t, ctx, *k, observerMock, types.CctxStatus_PendingOutbound, false) - lightclientMock.On("VerifyProof", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). - Return(ethTxBytes, errors.New("error")) - - msg := types.MsgAddOutboundTracker{ - Creator: admin, - ChainId: chainID, - TxHash: txHash, - Proof: &proofs.Proof{}, - BlockHash: "", - TxIndex: 0, - Nonce: 42, - } - keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, authoritytypes.ErrUnauthorized) - _, err := msgServer.AddOutboundTracker(ctx, &msg) - require.ErrorIs(t, err, types.ErrProofVerificationFail) - }) - - t.Run("should fail if no tss when adding hash with proof", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ - UseAuthorityMock: true, - UseObserverMock: true, - UseLightclientMock: true, - }) - msgServer := keeper.NewMsgServerImpl(*k) - - admin := sample.AccAddress() - chainID := getEthereumChainID() - ethTx, ethTxBytes, tssAddress := sample.EthTxSigned(t, chainID, sample.EthAddress(), 42) - txHash := ethTx.Hash().Hex() - - authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) - observerMock := keepertest.GetCrosschainObserverMock(t, k) - lightclientMock := keepertest.GetCrosschainLightclientMock(t, k) - - observerMock.On("GetSupportedChainFromChainID", mock.Anything, mock.Anything).Return(chains.Chain{}, true) - observerMock.On("IsNonTombstonedObserver", mock.Anything, mock.Anything).Return(false) - keepertest.MockCctxByNonce(t, ctx, *k, observerMock, types.CctxStatus_PendingOutbound, false) - lightclientMock.On("VerifyProof", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). - Return(ethTxBytes, nil) - observerMock.On("GetTssAddress", mock.Anything, mock.Anything).Return(&observertypes.QueryGetTssAddressResponse{ - Eth: tssAddress.Hex(), - }, errors.New("error")) - - msg := types.MsgAddOutboundTracker{ - Creator: admin, - ChainId: chainID, - TxHash: txHash, - Proof: &proofs.Proof{}, - BlockHash: "", - TxIndex: 0, - Nonce: 42, - } - keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, authoritytypes.ErrUnauthorized) - keepertest.MockGetChainListEmpty(&authorityMock.Mock) - _, err := msgServer.AddOutboundTracker(ctx, &msg) - require.ErrorIs(t, err, observertypes.ErrTssNotFound) - }) - - t.Run("should fail if body verification fail with proof", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ - UseAuthorityMock: true, - UseObserverMock: true, - UseLightclientMock: true, - }) - msgServer := keeper.NewMsgServerImpl(*k) - - admin := sample.AccAddress() - chainID := getEthereumChainID() - ethTx, _, tssAddress := sample.EthTxSigned(t, chainID, sample.EthAddress(), 42) - txHash := ethTx.Hash().Hex() - authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) - observerMock := keepertest.GetCrosschainObserverMock(t, k) - lightclientMock := keepertest.GetCrosschainLightclientMock(t, k) - - observerMock.On("GetSupportedChainFromChainID", mock.Anything, mock.Anything).Return(chains.Chain{}, true) - observerMock.On("IsNonTombstonedObserver", mock.Anything, mock.Anything).Return(false) - keepertest.MockCctxByNonce(t, ctx, *k, observerMock, types.CctxStatus_PendingOutbound, false) - observerMock.On("GetTssAddress", mock.Anything, mock.Anything).Return(&observertypes.QueryGetTssAddressResponse{ - Eth: tssAddress.Hex(), - }, nil) - - // makes VerifyProof returning an invalid hash - lightclientMock.On("VerifyProof", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). - Return(sample.Bytes(), nil) - - msg := types.MsgAddOutboundTracker{ - Creator: admin, - ChainId: chainID, - TxHash: txHash, - Proof: &proofs.Proof{}, - BlockHash: "", - TxIndex: 0, - Nonce: 42, - } - keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, authoritytypes.ErrUnauthorized) - keepertest.MockGetChainListEmpty(&authorityMock.Mock) - _, err := msgServer.AddOutboundTracker(ctx, &msg) - require.ErrorIs(t, err, types.ErrTxBodyVerificationFail) - }) } diff --git a/x/crosschain/types/message_add_outbound_tracker.go b/x/crosschain/types/message_add_outbound_tracker.go index dddb19900a..d81ccf98bb 100644 --- a/x/crosschain/types/message_add_outbound_tracker.go +++ b/x/crosschain/types/message_add_outbound_tracker.go @@ -4,31 +4,18 @@ import ( cosmoserrors "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - "github.com/zeta-chain/node/pkg/proofs" ) const TypeMsgAddOutboundTracker = "AddOutboundTracker" var _ sdk.Msg = &MsgAddOutboundTracker{} -func NewMsgAddOutboundTracker( - creator string, - chain int64, - nonce uint64, - txHash string, - proof *proofs.Proof, - blockHash string, - txIndex int64, -) *MsgAddOutboundTracker { +func NewMsgAddOutboundTracker(creator string, chain int64, nonce uint64, txHash string) *MsgAddOutboundTracker { return &MsgAddOutboundTracker{ - Creator: creator, - ChainId: chain, - Nonce: nonce, - TxHash: txHash, - Proof: proof, - BlockHash: blockHash, - TxIndex: txIndex, + Creator: creator, + ChainId: chain, + Nonce: nonce, + TxHash: txHash, } } diff --git a/x/crosschain/types/message_add_outbound_tracker_test.go b/x/crosschain/types/message_add_outbound_tracker_test.go index 5eafc7b31c..8f664da6f5 100644 --- a/x/crosschain/types/message_add_outbound_tracker_test.go +++ b/x/crosschain/types/message_add_outbound_tracker_test.go @@ -19,41 +19,17 @@ func TestMsgAddOutboundTracker_ValidateBasic(t *testing.T) { }{ { name: "invalid address", - msg: types.NewMsgAddOutboundTracker( - "invalid", - 1, - 1, - "", - nil, - "", - 1, - ), - err: sdkerrors.ErrInvalidAddress, + msg: types.NewMsgAddOutboundTracker("invalid", 1, 1, ""), + err: sdkerrors.ErrInvalidAddress, }, { name: "invalid chain id", - msg: types.NewMsgAddOutboundTracker( - sample.AccAddress(), - -1, - 1, - "", - nil, - "", - 1, - ), - err: sdkerrors.ErrInvalidChainID, + msg: types.NewMsgAddOutboundTracker(sample.AccAddress(), -1, 1, ""), + err: sdkerrors.ErrInvalidChainID, }, { name: "valid address", - msg: types.NewMsgAddOutboundTracker( - sample.AccAddress(), - 1, - 1, - "", - nil, - "", - 1, - ), + msg: types.NewMsgAddOutboundTracker(sample.AccAddress(), 1, 1, ""), }, } for _, tt := range tests { diff --git a/x/crosschain/types/outbound_tracker.pb.go b/x/crosschain/types/outbound_tracker.pb.go index 5002924b2b..a80ef25247 100644 --- a/x/crosschain/types/outbound_tracker.pb.go +++ b/x/crosschain/types/outbound_tracker.pb.go @@ -25,7 +25,6 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type TxHash struct { TxHash string `protobuf:"bytes,1,opt,name=tx_hash,json=txHash,proto3" json:"tx_hash,omitempty"` TxSigner string `protobuf:"bytes,2,opt,name=tx_signer,json=txSigner,proto3" json:"tx_signer,omitempty"` - Proved bool `protobuf:"varint,3,opt,name=proved,proto3" json:"proved,omitempty"` } func (m *TxHash) Reset() { *m = TxHash{} } @@ -75,13 +74,6 @@ func (m *TxHash) GetTxSigner() string { return "" } -func (m *TxHash) GetProved() bool { - if m != nil { - return m.Proved - } - return false -} - type OutboundTracker struct { Index string `protobuf:"bytes,1,opt,name=index,proto3" json:"index,omitempty"` ChainId int64 `protobuf:"varint,2,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` @@ -160,26 +152,26 @@ func init() { } var fileDescriptor_77cb2cfe04eb42d9 = []byte{ - // 304 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x90, 0xcd, 0x4a, 0x33, 0x31, - 0x14, 0x86, 0x9b, 0xaf, 0xfd, 0xa6, 0xd3, 0xb8, 0x10, 0x82, 0xe8, 0x88, 0x18, 0x4a, 0x41, 0xe8, - 0xa6, 0x29, 0xa8, 0x57, 0xd0, 0x8d, 0x0a, 0x82, 0x30, 0x16, 0x17, 0x6e, 0x86, 0xe9, 0x24, 0x74, - 0x82, 0x9a, 0x94, 0xe4, 0x54, 0xa2, 0x57, 0xe1, 0x05, 0x78, 0x41, 0x2e, 0xbb, 0x74, 0x29, 0x33, - 0x37, 0x22, 0x93, 0x8c, 0x3f, 0x2b, 0x77, 0x79, 0x72, 0x78, 0x0e, 0xef, 0x79, 0xf1, 0xe9, 0xb3, - 0x80, 0xbc, 0x28, 0x73, 0xa9, 0xa6, 0xfe, 0xa5, 0x8d, 0x98, 0x16, 0x46, 0x5b, 0x1b, 0xfe, 0xf4, - 0x1a, 0x16, 0x7a, 0xad, 0x78, 0x06, 0x26, 0x2f, 0xee, 0x84, 0x61, 0x2b, 0xa3, 0x41, 0x93, 0xc3, - 0x6f, 0x8b, 0x7d, 0x59, 0xec, 0xc7, 0x1a, 0xdd, 0xe0, 0x68, 0xee, 0xce, 0x73, 0x5b, 0x92, 0x3d, - 0xdc, 0x07, 0x97, 0x95, 0xb9, 0x2d, 0x13, 0x34, 0x44, 0xe3, 0x41, 0x1a, 0x41, 0x18, 0x1c, 0xe0, - 0x01, 0xb8, 0xcc, 0xca, 0xa5, 0x12, 0x26, 0xf9, 0xe7, 0x47, 0x31, 0xb8, 0x6b, 0xcf, 0x64, 0x17, - 0x47, 0x2b, 0xa3, 0x1f, 0x05, 0x4f, 0xba, 0x43, 0x34, 0x8e, 0xd3, 0x96, 0x46, 0xaf, 0x08, 0x6f, - 0x5f, 0xb5, 0x89, 0xe6, 0x21, 0x10, 0xd9, 0xc1, 0xff, 0xa5, 0xe2, 0xc2, 0xb5, 0xfb, 0x03, 0x90, - 0x7d, 0x1c, 0xfb, 0x28, 0x99, 0xe4, 0x7e, 0x7b, 0x37, 0xed, 0x7b, 0xbe, 0xe0, 0x8d, 0xa0, 0xb4, - 0x2a, 0x84, 0xdf, 0xdd, 0x4b, 0x03, 0x90, 0x19, 0x1e, 0x34, 0x29, 0xb3, 0x7b, 0x69, 0x21, 0xe9, - 0x0d, 0xbb, 0xe3, 0xad, 0xe3, 0x23, 0xf6, 0xe7, 0x95, 0x2c, 0x9c, 0x98, 0xc6, 0x8d, 0x77, 0x29, - 0x2d, 0xcc, 0xce, 0xde, 0x2a, 0x8a, 0x36, 0x15, 0x45, 0x1f, 0x15, 0x45, 0x2f, 0x35, 0xed, 0x6c, - 0x6a, 0xda, 0x79, 0xaf, 0x69, 0xe7, 0x76, 0xb2, 0x94, 0x50, 0xae, 0x17, 0xac, 0xd0, 0x0f, 0xbe, - 0xe6, 0x49, 0x68, 0x57, 0x69, 0x2e, 0xa6, 0xee, 0x77, 0xdf, 0xf0, 0xb4, 0x12, 0x76, 0x11, 0xf9, - 0x96, 0x4f, 0x3e, 0x03, 0x00, 0x00, 0xff, 0xff, 0x6f, 0x6a, 0xcf, 0xa4, 0x9d, 0x01, 0x00, 0x00, + // 296 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0xa9, 0x4a, 0x2d, 0x49, + 0x4c, 0xce, 0x48, 0xcc, 0xcc, 0xd3, 0x07, 0xb3, 0xf2, 0x8b, 0x52, 0xf5, 0x93, 0x8b, 0xf2, 0x8b, + 0x8b, 0x21, 0x62, 0xf9, 0xa5, 0x25, 0x49, 0xf9, 0xa5, 0x79, 0x29, 0xf1, 0x25, 0x45, 0x89, 0xc9, + 0xd9, 0xa9, 0x45, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0xb2, 0x70, 0x5d, 0x7a, 0x30, 0x5d, + 0x7a, 0x08, 0x5d, 0x4a, 0x2e, 0x5c, 0x6c, 0x21, 0x15, 0x1e, 0x89, 0xc5, 0x19, 0x42, 0xe2, 0x5c, + 0xec, 0x25, 0x15, 0xf1, 0x19, 0x89, 0xc5, 0x19, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x6c, + 0x25, 0x10, 0x09, 0x69, 0x2e, 0xce, 0x92, 0x8a, 0xf8, 0xe2, 0xcc, 0xf4, 0xbc, 0xd4, 0x22, 0x09, + 0x26, 0xb0, 0x14, 0x47, 0x49, 0x45, 0x30, 0x98, 0xef, 0xc5, 0xc2, 0xc1, 0x2c, 0xc0, 0xa2, 0x34, + 0x87, 0x91, 0x8b, 0xdf, 0x1f, 0x6a, 0x7f, 0x08, 0xc4, 0x7a, 0x21, 0x11, 0x2e, 0xd6, 0xcc, 0xbc, + 0x94, 0xd4, 0x0a, 0xa8, 0x69, 0x10, 0x8e, 0x90, 0x24, 0x17, 0x07, 0xd8, 0xe2, 0xf8, 0xcc, 0x14, + 0xb0, 0x59, 0xcc, 0x41, 0xec, 0x60, 0xbe, 0x67, 0x0a, 0x48, 0x43, 0x5e, 0x7e, 0x5e, 0x72, 0xaa, + 0x04, 0xb3, 0x02, 0xa3, 0x06, 0x4b, 0x10, 0x84, 0x23, 0xe4, 0xc4, 0xc5, 0x09, 0x72, 0x53, 0x7c, + 0x4e, 0x66, 0x71, 0x89, 0x04, 0x8b, 0x02, 0xb3, 0x06, 0xb7, 0x91, 0xaa, 0x1e, 0x5e, 0x3f, 0xe9, + 0x41, 0x3c, 0x14, 0xc4, 0x01, 0xd2, 0xe7, 0x93, 0x59, 0x5c, 0xe2, 0xe4, 0x7e, 0xe2, 0x91, 0x1c, + 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, + 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0xba, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, + 0xf9, 0xb9, 0xe0, 0x40, 0xd5, 0x85, 0x84, 0x65, 0x5e, 0x7e, 0x4a, 0xaa, 0x7e, 0x05, 0x72, 0xe8, + 0x96, 0x54, 0x16, 0xa4, 0x16, 0x27, 0xb1, 0x81, 0xc3, 0xd4, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, + 0xd8, 0x18, 0x1c, 0x85, 0x8b, 0x01, 0x00, 0x00, } func (m *TxHash) Marshal() (dAtA []byte, err error) { @@ -202,16 +194,6 @@ func (m *TxHash) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.Proved { - i-- - if m.Proved { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x18 - } if len(m.TxSigner) > 0 { i -= len(m.TxSigner) copy(dAtA[i:], m.TxSigner) @@ -308,9 +290,6 @@ func (m *TxHash) Size() (n int) { if l > 0 { n += 1 + l + sovOutboundTracker(uint64(l)) } - if m.Proved { - n += 2 - } return n } @@ -438,26 +417,6 @@ func (m *TxHash) Unmarshal(dAtA []byte) error { } m.TxSigner = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Proved", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowOutboundTracker - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.Proved = bool(v != 0) default: iNdEx = preIndex skippy, err := skipOutboundTracker(dAtA[iNdEx:]) diff --git a/x/crosschain/types/tx.pb.go b/x/crosschain/types/tx.pb.go index 52facc862d..5624d99667 100644 --- a/x/crosschain/types/tx.pb.go +++ b/x/crosschain/types/tx.pb.go @@ -12,7 +12,7 @@ import ( proto "github.com/cosmos/gogoproto/proto" chains "github.com/zeta-chain/node/pkg/chains" coin "github.com/zeta-chain/node/pkg/coin" - proofs "github.com/zeta-chain/node/pkg/proofs" + _ "github.com/zeta-chain/node/pkg/proofs" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" @@ -210,13 +210,10 @@ func (m *MsgUpdateTssAddressResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgUpdateTssAddressResponse proto.InternalMessageInfo type MsgAddInboundTracker struct { - Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` - ChainId int64 `protobuf:"varint,2,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` - TxHash string `protobuf:"bytes,3,opt,name=tx_hash,json=txHash,proto3" json:"tx_hash,omitempty"` - CoinType coin.CoinType `protobuf:"varint,4,opt,name=coin_type,json=coinType,proto3,enum=zetachain.zetacore.pkg.coin.CoinType" json:"coin_type,omitempty"` - Proof *proofs.Proof `protobuf:"bytes,5,opt,name=proof,proto3" json:"proof,omitempty"` - BlockHash string `protobuf:"bytes,6,opt,name=block_hash,json=blockHash,proto3" json:"block_hash,omitempty"` - TxIndex int64 `protobuf:"varint,7,opt,name=tx_index,json=txIndex,proto3" json:"tx_index,omitempty"` + Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` + ChainId int64 `protobuf:"varint,2,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + TxHash string `protobuf:"bytes,3,opt,name=tx_hash,json=txHash,proto3" json:"tx_hash,omitempty"` + CoinType coin.CoinType `protobuf:"varint,4,opt,name=coin_type,json=coinType,proto3,enum=zetachain.zetacore.pkg.coin.CoinType" json:"coin_type,omitempty"` } func (m *MsgAddInboundTracker) Reset() { *m = MsgAddInboundTracker{} } @@ -280,27 +277,6 @@ func (m *MsgAddInboundTracker) GetCoinType() coin.CoinType { return coin.CoinType_Zeta } -func (m *MsgAddInboundTracker) GetProof() *proofs.Proof { - if m != nil { - return m.Proof - } - return nil -} - -func (m *MsgAddInboundTracker) GetBlockHash() string { - if m != nil { - return m.BlockHash - } - return "" -} - -func (m *MsgAddInboundTracker) GetTxIndex() int64 { - if m != nil { - return m.TxIndex - } - return 0 -} - type MsgAddInboundTrackerResponse struct { } @@ -483,13 +459,10 @@ func (m *MsgWhitelistERC20Response) GetCctxIndex() string { } type MsgAddOutboundTracker struct { - Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` - ChainId int64 `protobuf:"varint,2,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` - Nonce uint64 `protobuf:"varint,3,opt,name=nonce,proto3" json:"nonce,omitempty"` - TxHash string `protobuf:"bytes,4,opt,name=tx_hash,json=txHash,proto3" json:"tx_hash,omitempty"` - Proof *proofs.Proof `protobuf:"bytes,5,opt,name=proof,proto3" json:"proof,omitempty"` - BlockHash string `protobuf:"bytes,6,opt,name=block_hash,json=blockHash,proto3" json:"block_hash,omitempty"` - TxIndex int64 `protobuf:"varint,7,opt,name=tx_index,json=txIndex,proto3" json:"tx_index,omitempty"` + Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` + ChainId int64 `protobuf:"varint,2,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + Nonce uint64 `protobuf:"varint,3,opt,name=nonce,proto3" json:"nonce,omitempty"` + TxHash string `protobuf:"bytes,4,opt,name=tx_hash,json=txHash,proto3" json:"tx_hash,omitempty"` } func (m *MsgAddOutboundTracker) Reset() { *m = MsgAddOutboundTracker{} } @@ -553,27 +526,6 @@ func (m *MsgAddOutboundTracker) GetTxHash() string { return "" } -func (m *MsgAddOutboundTracker) GetProof() *proofs.Proof { - if m != nil { - return m.Proof - } - return nil -} - -func (m *MsgAddOutboundTracker) GetBlockHash() string { - if m != nil { - return m.BlockHash - } - return "" -} - -func (m *MsgAddOutboundTracker) GetTxIndex() int64 { - if m != nil { - return m.TxIndex - } - return 0 -} - type MsgAddOutboundTrackerResponse struct { IsRemoved bool `protobuf:"varint,1,opt,name=is_removed,json=isRemoved,proto3" json:"is_removed,omitempty"` } @@ -995,9 +947,9 @@ type MsgVoteInbound struct { SenderChainId int64 `protobuf:"varint,3,opt,name=sender_chain_id,json=senderChainId,proto3" json:"sender_chain_id,omitempty"` Receiver string `protobuf:"bytes,4,opt,name=receiver,proto3" json:"receiver,omitempty"` ReceiverChain int64 `protobuf:"varint,5,opt,name=receiver_chain,json=receiverChain,proto3" json:"receiver_chain,omitempty"` - // string zeta_burnt = 6; + // string zeta_burnt = 6; Amount github_com_cosmos_cosmos_sdk_types.Uint `protobuf:"bytes,6,opt,name=amount,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Uint" json:"amount"` - // string mMint = 7; + // string mMint = 7; Message string `protobuf:"bytes,8,opt,name=message,proto3" json:"message,omitempty"` InboundHash string `protobuf:"bytes,9,opt,name=inbound_hash,json=inboundHash,proto3" json:"inbound_hash,omitempty"` InboundBlockHeight uint64 `protobuf:"varint,10,opt,name=inbound_block_height,json=inboundBlockHeight,proto3" json:"inbound_block_height,omitempty"` @@ -1734,122 +1686,121 @@ func init() { } var fileDescriptor_15f0860550897740 = []byte{ - // 1840 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x59, 0x4b, 0x6f, 0x1b, 0xc9, - 0x11, 0xf6, 0xd8, 0x12, 0x4d, 0x16, 0x25, 0x59, 0x6e, 0xcb, 0x36, 0x35, 0x5a, 0xc9, 0x32, 0x1d, - 0x3b, 0xc2, 0xc2, 0x22, 0x1d, 0x7a, 0xe3, 0x6c, 0xbc, 0x41, 0x36, 0x16, 0x77, 0xad, 0x15, 0x60, - 0xda, 0xc2, 0xac, 0xbc, 0x79, 0x5c, 0x06, 0xc3, 0x99, 0xd6, 0x68, 0x20, 0x72, 0x9a, 0x98, 0x6e, - 0x72, 0x29, 0x23, 0x40, 0x82, 0x00, 0x01, 0x72, 0x4c, 0x82, 0x9c, 0xf6, 0x90, 0x5b, 0x0e, 0xf9, - 0x13, 0x39, 0xef, 0xd1, 0xc8, 0x29, 0xc8, 0xc1, 0x08, 0xec, 0x43, 0xae, 0x49, 0xae, 0xb9, 0x04, - 0x5d, 0xdd, 0x33, 0x22, 0x87, 0x4f, 0x51, 0x08, 0xf6, 0x22, 0x76, 0x57, 0xd7, 0x57, 0x5d, 0x55, - 0x5d, 0xdd, 0x55, 0x35, 0x82, 0x7b, 0xaf, 0xa8, 0x70, 0xdc, 0x23, 0x27, 0x08, 0xcb, 0x38, 0x62, - 0x11, 0x2d, 0xbb, 0x11, 0xe3, 0x5c, 0xd1, 0x44, 0xb7, 0xd4, 0x8a, 0x98, 0x60, 0x64, 0x3d, 0xe1, - 0x2b, 0xc5, 0x7c, 0xa5, 0x53, 0x3e, 0x73, 0xc5, 0x67, 0x3e, 0x43, 0xce, 0xb2, 0x1c, 0x29, 0x90, - 0xf9, 0xfe, 0x10, 0xe1, 0xad, 0x63, 0xbf, 0x8c, 0x24, 0xae, 0x7f, 0x34, 0xef, 0xbd, 0x51, 0xbc, - 0x2c, 0x08, 0xf1, 0xcf, 0x04, 0x99, 0xad, 0x88, 0xb1, 0x43, 0xae, 0x7f, 0x34, 0xef, 0xa3, 0xf1, - 0xc6, 0x45, 0x8e, 0xa0, 0x76, 0x23, 0x68, 0x06, 0x82, 0x46, 0xf6, 0x61, 0xc3, 0xf1, 0x63, 0x5c, - 0x65, 0x3c, 0x0e, 0x87, 0x36, 0x8e, 0xed, 0xd8, 0x41, 0xc5, 0xdf, 0x1b, 0x40, 0x6a, 0xdc, 0xaf, - 0x05, 0xbe, 0x14, 0x7b, 0xc0, 0xf9, 0xd3, 0x76, 0xe8, 0x71, 0x52, 0x80, 0xcb, 0x6e, 0x44, 0x1d, - 0xc1, 0xa2, 0x82, 0xb1, 0x69, 0x6c, 0xe5, 0xac, 0x78, 0x4a, 0x56, 0x21, 0xab, 0x44, 0x04, 0x5e, - 0xe1, 0xe2, 0xa6, 0xb1, 0x75, 0xc9, 0xba, 0x8c, 0xf3, 0x3d, 0x8f, 0xec, 0x42, 0xc6, 0x69, 0xb2, - 0x76, 0x28, 0x0a, 0x97, 0x24, 0x66, 0xa7, 0xfc, 0xf5, 0x9b, 0x5b, 0x17, 0xfe, 0xfe, 0xe6, 0xd6, - 0xb7, 0xfd, 0x40, 0x1c, 0xb5, 0xeb, 0x25, 0x97, 0x35, 0xcb, 0x2e, 0xe3, 0x4d, 0xc6, 0xf5, 0xcf, - 0x36, 0xf7, 0x8e, 0xcb, 0xe2, 0xa4, 0x45, 0x79, 0xe9, 0x65, 0x10, 0x0a, 0x4b, 0xc3, 0x8b, 0xef, - 0x81, 0x39, 0xa8, 0x93, 0x45, 0x79, 0x8b, 0x85, 0x9c, 0x16, 0x9f, 0xc3, 0xb5, 0x1a, 0xf7, 0x5f, - 0xb6, 0x3c, 0xb5, 0xf8, 0xc4, 0xf3, 0x22, 0xca, 0xc7, 0xa9, 0xbc, 0x0e, 0x20, 0x38, 0xb7, 0x5b, - 0xed, 0xfa, 0x31, 0x3d, 0x41, 0xa5, 0x73, 0x56, 0x4e, 0x70, 0xbe, 0x8f, 0x84, 0xe2, 0x3a, 0xac, - 0x0d, 0x91, 0x97, 0x6c, 0xf7, 0xc7, 0x8b, 0xb0, 0x52, 0xe3, 0xfe, 0x13, 0xcf, 0xdb, 0x0b, 0xeb, - 0xac, 0x1d, 0x7a, 0x07, 0x91, 0xe3, 0x1e, 0xd3, 0x68, 0x36, 0x1f, 0xdd, 0x84, 0xcb, 0xa2, 0x6b, - 0x1f, 0x39, 0xfc, 0x48, 0x39, 0xc9, 0xca, 0x88, 0xee, 0x67, 0x0e, 0x3f, 0x22, 0x3b, 0x90, 0x93, - 0xe1, 0x62, 0x4b, 0x77, 0x14, 0xe6, 0x36, 0x8d, 0xad, 0xa5, 0xca, 0xdd, 0xd2, 0x90, 0xe8, 0x6d, - 0x1d, 0xfb, 0x25, 0x8c, 0xab, 0x2a, 0x0b, 0xc2, 0x83, 0x93, 0x16, 0xb5, 0xb2, 0xae, 0x1e, 0x91, - 0xc7, 0x30, 0x8f, 0x81, 0x54, 0x98, 0xdf, 0x34, 0xb6, 0xf2, 0x95, 0x6f, 0x8d, 0xc2, 0xeb, 0x68, - 0xdb, 0x97, 0x3f, 0x96, 0x82, 0x48, 0x27, 0xd5, 0x1b, 0xcc, 0x3d, 0x56, 0xba, 0x65, 0x94, 0x93, - 0x90, 0x82, 0xea, 0xad, 0x42, 0x56, 0x74, 0xed, 0x20, 0xf4, 0x68, 0xb7, 0x70, 0x59, 0x99, 0x24, - 0xba, 0x7b, 0x72, 0x5a, 0xdc, 0x80, 0xf7, 0x86, 0xf9, 0x27, 0x71, 0xe0, 0x5f, 0x0d, 0xb8, 0x5a, - 0xe3, 0xfe, 0x8f, 0x8f, 0x02, 0x41, 0x1b, 0x01, 0x17, 0x9f, 0x5a, 0xd5, 0xca, 0x83, 0x31, 0xde, - 0xbb, 0x03, 0x8b, 0x34, 0x72, 0x2b, 0x0f, 0x6c, 0x47, 0x9d, 0x84, 0x3e, 0xb1, 0x05, 0x24, 0xc6, - 0xa7, 0xdd, 0xeb, 0xe2, 0x4b, 0xfd, 0x2e, 0x26, 0x30, 0x17, 0x3a, 0x4d, 0xe5, 0xc4, 0x9c, 0x85, - 0x63, 0x72, 0x03, 0x32, 0xfc, 0xa4, 0x59, 0x67, 0x0d, 0x74, 0x4d, 0xce, 0xd2, 0x33, 0x62, 0x42, - 0xd6, 0xa3, 0x6e, 0xd0, 0x74, 0x1a, 0x1c, 0x6d, 0x5e, 0xb4, 0x92, 0x39, 0x59, 0x83, 0x9c, 0xef, - 0x70, 0x75, 0xd3, 0xb4, 0xcd, 0x59, 0xdf, 0xe1, 0xcf, 0xe4, 0xbc, 0x68, 0xc3, 0xea, 0x80, 0x4d, - 0xb1, 0xc5, 0xd2, 0x82, 0x57, 0x7d, 0x16, 0x28, 0x0b, 0x17, 0x5e, 0xf5, 0x5a, 0xb0, 0x0e, 0xe0, - 0xba, 0x89, 0x4f, 0x75, 0x54, 0x4a, 0x8a, 0xf2, 0xea, 0xbf, 0x0d, 0xb8, 0xae, 0xdc, 0xfa, 0xa2, - 0x2d, 0xce, 0x1f, 0x77, 0x2b, 0x30, 0x1f, 0xb2, 0xd0, 0xa5, 0xe8, 0xac, 0x39, 0x4b, 0x4d, 0x7a, - 0xa3, 0x71, 0xae, 0x2f, 0x1a, 0xbf, 0x99, 0x48, 0xfa, 0x21, 0xac, 0x0f, 0x35, 0x39, 0x71, 0xec, - 0x3a, 0x40, 0xc0, 0xed, 0x88, 0x36, 0x59, 0x87, 0x7a, 0x68, 0x7d, 0xd6, 0xca, 0x05, 0xdc, 0x52, - 0x84, 0x22, 0x85, 0x42, 0x8d, 0xfb, 0x6a, 0xf6, 0xff, 0xf3, 0x5a, 0xb1, 0x08, 0x9b, 0xa3, 0xb6, - 0x49, 0x82, 0xfe, 0x2f, 0x06, 0x5c, 0xa9, 0x71, 0xff, 0x0b, 0x26, 0xe8, 0xae, 0xc3, 0xf7, 0xa3, - 0xc0, 0xa5, 0x33, 0xab, 0xd0, 0x92, 0xe8, 0x58, 0x05, 0x9c, 0x90, 0xdb, 0xb0, 0xd0, 0x8a, 0x02, - 0x16, 0x05, 0xe2, 0xc4, 0x3e, 0xa4, 0x14, 0xbd, 0x3c, 0x67, 0xe5, 0x63, 0xda, 0x53, 0x8a, 0x2c, - 0xea, 0x18, 0xc2, 0x76, 0xb3, 0x4e, 0x23, 0x3c, 0xe0, 0x39, 0x2b, 0x8f, 0xb4, 0xe7, 0x48, 0x22, - 0x26, 0x64, 0x78, 0xbb, 0xd5, 0x6a, 0x9c, 0xa8, 0x5b, 0xb1, 0x73, 0xb1, 0x60, 0x58, 0x9a, 0x52, - 0x5c, 0x85, 0x9b, 0x29, 0xfd, 0x13, 0xdb, 0xfe, 0x94, 0x49, 0x6c, 0x8b, 0xcd, 0x1f, 0x63, 0xdb, - 0x1a, 0x60, 0x54, 0xab, 0x68, 0x50, 0x61, 0x9e, 0x95, 0x04, 0x0c, 0x86, 0x0f, 0xe0, 0x06, 0xab, - 0x73, 0x1a, 0x75, 0xa8, 0x67, 0x33, 0x2d, 0xab, 0xf7, 0x75, 0x5c, 0x89, 0x57, 0xe3, 0x8d, 0x10, - 0x55, 0x85, 0x8d, 0x41, 0x94, 0x8e, 0x39, 0x1a, 0xf8, 0x47, 0x42, 0x1b, 0xbb, 0x96, 0x46, 0xef, - 0x60, 0x14, 0x22, 0x0b, 0xf9, 0x08, 0xcc, 0x41, 0x21, 0xf2, 0xc2, 0xb7, 0x39, 0xf5, 0x0a, 0x80, - 0x02, 0x6e, 0xa6, 0x05, 0xec, 0x3a, 0xfc, 0x25, 0xa7, 0x1e, 0xf9, 0xa5, 0x01, 0x77, 0x07, 0xd1, - 0xf4, 0xf0, 0x90, 0xba, 0x22, 0xe8, 0x50, 0x94, 0xa3, 0x8e, 0x2d, 0x8f, 0x9e, 0x2d, 0xe9, 0x54, - 0x78, 0x6f, 0x8a, 0x54, 0xb8, 0x17, 0x0a, 0xeb, 0x76, 0x7a, 0xe3, 0x4f, 0x63, 0xd1, 0x49, 0x34, - 0xed, 0x4f, 0xd6, 0x40, 0x3d, 0x5d, 0x0b, 0x68, 0xca, 0x58, 0x89, 0xf8, 0xa6, 0x11, 0x06, 0x4b, - 0x1d, 0xa7, 0xd1, 0xa6, 0x76, 0x44, 0x5d, 0x1a, 0xc8, 0x1b, 0xa6, 0xc2, 0xe2, 0xb3, 0x33, 0xe6, - 0xf1, 0xff, 0xbc, 0xb9, 0x75, 0xfd, 0xc4, 0x69, 0x36, 0x1e, 0x17, 0xfb, 0xc5, 0x15, 0xad, 0x45, - 0x24, 0x58, 0x7a, 0x4e, 0x3e, 0x81, 0x0c, 0x17, 0x8e, 0x68, 0xab, 0xb7, 0x77, 0xa9, 0x72, 0x7f, - 0x64, 0xc2, 0x53, 0x25, 0x97, 0x06, 0x7e, 0x8e, 0x18, 0x4b, 0x63, 0xc9, 0x5d, 0x58, 0x4a, 0xec, - 0x47, 0x46, 0xfd, 0xac, 0x2c, 0xc6, 0xd4, 0xaa, 0x24, 0x92, 0xfb, 0x40, 0x12, 0x36, 0x59, 0x0e, - 0xa8, 0x8b, 0x9d, 0x45, 0xe7, 0x2c, 0xc7, 0x2b, 0x07, 0x9c, 0x3f, 0xc7, 0x97, 0xb1, 0x2f, 0x1d, - 0xe7, 0x66, 0x4a, 0xc7, 0x3d, 0x57, 0x28, 0xf6, 0x79, 0x72, 0x85, 0xfe, 0x99, 0x81, 0x25, 0xbd, - 0xa6, 0xb3, 0xe6, 0x98, 0x1b, 0x24, 0x93, 0x17, 0x0d, 0x3d, 0x1a, 0xe9, 0xeb, 0xa3, 0x67, 0xe4, - 0x1e, 0x5c, 0x51, 0x23, 0x3b, 0x95, 0x0a, 0x17, 0x15, 0xb9, 0xaa, 0x9f, 0x10, 0x13, 0xb2, 0xfa, - 0x08, 0x22, 0xfd, 0xcc, 0x27, 0x73, 0xe9, 0xbc, 0x78, 0xac, 0x9d, 0x37, 0xaf, 0x44, 0xc4, 0x54, - 0xe5, 0xbc, 0xd3, 0xd2, 0x2e, 0x73, 0xae, 0xd2, 0x4e, 0x5a, 0xd9, 0xa4, 0x9c, 0x3b, 0xbe, 0x72, - 0x7d, 0xce, 0x8a, 0xa7, 0xf2, 0xbd, 0x0a, 0xc2, 0x9e, 0x07, 0x20, 0x87, 0xcb, 0x79, 0x4d, 0xc3, - 0x7b, 0xff, 0x00, 0x56, 0x62, 0x96, 0xbe, 0xdb, 0xae, 0x2e, 0x2b, 0xd1, 0x6b, 0xbd, 0x97, 0xbc, - 0x2f, 0x87, 0xe7, 0x91, 0x2d, 0xc9, 0xe1, 0xfd, 0x67, 0xbc, 0x30, 0x5b, 0xc9, 0xb5, 0x06, 0x39, - 0xd1, 0xb5, 0x59, 0x14, 0xf8, 0x41, 0x58, 0x58, 0x54, 0xce, 0x15, 0xdd, 0x17, 0x38, 0x97, 0x6f, - 0xb7, 0xc3, 0x39, 0x15, 0x85, 0x25, 0x5c, 0x50, 0x13, 0x72, 0x0b, 0xf2, 0xb4, 0x43, 0x43, 0xa1, - 0x73, 0xe0, 0x15, 0xd4, 0x0a, 0x90, 0x84, 0x69, 0x90, 0x44, 0xb0, 0x8a, 0xc5, 0xb9, 0xcb, 0x1a, - 0xb6, 0xcb, 0x42, 0x11, 0x39, 0xae, 0xb0, 0x3b, 0x34, 0xe2, 0x01, 0x0b, 0x0b, 0xcb, 0xa8, 0xe7, - 0xa3, 0xd2, 0xd8, 0xc6, 0x46, 0x26, 0x64, 0xc4, 0x57, 0x35, 0xfc, 0x0b, 0x85, 0xb6, 0x6e, 0xb6, - 0x86, 0x2f, 0x90, 0x9f, 0xca, 0x38, 0xe8, 0xd0, 0x48, 0xd8, 0xac, 0x25, 0x02, 0x16, 0xf2, 0xc2, - 0x55, 0xcc, 0xfc, 0xf7, 0x27, 0x6c, 0x64, 0x21, 0xe8, 0x85, 0xc2, 0xec, 0xcc, 0xc9, 0xb0, 0x90, - 0xb1, 0xd3, 0x43, 0x24, 0x35, 0x58, 0x70, 0x9d, 0x46, 0x23, 0x11, 0x4c, 0x50, 0xf0, 0xfb, 0x13, - 0x04, 0x57, 0x9d, 0x46, 0x43, 0x4b, 0xb0, 0xf2, 0xee, 0xe9, 0x84, 0x6c, 0xc3, 0xb5, 0x80, 0xdb, - 0xbd, 0xcd, 0x8c, 0x5c, 0x2d, 0x5c, 0xc3, 0x62, 0x60, 0x39, 0xe0, 0x55, 0xb9, 0x82, 0x51, 0x2b, - 0x45, 0x14, 0x0b, 0x70, 0xa3, 0xff, 0xa2, 0x25, 0x77, 0xf0, 0x19, 0x96, 0xa5, 0x4f, 0xea, 0x2c, - 0x12, 0x9f, 0x8b, 0xb6, 0x7b, 0x5c, 0xad, 0x1e, 0xfc, 0x64, 0x7c, 0x17, 0x31, 0xae, 0x5e, 0x5b, - 0xc3, 0x82, 0xb0, 0x5f, 0x5a, 0xb2, 0x55, 0x07, 0x5b, 0x08, 0x8b, 0x1e, 0xb6, 0x43, 0x0f, 0x59, - 0xa8, 0x77, 0xae, 0xdd, 0xd4, 0xb5, 0x95, 0xd2, 0x92, 0x12, 0x53, 0xe5, 0xcb, 0x45, 0x45, 0xd5, - 0x35, 0xa6, 0x2e, 0xcd, 0x07, 0xf6, 0x4d, 0xf4, 0xfa, 0xca, 0x40, 0xad, 0x55, 0xef, 0x63, 0x39, - 0x82, 0x3e, 0x53, 0x6d, 0xe5, 0x53, 0xd9, 0x55, 0x8e, 0xd1, 0xce, 0x05, 0x32, 0xd8, 0x85, 0xa2, - 0x96, 0xf9, 0x4a, 0x79, 0x52, 0xc4, 0xa4, 0xb6, 0xd1, 0x41, 0xb3, 0x1c, 0xa5, 0xe8, 0xc5, 0x3b, - 0x70, 0x7b, 0xa4, 0x6e, 0x89, 0x05, 0xff, 0x32, 0xb0, 0x7b, 0xd3, 0xbd, 0x22, 0x96, 0xe1, 0xd5, - 0x36, 0x17, 0xcc, 0x3b, 0x39, 0x47, 0x23, 0x5b, 0x82, 0x6b, 0x21, 0xfd, 0xd2, 0x76, 0x95, 0xa0, - 0x94, 0x8b, 0xaf, 0x86, 0xf4, 0x4b, 0xbd, 0x45, 0x5c, 0xca, 0x0f, 0x74, 0x2c, 0x73, 0x43, 0x3a, - 0x96, 0xd3, 0x27, 0x74, 0xfe, 0x7c, 0xdd, 0xf1, 0x27, 0x70, 0x67, 0x8c, 0xc5, 0xbd, 0xb5, 0x72, - 0x4f, 0x04, 0x19, 0xe9, 0x78, 0x6d, 0x62, 0x11, 0xab, 0xbc, 0xdb, 0x2b, 0x64, 0xdf, 0x69, 0x73, - 0x9d, 0x61, 0x67, 0x2f, 0x58, 0xa5, 0x0c, 0x74, 0x57, 0xd6, 0x52, 0x93, 0xe2, 0x1e, 0x6c, 0x4d, - 0xda, 0x6e, 0x4a, 0xcd, 0x2b, 0xff, 0x5d, 0x82, 0x4b, 0x35, 0xee, 0x93, 0xdf, 0x18, 0x40, 0x86, - 0xb4, 0x47, 0x1f, 0x4c, 0x88, 0xbf, 0xa1, 0x1d, 0x86, 0xf9, 0x83, 0x59, 0x50, 0x89, 0xc6, 0xbf, - 0x36, 0xe0, 0xea, 0xe0, 0x07, 0x82, 0x87, 0x53, 0xc9, 0xec, 0x07, 0x99, 0x1f, 0xcd, 0x00, 0x4a, - 0xf4, 0xf8, 0x9d, 0x01, 0xd7, 0x87, 0xb7, 0x3f, 0xdf, 0x9b, 0x2c, 0x76, 0x28, 0xd0, 0xfc, 0x78, - 0x46, 0x60, 0xa2, 0x53, 0x07, 0x16, 0xfa, 0xba, 0xa0, 0xd2, 0x64, 0x81, 0xbd, 0xfc, 0xe6, 0xa3, - 0xb3, 0xf1, 0xa7, 0xf7, 0x4d, 0x3a, 0x94, 0x29, 0xf7, 0x8d, 0xf9, 0xa7, 0xdd, 0x37, 0x5d, 0xda, - 0x11, 0x0e, 0xf9, 0xde, 0xb2, 0x6e, 0x7b, 0x3a, 0x31, 0x9a, 0xdd, 0xfc, 0xee, 0x99, 0xd8, 0x93, - 0x4d, 0x7f, 0x0e, 0x4b, 0xa9, 0xef, 0x2b, 0x0f, 0x26, 0x0b, 0xea, 0x47, 0x98, 0x1f, 0x9e, 0x15, - 0x91, 0xec, 0xfe, 0x2b, 0x03, 0x96, 0x07, 0xbe, 0xc7, 0x55, 0x26, 0x8b, 0x4b, 0x63, 0xcc, 0xc7, - 0x67, 0xc7, 0x24, 0x4a, 0xfc, 0x02, 0xae, 0xa4, 0xbf, 0x62, 0x7e, 0x67, 0xb2, 0xb8, 0x14, 0xc4, - 0xfc, 0xfe, 0x99, 0x21, 0xbd, 0x67, 0x90, 0x2a, 0x26, 0xa6, 0x38, 0x83, 0x7e, 0xc4, 0x34, 0x67, - 0x30, 0xbc, 0xc4, 0xc0, 0x27, 0x68, 0xb0, 0xc0, 0x78, 0x38, 0xcd, 0xed, 0x4d, 0x81, 0xa6, 0x79, - 0x82, 0x46, 0x96, 0x14, 0xe4, 0x0f, 0x06, 0xdc, 0x18, 0x51, 0x4f, 0x7c, 0x38, 0xed, 0xe9, 0xa6, - 0x91, 0xe6, 0x8f, 0x66, 0x45, 0x26, 0x6a, 0x7d, 0x65, 0x40, 0x61, 0x64, 0x91, 0xf0, 0x78, 0xea, - 0x43, 0x1f, 0xc0, 0x9a, 0x3b, 0xb3, 0x63, 0x13, 0xe5, 0xfe, 0x6c, 0xc0, 0xfa, 0xf8, 0x4c, 0xfc, - 0xf1, 0xb4, 0x0e, 0x18, 0x21, 0xc0, 0xdc, 0x3d, 0xa7, 0x80, 0x58, 0xd7, 0x9d, 0xdd, 0xaf, 0xdf, - 0x6e, 0x18, 0xaf, 0xdf, 0x6e, 0x18, 0xff, 0x78, 0xbb, 0x61, 0xfc, 0xf6, 0xdd, 0xc6, 0x85, 0xd7, - 0xef, 0x36, 0x2e, 0xfc, 0xed, 0xdd, 0xc6, 0x85, 0x9f, 0x6d, 0xf7, 0x14, 0x32, 0x72, 0x8b, 0x6d, - 0xf5, 0x6f, 0x87, 0x90, 0x79, 0xb4, 0xdc, 0xed, 0xfb, 0xef, 0x8c, 0xac, 0x69, 0xea, 0x19, 0x6c, - 0x45, 0x1e, 0xfe, 0x2f, 0x00, 0x00, 0xff, 0xff, 0x5c, 0xd9, 0x42, 0xd4, 0xcb, 0x19, 0x00, 0x00, + // 1817 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0xcd, 0x6f, 0xdb, 0xc8, + 0x15, 0x0f, 0x37, 0xb2, 0x2c, 0x3d, 0xd9, 0x8e, 0x33, 0x71, 0x12, 0x86, 0x5e, 0x3b, 0x0e, 0x83, + 0xa4, 0xc6, 0x62, 0x2d, 0xa5, 0xce, 0x36, 0xdd, 0x66, 0x8b, 0x6e, 0x63, 0xed, 0xc6, 0x71, 0x10, + 0x25, 0x06, 0xd7, 0xd9, 0x7e, 0x5c, 0x08, 0x8a, 0x1c, 0x53, 0x84, 0x25, 0x8e, 0xc0, 0x19, 0x69, + 0xa5, 0xa0, 0x40, 0x8b, 0x02, 0x05, 0x7a, 0x29, 0xd0, 0x16, 0x3d, 0xed, 0xbd, 0x87, 0xfe, 0x0b, + 0x3d, 0xf4, 0xbc, 0xc7, 0xa0, 0xa7, 0xa2, 0x87, 0xa0, 0x48, 0x0e, 0xbd, 0x16, 0xbd, 0xf6, 0x52, + 0xcc, 0x07, 0x69, 0x8a, 0xfa, 0xb4, 0x8c, 0xbd, 0xc4, 0x9c, 0x37, 0xef, 0xf7, 0xbe, 0xe6, 0xcd, + 0xbc, 0xf7, 0x14, 0xb8, 0xfb, 0x0a, 0x33, 0xc7, 0x6d, 0x38, 0x41, 0x58, 0x11, 0x5f, 0x24, 0xc2, + 0x15, 0x37, 0x22, 0x94, 0x4a, 0x1a, 0xeb, 0x95, 0xdb, 0x11, 0x61, 0x04, 0x6d, 0x24, 0x7c, 0xe5, + 0x98, 0xaf, 0x7c, 0xca, 0x67, 0xac, 0xf9, 0xc4, 0x27, 0x82, 0xb3, 0xc2, 0xbf, 0x24, 0xc8, 0xf8, + 0x60, 0x84, 0xf0, 0xf6, 0x89, 0x5f, 0x11, 0x24, 0xaa, 0xfe, 0x28, 0xde, 0xbb, 0xe3, 0x78, 0x49, + 0x10, 0x8a, 0x7f, 0xa6, 0xc8, 0x6c, 0x47, 0x84, 0x1c, 0x53, 0xf5, 0x47, 0xf1, 0x3e, 0x98, 0xec, + 0x5c, 0xe4, 0x30, 0x6c, 0x37, 0x83, 0x56, 0xc0, 0x70, 0x64, 0x1f, 0x37, 0x1d, 0x3f, 0xc6, 0xed, + 0x4e, 0xc6, 0x89, 0x4f, 0x5b, 0x7c, 0xdb, 0x71, 0x80, 0xcc, 0x3f, 0x6a, 0x80, 0x6a, 0xd4, 0xaf, + 0x05, 0x3e, 0x17, 0x7b, 0x44, 0xe9, 0xe3, 0x4e, 0xe8, 0x51, 0xa4, 0xc3, 0xa2, 0x1b, 0x61, 0x87, + 0x91, 0x48, 0xd7, 0xb6, 0xb4, 0xed, 0xa2, 0x15, 0x2f, 0xd1, 0x0d, 0x28, 0x48, 0x11, 0x81, 0xa7, + 0xbf, 0xb7, 0xa5, 0x6d, 0x5f, 0xb4, 0x16, 0xc5, 0xfa, 0xc0, 0x43, 0xfb, 0x90, 0x77, 0x5a, 0xa4, + 0x13, 0x32, 0xfd, 0x22, 0xc7, 0xec, 0x55, 0xbe, 0x79, 0x73, 0xf3, 0xc2, 0x3f, 0xdf, 0xdc, 0xfc, + 0x8e, 0x1f, 0xb0, 0x46, 0xa7, 0x5e, 0x76, 0x49, 0xab, 0xe2, 0x12, 0xda, 0x22, 0x54, 0xfd, 0xd9, + 0xa1, 0xde, 0x49, 0x85, 0xf5, 0xdb, 0x98, 0x96, 0x5f, 0x06, 0x21, 0xb3, 0x14, 0xdc, 0x7c, 0x1f, + 0x8c, 0x61, 0x9b, 0x2c, 0x4c, 0xdb, 0x24, 0xa4, 0xd8, 0x7c, 0x0e, 0x57, 0x6a, 0xd4, 0x7f, 0xd9, + 0xf6, 0xe4, 0xe6, 0x23, 0xcf, 0x8b, 0x30, 0x9d, 0x64, 0xf2, 0x06, 0x00, 0xa3, 0xd4, 0x6e, 0x77, + 0xea, 0x27, 0xb8, 0x2f, 0x8c, 0x2e, 0x5a, 0x45, 0x46, 0xe9, 0xa1, 0x20, 0x98, 0x1b, 0xb0, 0x3e, + 0x42, 0x5e, 0xa2, 0xee, 0xaf, 0x1a, 0xac, 0xd5, 0xa8, 0xff, 0xc8, 0xf3, 0x0e, 0xc2, 0x3a, 0xe9, + 0x84, 0xde, 0x51, 0xe4, 0xb8, 0x27, 0x38, 0x9a, 0x2f, 0x46, 0xd7, 0x61, 0x91, 0xf5, 0xec, 0x86, + 0x43, 0x1b, 0x32, 0x48, 0x56, 0x9e, 0xf5, 0x9e, 0x38, 0xb4, 0x81, 0xf6, 0xa0, 0xc8, 0xd3, 0xc5, + 0xe6, 0xe1, 0xd0, 0x73, 0x5b, 0xda, 0xf6, 0xca, 0xee, 0x9d, 0xf2, 0x88, 0xec, 0x6d, 0x9f, 0xf8, + 0x65, 0x91, 0x57, 0x55, 0x12, 0x84, 0x47, 0xfd, 0x36, 0xb6, 0x0a, 0xae, 0xfa, 0x7a, 0x9a, 0x2b, + 0x2c, 0xac, 0xe6, 0x9f, 0xe6, 0x0a, 0xf9, 0xd5, 0xc5, 0xa7, 0xb9, 0xc2, 0xe2, 0x6a, 0xc1, 0xdc, + 0x84, 0xf7, 0x47, 0xd9, 0x9e, 0x38, 0xf7, 0x77, 0x0d, 0x2e, 0xd7, 0xa8, 0xff, 0x93, 0x46, 0xc0, + 0x70, 0x33, 0xa0, 0xec, 0x73, 0xab, 0xba, 0x7b, 0x6f, 0x82, 0x67, 0xb7, 0x61, 0x19, 0x47, 0xee, + 0xee, 0x3d, 0xdb, 0x91, 0x51, 0x52, 0xd1, 0x5c, 0x12, 0xc4, 0xf8, 0x24, 0xd2, 0xee, 0x5f, 0x1c, + 0x74, 0x1f, 0x41, 0x2e, 0x74, 0x5a, 0xd2, 0xc1, 0xa2, 0x25, 0xbe, 0xd1, 0x35, 0xc8, 0xd3, 0x7e, + 0xab, 0x4e, 0x9a, 0xfa, 0x82, 0x8c, 0x88, 0x5c, 0x21, 0x03, 0x0a, 0x1e, 0x76, 0x83, 0x96, 0xd3, + 0xa4, 0x7a, 0x7e, 0x4b, 0xdb, 0x5e, 0xb6, 0x92, 0x35, 0x5a, 0x87, 0xa2, 0xef, 0x50, 0x79, 0x0b, + 0xf4, 0x45, 0xa1, 0xa3, 0xe0, 0x3b, 0xf4, 0x19, 0x5f, 0x9b, 0x36, 0xdc, 0x18, 0xf2, 0x29, 0xf6, + 0x98, 0x7b, 0xf0, 0x6a, 0xc0, 0x03, 0xe9, 0xe1, 0xd2, 0xab, 0xb4, 0x07, 0x1b, 0x00, 0xae, 0xcb, + 0x7a, 0x76, 0x10, 0x7a, 0xb8, 0x17, 0x67, 0x0c, 0xa7, 0x1c, 0x70, 0x82, 0xf9, 0x3b, 0x0d, 0xae, + 0xca, 0xb0, 0xbe, 0xe8, 0xb0, 0xf3, 0xe7, 0xc4, 0x1a, 0x2c, 0x84, 0x24, 0x74, 0xb1, 0x08, 0x56, + 0xce, 0x92, 0x8b, 0x74, 0xa6, 0xe4, 0xd2, 0x99, 0x32, 0xe2, 0x94, 0x7f, 0x04, 0x1b, 0x23, 0xcd, + 0x49, 0x9c, 0xde, 0x00, 0x08, 0xa8, 0x1d, 0xe1, 0x16, 0xe9, 0x62, 0x4f, 0x58, 0x56, 0xb0, 0x8a, + 0x01, 0xb5, 0x24, 0xc1, 0xc4, 0xa0, 0xd7, 0xa8, 0x2f, 0x57, 0xdf, 0x9e, 0x47, 0xa6, 0x09, 0x5b, + 0xe3, 0xd4, 0x24, 0x09, 0xf9, 0x37, 0x0d, 0x2e, 0xd5, 0xa8, 0xff, 0x25, 0x61, 0x78, 0xdf, 0xa1, + 0x87, 0x51, 0xe0, 0xe2, 0xb9, 0x4d, 0x68, 0x73, 0x74, 0x6c, 0x82, 0x58, 0xa0, 0x5b, 0xb0, 0xd4, + 0x8e, 0x02, 0x12, 0x05, 0xac, 0x6f, 0x1f, 0x63, 0x2c, 0xf2, 0x2a, 0x67, 0x95, 0x62, 0xda, 0x63, + 0x2c, 0x58, 0xea, 0x4d, 0xe2, 0x9e, 0xd8, 0x61, 0xa7, 0x55, 0xc7, 0x91, 0x08, 0x7e, 0xce, 0x2a, + 0x09, 0xda, 0x73, 0x41, 0x42, 0x06, 0xe4, 0x69, 0xa7, 0xdd, 0x6e, 0xf6, 0x65, 0xc6, 0xee, 0xbd, + 0xa7, 0x6b, 0x96, 0xa2, 0x98, 0x37, 0xe0, 0x7a, 0xc6, 0xfe, 0xc4, 0xb7, 0x3f, 0xe7, 0x13, 0xdf, + 0x62, 0xf7, 0x27, 0xf8, 0xb6, 0x0e, 0x22, 0xe3, 0x64, 0x06, 0xc8, 0x14, 0x2c, 0x70, 0x82, 0x78, + 0x2d, 0x3e, 0x82, 0x6b, 0xa4, 0x4e, 0x71, 0xd4, 0xc5, 0x9e, 0x4d, 0x94, 0xac, 0xf4, 0xab, 0xb2, + 0x16, 0xef, 0xc6, 0x8a, 0x04, 0xaa, 0x0a, 0x9b, 0xc3, 0x28, 0xe9, 0x6c, 0x03, 0x07, 0x7e, 0x83, + 0x29, 0x67, 0xd7, 0xb3, 0xe8, 0x3d, 0xce, 0xf3, 0x44, 0xb0, 0xa0, 0x4f, 0xc0, 0x18, 0x16, 0xc2, + 0x2f, 0x63, 0x87, 0x62, 0x4f, 0x07, 0x21, 0xe0, 0x7a, 0x56, 0xc0, 0xbe, 0x43, 0x5f, 0x52, 0xec, + 0xa1, 0x5f, 0x69, 0x70, 0x67, 0x18, 0x8d, 0x8f, 0x8f, 0xb1, 0xcb, 0x82, 0x2e, 0x16, 0x72, 0xe4, + 0xb1, 0x95, 0x44, 0x64, 0xcb, 0xaa, 0x84, 0xdc, 0x9d, 0xa1, 0x84, 0x1c, 0x84, 0xcc, 0xba, 0x95, + 0x55, 0xfc, 0x79, 0x2c, 0x3a, 0xc9, 0xa6, 0xc3, 0xe9, 0x16, 0xc8, 0x67, 0x65, 0x49, 0xb8, 0x32, + 0x51, 0xa2, 0x78, 0x6f, 0x10, 0x81, 0x95, 0xae, 0xd3, 0xec, 0x60, 0x3b, 0xc2, 0x2e, 0x0e, 0xf8, + 0x0d, 0x93, 0x69, 0xf1, 0xe4, 0x8c, 0xf5, 0xef, 0xbf, 0x6f, 0x6e, 0x5e, 0xed, 0x3b, 0xad, 0xe6, + 0x43, 0x73, 0x50, 0x9c, 0x69, 0x2d, 0x0b, 0x82, 0xa5, 0xd6, 0xe8, 0x33, 0xc8, 0x53, 0xe6, 0xb0, + 0x8e, 0x7c, 0x17, 0x57, 0x76, 0x3f, 0x1c, 0x5b, 0x28, 0x64, 0xab, 0xa2, 0x80, 0x5f, 0x08, 0x8c, + 0xa5, 0xb0, 0xe8, 0x0e, 0xac, 0x24, 0xfe, 0x0b, 0x46, 0xf5, 0x90, 0x2e, 0xc7, 0xd4, 0x2a, 0x27, + 0xa2, 0x0f, 0x01, 0x25, 0x6c, 0xbc, 0x8c, 0xca, 0x8b, 0x5d, 0x10, 0xc1, 0x59, 0x8d, 0x77, 0x8e, + 0x28, 0x7d, 0x2e, 0x5e, 0xad, 0x81, 0x32, 0x56, 0x9c, 0xab, 0x8c, 0xa5, 0xae, 0x50, 0x1c, 0xf3, + 0xe4, 0x0a, 0xfd, 0x3b, 0x0f, 0x2b, 0x6a, 0x4f, 0x55, 0xb4, 0x09, 0x37, 0x88, 0x17, 0x16, 0x1c, + 0x7a, 0x38, 0x52, 0xd7, 0x47, 0xad, 0xd0, 0x5d, 0xb8, 0x24, 0xbf, 0xec, 0x4c, 0x99, 0x5a, 0x96, + 0xe4, 0xaa, 0x7a, 0x42, 0x0c, 0x28, 0xa8, 0x23, 0x88, 0xd4, 0x13, 0x9c, 0xac, 0x79, 0xf0, 0xe2, + 0x6f, 0x15, 0xbc, 0x05, 0x29, 0x22, 0xa6, 0xca, 0xe0, 0x9d, 0xb6, 0x44, 0xf9, 0x73, 0xb5, 0x44, + 0xdc, 0xcb, 0x16, 0xa6, 0xd4, 0xf1, 0x65, 0xe8, 0x8b, 0x56, 0xbc, 0xe4, 0xef, 0x55, 0x10, 0xa6, + 0x1e, 0x80, 0xa2, 0xd8, 0x2e, 0x29, 0x9a, 0xb8, 0xf7, 0xf7, 0x60, 0x2d, 0x66, 0x19, 0xb8, 0xed, + 0xf2, 0xb2, 0x22, 0xb5, 0x97, 0xbe, 0xe4, 0x03, 0xf5, 0xb5, 0x24, 0xd8, 0x92, 0xfa, 0x3a, 0x78, + 0xc6, 0x4b, 0x73, 0x9d, 0x31, 0x57, 0xc0, 0x7a, 0x36, 0x89, 0x02, 0x3f, 0x08, 0xf5, 0x65, 0x19, + 0x5c, 0xd6, 0x7b, 0x21, 0xd6, 0xfc, 0xed, 0x76, 0x28, 0xc5, 0x4c, 0x5f, 0x11, 0x1b, 0x72, 0x81, + 0x6e, 0x42, 0x09, 0x77, 0x71, 0xc8, 0x54, 0x55, 0xbe, 0x24, 0xac, 0x02, 0x41, 0x12, 0x65, 0x19, + 0x45, 0x70, 0x43, 0x34, 0xb5, 0x2e, 0x69, 0xda, 0x2e, 0x09, 0x59, 0xe4, 0xb8, 0xcc, 0xee, 0xe2, + 0x88, 0x06, 0x24, 0xd4, 0x57, 0x85, 0x9d, 0x0f, 0xca, 0x13, 0x07, 0x82, 0xf2, 0xa1, 0xc2, 0x57, + 0x15, 0xfc, 0x4b, 0x89, 0xb6, 0xae, 0xb7, 0x47, 0x6f, 0xa0, 0x9f, 0xf1, 0x3c, 0xe8, 0xe2, 0x88, + 0xd9, 0xa4, 0xcd, 0x02, 0x12, 0x52, 0xfd, 0xf2, 0x96, 0xb6, 0x5d, 0x1a, 0x7d, 0x25, 0x53, 0x8a, + 0x2c, 0x01, 0x7a, 0x21, 0x31, 0x7b, 0x39, 0x9e, 0x16, 0x3c, 0x77, 0x52, 0x44, 0x54, 0x83, 0x25, + 0xd7, 0x69, 0x36, 0x13, 0xc1, 0x48, 0x08, 0xfe, 0x60, 0x8a, 0xe0, 0xaa, 0xd3, 0x6c, 0x2a, 0x09, + 0x56, 0xc9, 0x3d, 0x5d, 0xa0, 0x1d, 0xb8, 0x12, 0x50, 0x3b, 0x3d, 0x04, 0xf0, 0x5d, 0xfd, 0x8a, + 0x68, 0x06, 0x56, 0x03, 0x5a, 0xe5, 0x3b, 0x22, 0x6b, 0xb9, 0x08, 0x53, 0x87, 0x6b, 0x83, 0x17, + 0x2d, 0xb9, 0x83, 0xcf, 0x44, 0xcb, 0xf8, 0xa8, 0x4e, 0x22, 0xf6, 0x05, 0xeb, 0xb8, 0x27, 0xd5, + 0xea, 0xd1, 0x4f, 0x27, 0x77, 0xdf, 0x93, 0x7a, 0xa9, 0x75, 0xd1, 0xac, 0x0d, 0x4a, 0x4b, 0x54, + 0x75, 0x45, 0xeb, 0x6d, 0xe1, 0xe3, 0x4e, 0xe8, 0x09, 0x16, 0xec, 0x9d, 0x4b, 0x9b, 0xbc, 0xb6, + 0x5c, 0x5a, 0xd2, 0xfe, 0xc9, 0x7a, 0xb9, 0x2c, 0xa9, 0xaa, 0xff, 0x53, 0x6d, 0xf3, 0x90, 0xde, + 0xc4, 0xae, 0xaf, 0x35, 0x61, 0xb5, 0x9c, 0x19, 0x2c, 0x87, 0xe1, 0x67, 0x72, 0x1c, 0x7b, 0xcc, + 0xa7, 0xb1, 0x09, 0xd6, 0xb9, 0x80, 0x86, 0xa7, 0x37, 0x61, 0x65, 0x69, 0xb7, 0x32, 0x2d, 0x63, + 0x32, 0x6a, 0x54, 0xd2, 0xac, 0x46, 0x19, 0xba, 0x79, 0x1b, 0x6e, 0x8d, 0xb5, 0x2d, 0xf1, 0xe0, + 0x3f, 0x9a, 0x98, 0x7a, 0xd4, 0x8c, 0x25, 0x5a, 0xe4, 0x6a, 0x87, 0x32, 0xe2, 0xf5, 0xcf, 0x31, + 0x00, 0x96, 0xe1, 0x4a, 0x88, 0xbf, 0xb2, 0x5d, 0x29, 0x28, 0x13, 0xe2, 0xcb, 0x21, 0xfe, 0x4a, + 0xa9, 0x88, 0xdb, 0xec, 0xa1, 0x69, 0x22, 0x37, 0x62, 0x9a, 0x38, 0x7d, 0x42, 0x17, 0xce, 0x37, + 0x55, 0x7e, 0x06, 0xb7, 0x27, 0x78, 0x9c, 0xee, 0x95, 0x53, 0x19, 0xa4, 0x65, 0xf3, 0xb5, 0x25, + 0x9a, 0x58, 0x19, 0xdd, 0xb4, 0x90, 0x43, 0xa7, 0x43, 0x55, 0x85, 0x9d, 0xbf, 0x61, 0xe5, 0x32, + 0x44, 0xb8, 0x0a, 0x96, 0x5c, 0x98, 0x07, 0xb0, 0x3d, 0x4d, 0xdd, 0x8c, 0x96, 0xef, 0xfe, 0x6f, + 0x05, 0x2e, 0xd6, 0xa8, 0x8f, 0x7e, 0xab, 0x01, 0x1a, 0x31, 0xba, 0x7c, 0x34, 0x25, 0xff, 0x46, + 0x4e, 0x18, 0xc6, 0x0f, 0xe7, 0x41, 0x25, 0x16, 0xff, 0x46, 0x83, 0xcb, 0xc3, 0x83, 0xf5, 0xfd, + 0x99, 0x64, 0x0e, 0x82, 0x8c, 0x4f, 0xe6, 0x00, 0x25, 0x76, 0xfc, 0x41, 0x83, 0xab, 0xa3, 0xc7, + 0x9f, 0xef, 0x4f, 0x17, 0x3b, 0x12, 0x68, 0x7c, 0x3a, 0x27, 0x30, 0xb1, 0xa9, 0x0b, 0x4b, 0x03, + 0x53, 0x50, 0x79, 0xba, 0xc0, 0x34, 0xbf, 0xf1, 0xe0, 0x6c, 0xfc, 0x59, 0xbd, 0xc9, 0x84, 0x32, + 0xa3, 0xde, 0x98, 0x7f, 0x56, 0xbd, 0xd9, 0xd6, 0x0e, 0x51, 0x28, 0xa5, 0xdb, 0xba, 0x9d, 0xd9, + 0xc4, 0x28, 0x76, 0xe3, 0x7b, 0x67, 0x62, 0x4f, 0x94, 0xfe, 0x02, 0x56, 0x32, 0xbf, 0x7d, 0xdc, + 0x9b, 0x2e, 0x68, 0x10, 0x61, 0x7c, 0x7c, 0x56, 0x44, 0xa2, 0xfd, 0xd7, 0x1a, 0xac, 0x0e, 0xfd, + 0x8e, 0xb5, 0x3b, 0x5d, 0x5c, 0x16, 0x63, 0x3c, 0x3c, 0x3b, 0x26, 0x31, 0xe2, 0x97, 0x70, 0x29, + 0xfb, 0xeb, 0xdf, 0x77, 0xa7, 0x8b, 0xcb, 0x40, 0x8c, 0x1f, 0x9c, 0x19, 0x92, 0x3e, 0x83, 0x4c, + 0x33, 0x31, 0xc3, 0x19, 0x0c, 0x22, 0x66, 0x39, 0x83, 0xd1, 0x2d, 0x86, 0x78, 0x82, 0x86, 0x1b, + 0x8c, 0xfb, 0xb3, 0xdc, 0xde, 0x0c, 0x68, 0x96, 0x27, 0x68, 0x6c, 0x4b, 0x81, 0xfe, 0xa4, 0xc1, + 0xb5, 0x31, 0xfd, 0xc4, 0xc7, 0xb3, 0x9e, 0x6e, 0x16, 0x69, 0xfc, 0x78, 0x5e, 0x64, 0x62, 0xd6, + 0xd7, 0x1a, 0xe8, 0x63, 0x9b, 0x84, 0x87, 0x33, 0x1f, 0xfa, 0x10, 0xd6, 0xd8, 0x9b, 0x1f, 0x9b, + 0x18, 0xf7, 0x17, 0x0d, 0x36, 0x26, 0x57, 0xe2, 0x4f, 0x67, 0x0d, 0xc0, 0x18, 0x01, 0xc6, 0xfe, + 0x39, 0x05, 0xc4, 0xb6, 0xee, 0xed, 0x7f, 0xf3, 0x76, 0x53, 0x7b, 0xfd, 0x76, 0x53, 0xfb, 0xd7, + 0xdb, 0x4d, 0xed, 0xf7, 0xef, 0x36, 0x2f, 0xbc, 0x7e, 0xb7, 0x79, 0xe1, 0x1f, 0xef, 0x36, 0x2f, + 0xfc, 0x7c, 0x27, 0xd5, 0xc8, 0x70, 0x15, 0x3b, 0xf2, 0xe7, 0xfa, 0x90, 0x78, 0xb8, 0xd2, 0x1b, + 0xf8, 0x5f, 0x0d, 0xde, 0xd3, 0xd4, 0xf3, 0x62, 0x14, 0xb9, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xbe, 0xc4, 0x2d, 0x51, 0x03, 0x19, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2548,30 +2499,6 @@ func (m *MsgAddInboundTracker) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.TxIndex != 0 { - i = encodeVarintTx(dAtA, i, uint64(m.TxIndex)) - i-- - dAtA[i] = 0x38 - } - if len(m.BlockHash) > 0 { - i -= len(m.BlockHash) - copy(dAtA[i:], m.BlockHash) - i = encodeVarintTx(dAtA, i, uint64(len(m.BlockHash))) - i-- - dAtA[i] = 0x32 - } - if m.Proof != nil { - { - size, err := m.Proof.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintTx(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x2a - } if m.CoinType != 0 { i = encodeVarintTx(dAtA, i, uint64(m.CoinType)) i-- @@ -2745,30 +2672,6 @@ func (m *MsgAddOutboundTracker) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.TxIndex != 0 { - i = encodeVarintTx(dAtA, i, uint64(m.TxIndex)) - i-- - dAtA[i] = 0x38 - } - if len(m.BlockHash) > 0 { - i -= len(m.BlockHash) - copy(dAtA[i:], m.BlockHash) - i = encodeVarintTx(dAtA, i, uint64(len(m.BlockHash))) - i-- - dAtA[i] = 0x32 - } - if m.Proof != nil { - { - size, err := m.Proof.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintTx(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x2a - } if len(m.TxHash) > 0 { i -= len(m.TxHash) copy(dAtA[i:], m.TxHash) @@ -3712,17 +3615,6 @@ func (m *MsgAddInboundTracker) Size() (n int) { if m.CoinType != 0 { n += 1 + sovTx(uint64(m.CoinType)) } - if m.Proof != nil { - l = m.Proof.Size() - n += 1 + l + sovTx(uint64(l)) - } - l = len(m.BlockHash) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) - } - if m.TxIndex != 0 { - n += 1 + sovTx(uint64(m.TxIndex)) - } return n } @@ -3806,17 +3698,6 @@ func (m *MsgAddOutboundTracker) Size() (n int) { if l > 0 { n += 1 + l + sovTx(uint64(l)) } - if m.Proof != nil { - l = m.Proof.Size() - n += 1 + l + sovTx(uint64(l)) - } - l = len(m.BlockHash) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) - } - if m.TxIndex != 0 { - n += 1 + sovTx(uint64(m.TxIndex)) - } return n } @@ -4668,93 +4549,6 @@ func (m *MsgAddInboundTracker) Unmarshal(dAtA []byte) error { break } } - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthTx - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthTx - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Proof == nil { - m.Proof = &proofs.Proof{} - } - if err := m.Proof.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 6: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field BlockHash", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthTx - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthTx - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.BlockHash = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 7: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field TxIndex", wireType) - } - m.TxIndex = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.TxIndex |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) @@ -5306,93 +5100,6 @@ func (m *MsgAddOutboundTracker) Unmarshal(dAtA []byte) error { } m.TxHash = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthTx - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthTx - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Proof == nil { - m.Proof = &proofs.Proof{} - } - if err := m.Proof.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 6: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field BlockHash", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthTx - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthTx - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.BlockHash = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 7: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field TxIndex", wireType) - } - m.TxIndex = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.TxIndex |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) diff --git a/zetaclient/chains/bitcoin/signer/signer.go b/zetaclient/chains/bitcoin/signer/signer.go index 90257e019e..1c08dcdcd6 100644 --- a/zetaclient/chains/bitcoin/signer/signer.go +++ b/zetaclient/chains/bitcoin/signer/signer.go @@ -469,14 +469,11 @@ func (signer *Signer) TryProcessOutbound( } logger.Info(). Msgf("Broadcast success: nonce %d to chain %s outboundHash %s", outboundTssNonce, chain.String(), outboundHash) - zetaHash, err := zetacoreClient.AddOutboundTracker( + zetaHash, err := zetacoreClient.PostOutboundTracker( ctx, chain.ChainId, outboundTssNonce, outboundHash, - nil, - "", - -1, ) if err != nil { logger.Err(err). diff --git a/zetaclient/chains/evm/signer/outbound_tracker_reporter.go b/zetaclient/chains/evm/signer/outbound_tracker_reporter.go index d4562cee9b..8be76f66a4 100644 --- a/zetaclient/chains/evm/signer/outbound_tracker_reporter.go +++ b/zetaclient/chains/evm/signer/outbound_tracker_reporter.go @@ -70,7 +70,7 @@ func (signer *Signer) reportToOutboundTracker( } // report outbound hash to tracker - zetaHash, err := zetacoreClient.AddOutboundTracker(ctx, chainID, nonce, outboundHash, nil, "", -1) + zetaHash, err := zetacoreClient.PostOutboundTracker(ctx, chainID, nonce, outboundHash) if err != nil { logger.Err(err).Msg("error adding outbound to tracker") } else if zetaHash != "" { diff --git a/zetaclient/chains/interfaces/interfaces.go b/zetaclient/chains/interfaces/interfaces.go index c89a77e4b8..0043d0a7a9 100644 --- a/zetaclient/chains/interfaces/interfaces.go +++ b/zetaclient/chains/interfaces/interfaces.go @@ -21,9 +21,7 @@ import ( "gitlab.com/thorchain/tss/go-tss/blame" "github.com/zeta-chain/node/pkg/chains" - "github.com/zeta-chain/node/pkg/proofs" 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" keyinterfaces "github.com/zeta-chain/node/zetaclient/keys/interfaces" "github.com/zeta-chain/node/zetaclient/outboundprocessor" @@ -75,15 +73,7 @@ type ChainSigner interface { GetGatewayAddress() string } -// ZetacoreVoter represents voter interface. type ZetacoreVoter interface { - PostVoteBlockHeader( - ctx context.Context, - chainID int64, - txhash []byte, - height int64, - header proofs.HeaderData, - ) (string, error) PostVoteGasPrice( ctx context.Context, chain chains.Chain, @@ -117,7 +107,6 @@ type ZetacoreClient interface { GetTSSHistory(ctx context.Context) ([]observertypes.TSS, error) GetBlockHeight(ctx context.Context) (int64, error) - GetBlockHeaderChainState(ctx context.Context, chainID int64) (*lightclienttypes.ChainState, error) ListPendingCCTX(ctx context.Context, chainID int64) ([]*crosschaintypes.CrossChainTx, uint64, error) ListPendingCCTXWithinRateLimit( @@ -141,16 +130,7 @@ type ZetacoreClient interface { GetZetaHotKeyBalance(ctx context.Context) (sdkmath.Int, error) GetInboundTrackersForChain(ctx context.Context, chainID int64) ([]crosschaintypes.InboundTracker, error) - // todo(revamp): refactor input to struct - AddOutboundTracker( - ctx context.Context, - chainID int64, - nonce uint64, - txHash string, - proof *proofs.Proof, - blockHash string, - txIndex int64, - ) (string, error) + PostOutboundTracker(ctx context.Context, chainID int64, nonce uint64, txHash string) (string, error) Stop() OnBeforeStop(callback func()) diff --git a/zetaclient/chains/solana/signer/outbound_tracker_reporter.go b/zetaclient/chains/solana/signer/outbound_tracker_reporter.go index 74d2b7eb52..3fc8acf181 100644 --- a/zetaclient/chains/solana/signer/outbound_tracker_reporter.go +++ b/zetaclient/chains/solana/signer/outbound_tracker_reporter.go @@ -81,7 +81,7 @@ func (signer *Signer) reportToOutboundTracker( } // report outbound hash to zetacore - zetaHash, err := zetacoreClient.AddOutboundTracker(ctx, chainID, nonce, txSig.String(), nil, "", -1) + zetaHash, err := zetacoreClient.PostOutboundTracker(ctx, chainID, nonce, txSig.String()) if err != nil { logger.Err(err).Msg("error adding outbound to tracker") } else if zetaHash != "" { diff --git a/zetaclient/chains/ton/observer/observer_test.go b/zetaclient/chains/ton/observer/observer_test.go index 290e34081a..45a79f65ec 100644 --- a/zetaclient/chains/ton/observer/observer_test.go +++ b/zetaclient/chains/ton/observer/observer_test.go @@ -251,10 +251,7 @@ func setupTrackersBag(ts *testSuite) { } ts.zetacore.On( - "AddOutboundTracker", - mock.Anything, - mock.Anything, - mock.Anything, + "PostOutboundTracker", mock.Anything, mock.Anything, mock.Anything, diff --git a/zetaclient/chains/ton/observer/outbound.go b/zetaclient/chains/ton/observer/outbound.go index b4a466bcf2..deacb8e359 100644 --- a/zetaclient/chains/ton/observer/outbound.go +++ b/zetaclient/chains/ton/observer/outbound.go @@ -208,9 +208,7 @@ func (ob *Observer) addOutboundTracker(ctx context.Context, tx *toncontracts.Tra ) // note it has a check for noop - _, err = ob. - ZetacoreClient(). - AddOutboundTracker(ctx, chainID, nonce, hash, nil, "", 0) + _, err = ob.ZetacoreClient().PostOutboundTracker(ctx, chainID, nonce, hash) return err } diff --git a/zetaclient/chains/ton/signer/signer_test.go b/zetaclient/chains/ton/signer/signer_test.go index dcd78c9817..f491b59391 100644 --- a/zetaclient/chains/ton/signer/signer_test.go +++ b/zetaclient/chains/ton/signer/signer_test.go @@ -246,10 +246,7 @@ func setupTrackersBag(ts *testSuite) { } ts.zetacore.On( - "AddOutboundTracker", - mock.Anything, - mock.Anything, - mock.Anything, + "PostOutboundTracker", mock.Anything, mock.Anything, mock.Anything, diff --git a/zetaclient/chains/ton/signer/signer_tracker.go b/zetaclient/chains/ton/signer/signer_tracker.go index 065d0f8204..e698830269 100644 --- a/zetaclient/chains/ton/signer/signer_tracker.go +++ b/zetaclient/chains/ton/signer/signer_tracker.go @@ -64,7 +64,7 @@ func (s *Signer) trackOutbound( } // Note that this method has a check for noop - _, err = zetacore.AddOutboundTracker(ctx, chainID, nonce, txHash, nil, "", 0) + _, err = zetacore.PostOutboundTracker(ctx, chainID, nonce, txHash) if err != nil { return errors.Wrap(err, "unable to add outbound tracker") } diff --git a/zetaclient/testutils/mocks/zetacore_client.go b/zetaclient/testutils/mocks/zetacore_client.go index 864b130560..5bd9b685f9 100644 --- a/zetaclient/testutils/mocks/zetacore_client.go +++ b/zetaclient/testutils/mocks/zetacore_client.go @@ -12,16 +12,12 @@ import ( keysinterfaces "github.com/zeta-chain/node/zetaclient/keys/interfaces" - lightclienttypes "github.com/zeta-chain/node/x/lightclient/types" - math "cosmossdk.io/math" mock "github.com/stretchr/testify/mock" observertypes "github.com/zeta-chain/node/x/observer/types" - proofs "github.com/zeta-chain/node/pkg/proofs" - types "github.com/zeta-chain/node/x/crosschain/types" zerolog "github.com/rs/zerolog" @@ -32,34 +28,6 @@ type ZetacoreClient struct { mock.Mock } -// AddOutboundTracker provides a mock function with given fields: ctx, chainID, nonce, txHash, proof, blockHash, txIndex -func (_m *ZetacoreClient) AddOutboundTracker(ctx context.Context, chainID int64, nonce uint64, txHash string, proof *proofs.Proof, blockHash string, txIndex int64) (string, error) { - ret := _m.Called(ctx, chainID, nonce, txHash, proof, blockHash, txIndex) - - if len(ret) == 0 { - panic("no return value specified for AddOutboundTracker") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, int64, uint64, string, *proofs.Proof, string, int64) (string, error)); ok { - return rf(ctx, chainID, nonce, txHash, proof, blockHash, txIndex) - } - if rf, ok := ret.Get(0).(func(context.Context, int64, uint64, string, *proofs.Proof, string, int64) string); ok { - r0 = rf(ctx, chainID, nonce, txHash, proof, blockHash, txIndex) - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func(context.Context, int64, uint64, string, *proofs.Proof, string, int64) error); ok { - r1 = rf(ctx, chainID, nonce, txHash, proof, blockHash, txIndex) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // Chain provides a mock function with given fields: func (_m *ZetacoreClient) Chain() chains.Chain { ret := _m.Called() @@ -136,36 +104,6 @@ func (_m *ZetacoreClient) GetBTCTSSAddress(ctx context.Context, chainID int64) ( return r0, r1 } -// GetBlockHeaderChainState provides a mock function with given fields: ctx, chainID -func (_m *ZetacoreClient) GetBlockHeaderChainState(ctx context.Context, chainID int64) (*lightclienttypes.ChainState, error) { - ret := _m.Called(ctx, chainID) - - if len(ret) == 0 { - panic("no return value specified for GetBlockHeaderChainState") - } - - var r0 *lightclienttypes.ChainState - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, int64) (*lightclienttypes.ChainState, error)); ok { - return rf(ctx, chainID) - } - if rf, ok := ret.Get(0).(func(context.Context, int64) *lightclienttypes.ChainState); ok { - r0 = rf(ctx, chainID) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*lightclienttypes.ChainState) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok { - r1 = rf(ctx, chainID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // GetBlockHeight provides a mock function with given fields: ctx func (_m *ZetacoreClient) GetBlockHeight(ctx context.Context) (int64, error) { ret := _m.Called(ctx) @@ -654,27 +592,27 @@ func (_m *ZetacoreClient) OnBeforeStop(callback func()) { _m.Called(callback) } -// PostVoteBlameData provides a mock function with given fields: ctx, _a1, chainID, index -func (_m *ZetacoreClient) PostVoteBlameData(ctx context.Context, _a1 *blame.Blame, chainID int64, index string) (string, error) { - ret := _m.Called(ctx, _a1, chainID, index) +// 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) if len(ret) == 0 { - panic("no return value specified for PostVoteBlameData") + panic("no return value specified for PostOutboundTracker") } var r0 string var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *blame.Blame, int64, string) (string, error)); ok { - return rf(ctx, _a1, chainID, index) + if rf, ok := ret.Get(0).(func(context.Context, int64, uint64, string) (string, error)); ok { + return rf(ctx, chainID, nonce, txHash) } - if rf, ok := ret.Get(0).(func(context.Context, *blame.Blame, int64, string) string); ok { - r0 = rf(ctx, _a1, chainID, index) + if rf, ok := ret.Get(0).(func(context.Context, int64, uint64, string) string); ok { + r0 = rf(ctx, chainID, nonce, txHash) } else { r0 = ret.Get(0).(string) } - if rf, ok := ret.Get(1).(func(context.Context, *blame.Blame, int64, string) error); ok { - r1 = rf(ctx, _a1, chainID, index) + if rf, ok := ret.Get(1).(func(context.Context, int64, uint64, string) error); ok { + r1 = rf(ctx, chainID, nonce, txHash) } else { r1 = ret.Error(1) } @@ -682,27 +620,27 @@ func (_m *ZetacoreClient) PostVoteBlameData(ctx context.Context, _a1 *blame.Blam return r0, r1 } -// PostVoteBlockHeader provides a mock function with given fields: ctx, chainID, txhash, height, header -func (_m *ZetacoreClient) PostVoteBlockHeader(ctx context.Context, chainID int64, txhash []byte, height int64, header proofs.HeaderData) (string, error) { - ret := _m.Called(ctx, chainID, txhash, height, header) +// PostVoteBlameData provides a mock function with given fields: ctx, _a1, chainID, index +func (_m *ZetacoreClient) PostVoteBlameData(ctx context.Context, _a1 *blame.Blame, chainID int64, index string) (string, error) { + ret := _m.Called(ctx, _a1, chainID, index) if len(ret) == 0 { - panic("no return value specified for PostVoteBlockHeader") + panic("no return value specified for PostVoteBlameData") } var r0 string var r1 error - if rf, ok := ret.Get(0).(func(context.Context, int64, []byte, int64, proofs.HeaderData) (string, error)); ok { - return rf(ctx, chainID, txhash, height, header) + if rf, ok := ret.Get(0).(func(context.Context, *blame.Blame, int64, string) (string, error)); ok { + return rf(ctx, _a1, chainID, index) } - if rf, ok := ret.Get(0).(func(context.Context, int64, []byte, int64, proofs.HeaderData) string); ok { - r0 = rf(ctx, chainID, txhash, height, header) + if rf, ok := ret.Get(0).(func(context.Context, *blame.Blame, int64, string) string); ok { + r0 = rf(ctx, _a1, chainID, index) } else { r0 = ret.Get(0).(string) } - if rf, ok := ret.Get(1).(func(context.Context, int64, []byte, int64, proofs.HeaderData) error); ok { - r1 = rf(ctx, chainID, txhash, height, header) + if rf, ok := ret.Get(1).(func(context.Context, *blame.Blame, int64, string) error); ok { + r1 = rf(ctx, _a1, chainID, index) } else { r1 = ret.Error(1) } diff --git a/zetaclient/zetacore/broadcast_test.go b/zetaclient/zetacore/broadcast_test.go index 97f335abfd..3fb5093963 100644 --- a/zetaclient/zetacore/broadcast_test.go +++ b/zetaclient/zetacore/broadcast_test.go @@ -2,7 +2,6 @@ package zetacore import ( "context" - "encoding/hex" "errors" "net" "testing" @@ -16,7 +15,6 @@ import ( "github.com/zeta-chain/node/pkg/chains" crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" - observerTypes "github.com/zeta-chain/node/x/observer/types" "github.com/zeta-chain/node/zetaclient/keys" "github.com/zeta-chain/node/zetaclient/testutils/mocks" ) @@ -84,15 +82,7 @@ func TestBroadcast(t *testing.T) { withTendermint(mocks.NewSDKClientWithErr(t, nil, 0)), ) - blockHash, err := hex.DecodeString(ethBlockHash) - require.NoError(t, err) - msg := observerTypes.NewMsgVoteBlockHeader( - address.String(), - chains.Ethereum.ChainId, - blockHash, - 18495266, - getHeaderData(t), - ) + msg := crosschaintypes.NewMsgVoteGasPrice(address.String(), chains.Ethereum.ChainId, 10000, 1000, 1) authzMsg, authzSigner, err := WrapMessageWithAuthz(msg) require.NoError(t, err) @@ -108,15 +98,7 @@ func TestBroadcast(t *testing.T) { ), ) - blockHash, err := hex.DecodeString(ethBlockHash) - require.NoError(t, err) - msg := observerTypes.NewMsgVoteBlockHeader( - address.String(), - chains.Ethereum.ChainId, - blockHash, - 18495266, - getHeaderData(t), - ) + msg := crosschaintypes.NewMsgVoteGasPrice(address.String(), chains.Ethereum.ChainId, 10000, 1000, 1) authzMsg, authzSigner, err := WrapMessageWithAuthz(msg) require.NoError(t, err) diff --git a/zetaclient/zetacore/client_vote.go b/zetaclient/zetacore/client_vote.go index a6eec263c4..491ec892e0 100644 --- a/zetaclient/zetacore/client_vote.go +++ b/zetaclient/zetacore/client_vote.go @@ -7,7 +7,6 @@ import ( "gitlab.com/thorchain/tss/go-tss/blame" "github.com/zeta-chain/node/pkg/chains" - "github.com/zeta-chain/node/pkg/proofs" "github.com/zeta-chain/node/pkg/retry" "github.com/zeta-chain/node/x/crosschain/types" observerclient "github.com/zeta-chain/node/x/observer/client/cli" @@ -15,34 +14,6 @@ import ( zctx "github.com/zeta-chain/node/zetaclient/context" ) -// PostVoteBlockHeader posts a vote on an observed block header -func (c *Client) PostVoteBlockHeader( - ctx context.Context, - chainID int64, - blockHash []byte, - height int64, - header proofs.HeaderData, -) (string, error) { - signerAddress := c.keys.GetOperatorAddress().String() - - msg := observertypes.NewMsgVoteBlockHeader(signerAddress, chainID, blockHash, height, header) - - authzMsg, authzSigner, err := WrapMessageWithAuthz(msg) - if err != nil { - return "", err - } - - zetaTxHash, err := retry.DoTypedWithRetry(func() (string, error) { - return c.Broadcast(ctx, DefaultGasLimit, authzMsg, authzSigner) - }) - - if err != nil { - return "", errors.Wrap(err, "unable to broadcast vote block header") - } - - return zetaTxHash, nil -} - // PostVoteGasPrice posts a gas price vote. Returns txHash and error. func (c *Client) PostVoteGasPrice( ctx context.Context, diff --git a/zetaclient/zetacore/tx.go b/zetaclient/zetacore/tx.go index bb7b106fd5..59fbc92edd 100644 --- a/zetaclient/zetacore/tx.go +++ b/zetaclient/zetacore/tx.go @@ -11,7 +11,6 @@ import ( "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/coin" - "github.com/zeta-chain/node/pkg/proofs" "github.com/zeta-chain/node/x/crosschain/types" clientauthz "github.com/zeta-chain/node/zetaclient/authz" clientcommon "github.com/zeta-chain/node/zetaclient/common" @@ -84,17 +83,8 @@ func WrapMessageWithAuthz(msg sdk.Msg) (sdk.Msg, clientauthz.Signer, error) { return &authzMessage, authzSigner, nil } -// AddOutboundTracker adds an outbound tracker -// TODO(revamp): rename to PostAddOutboundTracker -func (c *Client) AddOutboundTracker( - ctx context.Context, - chainID int64, - nonce uint64, - txHash string, - proof *proofs.Proof, - blockHash string, - txIndex int64, -) (string, error) { +// PostOutboundTracker adds an outbound tracker +func (c *Client) PostOutboundTracker(ctx context.Context, chainID int64, nonce uint64, txHash string) (string, error) { // don't report if the tracker already contains the txHash tracker, err := c.GetOutboundTracker(ctx, chains.Chain{ChainId: chainID}, nonce) if err == nil { @@ -106,7 +96,7 @@ func (c *Client) AddOutboundTracker( } signerAddress := c.keys.GetOperatorAddress().String() - msg := types.NewMsgAddOutboundTracker(signerAddress, chainID, nonce, txHash, proof, blockHash, txIndex) + msg := types.NewMsgAddOutboundTracker(signerAddress, chainID, nonce, txHash) authzMsg, authzSigner, err := WrapMessageWithAuthz(msg) if err != nil { diff --git a/zetaclient/zetacore/tx_test.go b/zetaclient/zetacore/tx_test.go index b58981f67b..f1cac183f1 100644 --- a/zetaclient/zetacore/tx_test.go +++ b/zetaclient/zetacore/tx_test.go @@ -1,11 +1,8 @@ package zetacore import ( - "bytes" "context" - "encoding/hex" "net" - "os" "testing" "github.com/zeta-chain/node/testutil/sample" @@ -14,7 +11,6 @@ import ( "cosmossdk.io/math" sdktypes "github.com/cosmos/cosmos-sdk/types" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/stretchr/testify/assert" @@ -26,7 +22,6 @@ import ( "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/coin" - "github.com/zeta-chain/node/pkg/proofs" 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" @@ -106,22 +101,6 @@ func Test_GasPriceMultiplier(t *testing.T) { } } -func getHeaderData(t *testing.T) proofs.HeaderData { - var header ethtypes.Header - file, err := os.Open("../../testutil/testdata/eth_header_18495266.json") - require.NoError(t, err) - defer file.Close() - headerBytes := make([]byte, 4096) - n, err := file.Read(headerBytes) - require.NoError(t, err) - err = header.UnmarshalJSON(headerBytes[:n]) - require.NoError(t, err) - var buffer bytes.Buffer - err = header.EncodeRLP(&buffer) - require.NoError(t, err) - return proofs.NewEthereumHeader(buffer.Bytes()) -} - func TestZetacore_PostGasPrice(t *testing.T) { ctx := context.Background() @@ -183,14 +162,14 @@ func TestZetacore_AddOutboundTracker(t *testing.T) { t.Run("add tx hash success", func(t *testing.T) { tendermintMock.SetBroadcastTxHash(sampleHash) - hash, err := client.AddOutboundTracker(ctx, chainID, nonce, "", nil, "", 456) + hash, err := client.PostOutboundTracker(ctx, chainID, nonce, "") assert.NoError(t, err) assert.Equal(t, sampleHash, hash) }) t.Run("add tx hash fail", func(t *testing.T) { tendermintMock.SetError(errors.New("broadcast error")) - hash, err := client.AddOutboundTracker(ctx, chainID, nonce, "", nil, "", 456) + hash, err := client.PostOutboundTracker(ctx, chainID, nonce, "") assert.Error(t, err) assert.Empty(t, hash) }) @@ -391,34 +370,6 @@ func TestZetacore_PostBlameData(t *testing.T) { }) } -func TestZetacore_PostVoteBlockHeader(t *testing.T) { - ctx := context.Background() - - extraGRPC := withDummyServer(100) - setupMockServer(t, observertypes.RegisterQueryServer, skipMethod, nil, nil, extraGRPC...) - - client := setupZetacoreClient(t, - withDefaultObserverKeys(), - withAccountRetriever(t, 100, 100), - withTendermint(mocks.NewSDKClientWithErr(t, nil, 0).SetBroadcastTxHash(sampleHash)), - ) - - blockHash, err := hex.DecodeString(ethBlockHash) - require.NoError(t, err) - - t.Run("post add block header success", func(t *testing.T) { - hash, err := client.PostVoteBlockHeader( - ctx, - chains.Ethereum.ChainId, - blockHash, - 18495266, - getHeaderData(t), - ) - require.NoError(t, err) - require.Equal(t, sampleHash, hash) - }) -} - func TestZetacore_PostVoteInbound(t *testing.T) { ctx := context.Background() From 13cfffed704a9ff80fcd3813b231b8308b5db0c6 Mon Sep 17 00:00:00 2001 From: skosito Date: Fri, 8 Nov 2024 14:31:00 +0000 Subject: [PATCH 08/10] feat: integrate SPL deposits (#3124) * deposit spl integration and start with e2e test * test wip for deposit spl and call * fix up creation of ata * add balance assertions for deposit spl e2e tests * CI fixes * lint fix * add inbound parse unit test and cleanup * comment * PR comments * move inbound parsing to solana pkg * refactor e2e test zrc20 deployment * fix e2e tests * Update changelog.md Co-authored-by: Lucas Bertrand --------- Co-authored-by: Dmitry S <11892559+swift1337@users.noreply.github.com> Co-authored-by: Lucas Bertrand --- changelog.md | 1 + cmd/zetae2e/config/config.go | 4 +- cmd/zetae2e/config/contracts.go | 15 ++ cmd/zetae2e/local/local.go | 20 +- cmd/zetae2e/stress.go | 6 +- e2e/config/config.go | 6 +- e2e/e2etests/e2etests.go | 18 ++ e2e/e2etests/test_solana_whitelist_spl.go | 3 +- e2e/e2etests/test_spl_deposit.go | 66 ++++++ e2e/e2etests/test_spl_deposit_and_call.go | 76 +++++++ e2e/runner/runner.go | 6 + e2e/runner/setup_solana.go | 4 + e2e/runner/setup_zeta.go | 17 +- e2e/runner/solana.go | 200 ++++++++++++++++-- e2e/txserver/zeta_tx_server.go | 79 +++++-- pkg/contracts/solana/gateway.go | 6 +- pkg/contracts/solana/inbound.go | 127 +++++++++++ pkg/contracts/solana/inbound_test.go | 105 +++++++++ pkg/contracts/solana/instruction.go | 12 ++ ...axBorK9q1JFVbqnAvu9jXm6ertj7kT7HpYw1j.json | 64 ++++++ ...x5z5JfXs5ed8LZqpXUy4VijoU3x15mBd66ZGE.json | 93 ++++++++ x/fungible/keeper/foreign_coins.go | 9 +- x/fungible/keeper/foreign_coins_test.go | 18 -- zetaclient/chains/solana/observer/inbound.go | 134 +++--------- .../chains/solana/observer/inbound_test.go | 44 ---- 25 files changed, 907 insertions(+), 226 deletions(-) create mode 100644 e2e/e2etests/test_spl_deposit.go create mode 100644 e2e/e2etests/test_spl_deposit_and_call.go create mode 100644 pkg/contracts/solana/inbound.go create mode 100644 pkg/contracts/solana/inbound_test.go create mode 100644 pkg/contracts/solana/testdata/MS3MPLN7hkbyCZFwKqXcg8fmEvQMD74fN6Ps2LSWXJoRxPW5ehaxBorK9q1JFVbqnAvu9jXm6ertj7kT7HpYw1j.json create mode 100644 pkg/contracts/solana/testdata/aY8yLDze6nHSRi7L5REozKAZY1aAyPJ6TfibiqQL5JGwgSBkYux5z5JfXs5ed8LZqpXUy4VijoU3x15mBd66ZGE.json diff --git a/changelog.md b/changelog.md index 074a2877aa..d71bded0a0 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,7 @@ ### Features * [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 ### Tests * [3075](https://github.com/zeta-chain/node/pull/3075) - ton: withdraw concurrent, deposit & revert. diff --git a/cmd/zetae2e/config/config.go b/cmd/zetae2e/config/config.go index 0cea78af39..65e5418f53 100644 --- a/cmd/zetae2e/config/config.go +++ b/cmd/zetae2e/config/config.go @@ -54,9 +54,10 @@ 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.SPLAddr = config.DoubleQuotedString(r.SPLAddr.String()) - // copy contracts from deployer runner conf.Contracts.EVM.ZetaEthAddr = config.DoubleQuotedString(r.ZetaEthAddr.Hex()) conf.Contracts.EVM.ConnectorEthAddr = config.DoubleQuotedString(r.ConnectorEthAddr.Hex()) conf.Contracts.EVM.CustodyAddr = config.DoubleQuotedString(r.ERC20CustodyAddr.Hex()) @@ -68,6 +69,7 @@ func ExportContractsFromRunner(r *runner.E2ERunner, conf config.Config) config.C conf.Contracts.ZEVM.ERC20ZRC20Addr = config.DoubleQuotedString(r.ERC20ZRC20Addr.Hex()) conf.Contracts.ZEVM.BTCZRC20Addr = config.DoubleQuotedString(r.BTCZRC20Addr.Hex()) conf.Contracts.ZEVM.SOLZRC20Addr = config.DoubleQuotedString(r.SOLZRC20Addr.Hex()) + conf.Contracts.ZEVM.SPLZRC20Addr = config.DoubleQuotedString(r.SPLZRC20Addr.Hex()) conf.Contracts.ZEVM.TONZRC20Addr = config.DoubleQuotedString(r.TONZRC20Addr.Hex()) conf.Contracts.ZEVM.UniswapFactoryAddr = config.DoubleQuotedString(r.UniswapV2FactoryAddr.Hex()) conf.Contracts.ZEVM.UniswapRouterAddr = config.DoubleQuotedString(r.UniswapV2RouterAddr.Hex()) diff --git a/cmd/zetae2e/config/contracts.go b/cmd/zetae2e/config/contracts.go index 6f2dff72c6..5c46cdc047 100644 --- a/cmd/zetae2e/config/contracts.go +++ b/cmd/zetae2e/config/contracts.go @@ -34,6 +34,10 @@ func setContractsFromConfig(r *runner.E2ERunner, conf config.Config) error { r.GatewayProgram = solana.MustPublicKeyFromBase58(c) } + if c := conf.Contracts.Solana.SPLAddr; c != "" { + r.SPLAddr = solana.MustPublicKeyFromBase58(c.String()) + } + // set EVM contracts if c := conf.Contracts.EVM.ZetaEthAddr; c != "" { r.ZetaEthAddr, err = c.AsEVMAddress() @@ -135,6 +139,17 @@ func setContractsFromConfig(r *runner.E2ERunner, conf config.Config) error { } } + if c := conf.Contracts.ZEVM.SPLZRC20Addr; c != "" { + r.SPLZRC20Addr, err = c.AsEVMAddress() + if err != nil { + return fmt.Errorf("invalid SPLZRC20Addr: %w", err) + } + r.SPLZRC20, err = zrc20.NewZRC20(r.SPLZRC20Addr, r.ZEVMClient) + if err != nil { + return err + } + } + if c := conf.Contracts.ZEVM.TONZRC20Addr; c != "" { r.TONZRC20Addr, err = c.AsEVMAddress() if err != nil { diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index e7b1ee3e29..9b818ea8c0 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -222,12 +222,24 @@ func localE2ETest(cmd *cobra.Command, _ []string) { deployerRunner.SetupEVMV2() + if testSolana { + deployerRunner.SetupSolana(conf.AdditionalAccounts.UserSolana.SolanaPrivateKey.String()) + } + deployerRunner.SetZEVMSystemContracts() // NOTE: v2 (gateway) setup called here because system contract needs to be set first, then gateway, then zrc20 deployerRunner.SetZEVMContractsV2() - deployerRunner.SetZEVMZRC20s() + zrc20Deployment := txserver.ZRC20Deployment{ + ERC20Addr: deployerRunner.ERC20Addr, + SPLAddr: nil, + } + if testSolana { + zrc20Deployment.SPLAddr = deployerRunner.SPLAddr.ToPointer() + } + + deployerRunner.SetZEVMZRC20s(zrc20Deployment) // Update the chain params to use v2 contract for ERC20Custody // TODO: this function should be removed and the chain params should be directly set to use v2 contract @@ -235,10 +247,6 @@ func localE2ETest(cmd *cobra.Command, _ []string) { deployerRunner.UpdateChainParamsV2Contracts() deployerRunner.ERC20CustodyAddr = deployerRunner.ERC20CustodyV2Addr - if testSolana { - deployerRunner.SetupSolana(conf.AdditionalAccounts.UserSolana.SolanaPrivateKey.String()) - } - deployerRunner.MintERC20OnEvm(1000000) logger.Print("✅ setup completed in %s", time.Since(startTime)) @@ -417,6 +425,8 @@ func localE2ETest(cmd *cobra.Command, _ []string) { // TODO move under admin tests // https://github.com/zeta-chain/node/issues/3085 e2etests.TestSolanaWhitelistSPLName, + e2etests.TestSPLDepositName, + e2etests.TestSPLDepositAndCallName, } eg.Go(solanaTestRoutine(conf, deployerRunner, verbose, solanaTests...)) } diff --git a/cmd/zetae2e/stress.go b/cmd/zetae2e/stress.go index 66e8cd700d..b2ab4a9154 100644 --- a/cmd/zetae2e/stress.go +++ b/cmd/zetae2e/stress.go @@ -22,6 +22,7 @@ import ( zetae2econfig "github.com/zeta-chain/node/cmd/zetae2e/config" "github.com/zeta-chain/node/cmd/zetae2e/local" "github.com/zeta-chain/node/e2e/runner" + "github.com/zeta-chain/node/e2e/txserver" "github.com/zeta-chain/node/e2e/utils" "github.com/zeta-chain/node/testutil" crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" @@ -142,7 +143,10 @@ func StressTest(cmd *cobra.Command, _ []string) { case "LOCAL": // deploy and set zevm contract e2eTest.SetZEVMSystemContracts() - e2eTest.SetZEVMZRC20s() + e2eTest.SetZEVMZRC20s(txserver.ZRC20Deployment{ + ERC20Addr: e2eTest.ERC20Addr, + SPLAddr: nil, // no stress tests for solana atm + }) // deposit on ZetaChain e2eTest.DepositEther() diff --git a/e2e/config/config.go b/e2e/config/config.go index 67685b0dd3..15ca4a1f2c 100644 --- a/e2e/config/config.go +++ b/e2e/config/config.go @@ -117,9 +117,10 @@ type Contracts struct { Solana Solana `yaml:"solana"` } -// Solana contains the addresses of predeployed contracts on the Solana chain +// Solana contains the addresses of predeployed contracts and accounts on the Solana chain type Solana struct { - GatewayProgramID string `yaml:"gateway_program_id"` + GatewayProgramID string `yaml:"gateway_program_id"` + SPLAddr DoubleQuotedString `yaml:"spl"` } // EVM contains the addresses of predeployed contracts on the EVM chain @@ -141,6 +142,7 @@ type ZEVM struct { ERC20ZRC20Addr DoubleQuotedString `yaml:"erc20_zrc20"` BTCZRC20Addr DoubleQuotedString `yaml:"btc_zrc20"` SOLZRC20Addr DoubleQuotedString `yaml:"sol_zrc20"` + SPLZRC20Addr DoubleQuotedString `yaml:"spl_zrc20"` TONZRC20Addr DoubleQuotedString `yaml:"ton_zrc20"` UniswapFactoryAddr DoubleQuotedString `yaml:"uniswap_factory"` UniswapRouterAddr DoubleQuotedString `yaml:"uniswap_router"` diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index 842abecbe4..a880172830 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -61,6 +61,8 @@ const ( TestSolanaDepositAndCallRefundName = "solana_deposit_and_call_refund" TestSolanaDepositRestrictedName = "solana_deposit_restricted" TestSolanaWithdrawRestrictedName = "solana_withdraw_restricted" + TestSPLDepositName = "spl_deposit" + TestSPLDepositAndCallName = "spl_deposit_and_call" /** * TON tests @@ -463,6 +465,22 @@ var AllE2ETests = []runner.E2ETest{ []runner.ArgDefinition{}, TestSolanaWhitelistSPL, ), + runner.NewE2ETest( + TestSPLDepositName, + "deposit SPL into ZEVM", + []runner.ArgDefinition{ + {Description: "amount of spl tokens", DefaultValue: "500000"}, + }, + TestSPLDeposit, + ), + runner.NewE2ETest( + TestSPLDepositAndCallName, + "deposit SPL into ZEVM and call", + []runner.ArgDefinition{ + {Description: "amount of spl tokens", DefaultValue: "500000"}, + }, + TestSPLDepositAndCall, + ), /* TON tests */ diff --git a/e2e/e2etests/test_solana_whitelist_spl.go b/e2e/e2etests/test_solana_whitelist_spl.go index 259657f72b..fadff22805 100644 --- a/e2e/e2etests/test_solana_whitelist_spl.go +++ b/e2e/e2etests/test_solana_whitelist_spl.go @@ -19,7 +19,8 @@ func TestSolanaWhitelistSPL(r *runner.E2ERunner, _ []string) { privkey, err := solana.PrivateKeyFromBase58(r.Account.SolanaPrivateKey.String()) require.NoError(r, err) - spl := r.DeploySPL(&privkey) + // deploy SPL token, but don't whitelist in gateway + spl := r.DeploySPL(&privkey, false) // check that whitelist entry doesn't exist for this spl seed := [][]byte{[]byte("whitelist"), spl.PublicKey().Bytes()} diff --git a/e2e/e2etests/test_spl_deposit.go b/e2e/e2etests/test_spl_deposit.go new file mode 100644 index 0000000000..ee5013d16c --- /dev/null +++ b/e2e/e2etests/test_spl_deposit.go @@ -0,0 +1,66 @@ +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 TestSPLDeposit(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + amount := parseInt(r, args[0]) + + // load deployer private key + privKey, err := solana.PrivateKeyFromBase58(r.Account.SolanaPrivateKey.String()) + require.NoError(r, err) + + // get SPL balance for pda and sender atas + pda := r.ComputePdaAddress() + pdaAta := r.FindOrCreateAssociatedTokenAccount(privKey, pda, r.SPLAddr) + + pdaBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, pdaAta, rpc.CommitmentConfirmed) + require.NoError(r, err) + + senderAta := r.FindOrCreateAssociatedTokenAccount(privKey, privKey.PublicKey(), r.SPLAddr) + senderBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, senderAta, rpc.CommitmentConfirmed) + require.NoError(r, err) + + // get zrc20 balance for recipient + zrc20BalanceBefore, err := r.SPLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) + require.NoError(r, err) + + // deposit SPL tokens + // #nosec G115 e2eTest - always in range + sig := r.SPLDepositAndCall(&privKey, uint64(amount), r.SPLAddr, r.EVMAddress(), nil) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, sig.String(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "solana_deposit_spl") + utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_OutboundMined) + + // verify balances are updated + pdaBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, pdaAta, rpc.CommitmentConfirmed) + require.NoError(r, err) + + senderBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, senderAta, rpc.CommitmentConfirmed) + require.NoError(r, err) + + zrc20BalanceAfter, err := r.SPLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) + require.NoError(r, err) + + // verify amount is deposited to pda ata + require.Equal(r, parseInt(r, pdaBalanceBefore.Value.Amount)+amount, parseInt(r, pdaBalanceAfter.Value.Amount)) + + // verify amount is subtracted from sender ata + require.Equal(r, parseInt(r, senderBalanceBefore.Value.Amount)-amount, parseInt(r, senderBalanceAfter.Value.Amount)) + + // verify amount is minted to receiver + require.Zero(r, zrc20BalanceBefore.Add(zrc20BalanceBefore, big.NewInt(int64(amount))).Cmp(zrc20BalanceAfter)) +} diff --git a/e2e/e2etests/test_spl_deposit_and_call.go b/e2e/e2etests/test_spl_deposit_and_call.go new file mode 100644 index 0000000000..cdfc94daa1 --- /dev/null +++ b/e2e/e2etests/test_spl_deposit_and_call.go @@ -0,0 +1,76 @@ +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" + testcontract "github.com/zeta-chain/node/testutil/contracts" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" +) + +func TestSPLDepositAndCall(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + amount := parseInt(r, args[0]) + + // deploy an example contract in ZEVM + contractAddr, _, contract, err := testcontract.DeployExample(r.ZEVMAuth, r.ZEVMClient) + require.NoError(r, err) + 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) + + // get SPL balance for pda and sender atas + pda := r.ComputePdaAddress() + pdaAta := r.FindOrCreateAssociatedTokenAccount(privKey, pda, r.SPLAddr) + + pdaBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, pdaAta, rpc.CommitmentConfirmed) + require.NoError(r, err) + + senderAta := r.FindOrCreateAssociatedTokenAccount(privKey, privKey.PublicKey(), r.SPLAddr) + senderBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, senderAta, rpc.CommitmentConfirmed) + require.NoError(r, err) + + // get zrc20 balance for recipient + zrc20BalanceBefore, err := r.SPLZRC20.BalanceOf(&bind.CallOpts{}, contractAddr) + require.NoError(r, err) + + // execute the deposit transaction + data := []byte("hello spl tokens") + // #nosec G115 e2eTest - always in range + sig := r.SPLDepositAndCall(&privKey, uint64(amount), r.SPLAddr, contractAddr, data) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, sig.String(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "solana_deposit_spl_and_call") + utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_OutboundMined) + + // check if example contract has been called, bar value should be set to amount + utils.MustHaveCalledExampleContract(r, contract, big.NewInt(int64(amount))) + + // verify balances are updated + pdaBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, pdaAta, rpc.CommitmentConfirmed) + require.NoError(r, err) + + senderBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, senderAta, rpc.CommitmentConfirmed) + require.NoError(r, err) + + zrc20BalanceAfter, err := r.SPLZRC20.BalanceOf(&bind.CallOpts{}, contractAddr) + require.NoError(r, err) + + // verify amount is deposited to pda ata + require.Equal(r, parseInt(r, pdaBalanceBefore.Value.Amount)+amount, parseInt(r, pdaBalanceAfter.Value.Amount)) + + // verify amount is subtracted from sender ata + require.Equal(r, parseInt(r, senderBalanceBefore.Value.Amount)-amount, parseInt(r, senderBalanceAfter.Value.Amount)) + + // verify amount is minted to receiver + require.Zero(r, zrc20BalanceBefore.Add(zrc20BalanceBefore, big.NewInt(int64(amount))).Cmp(zrc20BalanceAfter)) +} diff --git a/e2e/runner/runner.go b/e2e/runner/runner.go index cf7a8e6f02..ef68abe6a8 100644 --- a/e2e/runner/runner.go +++ b/e2e/runner/runner.go @@ -110,6 +110,7 @@ type E2ERunner struct { // programs on Solana GatewayProgram solana.PublicKey + SPLAddr solana.PublicKey // contracts evm ZetaEthAddr ethcommon.Address @@ -125,6 +126,8 @@ type E2ERunner struct { // contracts zevm ERC20ZRC20Addr ethcommon.Address ERC20ZRC20 *zrc20.ZRC20 + SPLZRC20Addr ethcommon.Address + SPLZRC20 *zrc20.ZRC20 ETHZRC20Addr ethcommon.Address ETHZRC20 *zrc20.ZRC20 BTCZRC20Addr ethcommon.Address @@ -366,6 +369,8 @@ func (r *E2ERunner) Unlock() { func (r *E2ERunner) PrintContractAddresses() { r.Logger.Print(" --- 📜Solana addresses ---") r.Logger.Print("GatewayProgram: %s", r.GatewayProgram.String()) + r.Logger.Print("SPL: %s", r.SPLAddr.String()) + // zevm contracts r.Logger.Print(" --- 📜zEVM contracts ---") r.Logger.Print("SystemContract: %s", r.SystemContractAddr.Hex()) @@ -373,6 +378,7 @@ func (r *E2ERunner) PrintContractAddresses() { r.Logger.Print("ERC20ZRC20: %s", r.ERC20ZRC20Addr.Hex()) r.Logger.Print("BTCZRC20: %s", r.BTCZRC20Addr.Hex()) r.Logger.Print("SOLZRC20: %s", r.SOLZRC20Addr.Hex()) + r.Logger.Print("SPLZRC20: %s", r.SPLZRC20Addr.Hex()) r.Logger.Print("TONZRC20: %s", r.TONZRC20Addr.Hex()) r.Logger.Print("UniswapFactory: %s", r.UniswapV2FactoryAddr.Hex()) r.Logger.Print("UniswapRouter: %s", r.UniswapV2RouterAddr.Hex()) diff --git a/e2e/runner/setup_solana.go b/e2e/runner/setup_solana.go index 73a571b2be..a7589d6af1 100644 --- a/e2e/runner/setup_solana.go +++ b/e2e/runner/setup_solana.go @@ -87,6 +87,10 @@ func (r *E2ERunner) SetupSolana(deployerPrivateKey string) { err = r.ensureSolanaChainParams() require.NoError(r, err) + + // deploy test spl + tokenAccount := r.DeploySPL(&privkey, true) + r.SPLAddr = tokenAccount.PublicKey() } func (r *E2ERunner) ensureSolanaChainParams() error { diff --git a/e2e/runner/setup_zeta.go b/e2e/runner/setup_zeta.go index 1d1db47108..072957346c 100644 --- a/e2e/runner/setup_zeta.go +++ b/e2e/runner/setup_zeta.go @@ -167,7 +167,7 @@ func (r *E2ERunner) SetZEVMSystemContracts() { } // SetZEVMZRC20s set ZRC20 for the ZEVM -func (r *E2ERunner) SetZEVMZRC20s() { +func (r *E2ERunner) SetZEVMZRC20s(zrc20Deployment txserver.ZRC20Deployment) { r.Logger.Print("⚙️ deploying ZRC20s on ZEVM") startTime := time.Now() defer func() { @@ -175,19 +175,24 @@ func (r *E2ERunner) SetZEVMZRC20s() { }() // deploy system contracts and ZRC20 contracts on ZetaChain - erc20zrc20Addr, err := r.ZetaTxServer.DeployZRC20s( - e2eutils.OperationalPolicyName, - e2eutils.AdminPolicyName, - r.ERC20Addr.Hex(), + deployedZRC20Addresses, err := r.ZetaTxServer.DeployZRC20s( + zrc20Deployment, r.skipChainOperations, ) require.NoError(r, err) // Set ERC20ZRC20Addr - r.ERC20ZRC20Addr = ethcommon.HexToAddress(erc20zrc20Addr) + r.ERC20ZRC20Addr = deployedZRC20Addresses.ERC20ZRC20Addr r.ERC20ZRC20, err = zrc20.NewZRC20(r.ERC20ZRC20Addr, r.ZEVMClient) require.NoError(r, err) + // Set SPLZRC20Addr if set + if deployedZRC20Addresses.SPLZRC20Addr != (ethcommon.Address{}) { + r.SPLZRC20Addr = deployedZRC20Addresses.SPLZRC20Addr + r.SPLZRC20, err = zrc20.NewZRC20(r.SPLZRC20Addr, r.ZEVMClient) + require.NoError(r, err) + } + // set ZRC20 contracts r.SetupETHZRC20() r.SetupBTCZRC20() diff --git a/e2e/runner/solana.go b/e2e/runner/solana.go index 24ea3c3b2f..542968938d 100644 --- a/e2e/runner/solana.go +++ b/e2e/runner/solana.go @@ -6,6 +6,7 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" "github.com/gagliardetto/solana-go" + associatedtokenaccount "github.com/gagliardetto/solana-go/programs/associated-token-account" "github.com/gagliardetto/solana-go/programs/system" "github.com/gagliardetto/solana-go/programs/token" "github.com/gagliardetto/solana-go/rpc" @@ -36,28 +37,77 @@ func (r *E2ERunner) CreateDepositInstruction( data []byte, amount uint64, ) solana.Instruction { - // compute the gateway PDA address - pdaComputed := r.ComputePdaAddress() - programID := r.GatewayProgram - - // create 'deposit' instruction - inst := &solana.GenericInstruction{} - accountSlice := []*solana.AccountMeta{} - accountSlice = append(accountSlice, solana.Meta(signer).WRITE().SIGNER()) - accountSlice = append(accountSlice, solana.Meta(pdaComputed).WRITE()) - accountSlice = append(accountSlice, solana.Meta(solana.SystemProgramID)) - inst.ProgID = programID - inst.AccountValues = accountSlice - - var err error - inst.DataBytes, err = borsh.Serialize(solanacontract.DepositInstructionParams{ + depositData, err := borsh.Serialize(solanacontract.DepositInstructionParams{ Discriminator: solanacontract.DiscriminatorDeposit, Amount: amount, Memo: append(receiver.Bytes(), data...), }) require.NoError(r, err) - return inst + return &solana.GenericInstruction{ + ProgID: r.GatewayProgram, + DataBytes: depositData, + AccountValues: []*solana.AccountMeta{ + solana.Meta(signer).WRITE().SIGNER(), + solana.Meta(r.ComputePdaAddress()).WRITE(), + solana.Meta(solana.SystemProgramID), + }, + } +} + +// CreateWhitelistSPLMintInstruction creates a 'whitelist_spl_mint' instruction +func (r *E2ERunner) CreateWhitelistSPLMintInstruction( + signer, whitelistEntry, whitelistCandidate solana.PublicKey, +) solana.Instruction { + data, err := borsh.Serialize(solanacontract.WhitelistInstructionParams{ + Discriminator: solanacontract.DiscriminatorWhitelistSplMint, + }) + require.NoError(r, err) + + return &solana.GenericInstruction{ + ProgID: r.GatewayProgram, + DataBytes: data, + AccountValues: []*solana.AccountMeta{ + solana.Meta(whitelistEntry).WRITE(), + solana.Meta(whitelistCandidate), + solana.Meta(r.ComputePdaAddress()).WRITE(), + solana.Meta(signer).WRITE().SIGNER(), + solana.Meta(solana.SystemProgramID), + }, + } +} + +// CreateDepositSPLInstruction creates a 'deposit_spl' instruction +func (r *E2ERunner) CreateDepositSPLInstruction( + amount uint64, + signer solana.PublicKey, + whitelistEntry solana.PublicKey, + mint solana.PublicKey, + from solana.PublicKey, + to solana.PublicKey, + receiver ethcommon.Address, + data []byte, +) solana.Instruction { + depositSPLData, err := borsh.Serialize(solanacontract.DepositInstructionParams{ + Discriminator: solanacontract.DiscriminatorDepositSPL, + Amount: amount, + Memo: append(receiver.Bytes(), data...), + }) + require.NoError(r, err) + + return &solana.GenericInstruction{ + ProgID: r.GatewayProgram, + DataBytes: depositSPLData, + AccountValues: []*solana.AccountMeta{ + solana.Meta(signer).WRITE().SIGNER(), + solana.Meta(r.ComputePdaAddress()), + solana.Meta(whitelistEntry), + solana.Meta(mint), + solana.Meta(solana.TokenProgramID), + solana.Meta(from).WRITE(), + solana.Meta(to).WRITE(), + }, + } } // CreateSignedTransaction creates a signed transaction from instructions @@ -97,7 +147,76 @@ func (r *E2ERunner) CreateSignedTransaction( return tx } -func (r *E2ERunner) DeploySPL(privateKey *solana.PrivateKey) *solana.Wallet { +// FindOrCreateAssociatedTokenAccount checks if ata exists, and if not creates it +func (r *E2ERunner) FindOrCreateAssociatedTokenAccount( + payer solana.PrivateKey, + owner solana.PublicKey, + tokenAccount solana.PublicKey, +) solana.PublicKey { + pdaAta, _, err := solana.FindAssociatedTokenAddress(owner, tokenAccount) + require.NoError(r, err) + + info, _ := r.SolanaClient.GetAccountInfo(r.Ctx, pdaAta) + if info != nil { + // already exists + return pdaAta + } + // doesn't exist, create it + ataInstruction := associatedtokenaccount.NewCreateInstruction(payer.PublicKey(), owner, tokenAccount).Build() + signedTx := r.CreateSignedTransaction( + []solana.Instruction{ataInstruction}, + payer, + []solana.PrivateKey{}, + ) + // broadcast the transaction and wait for finalization + r.BroadcastTxSync(signedTx) + + return pdaAta +} + +// SPLDepositAndCall deposits an amount of SPL tokens and calls a contract (if data is provided) +func (r *E2ERunner) SPLDepositAndCall( + privateKey *solana.PrivateKey, + amount uint64, + tokenAccount solana.PublicKey, + receiver ethcommon.Address, + data []byte, +) solana.Signature { + // ata for pda + pda := r.ComputePdaAddress() + pdaAta := r.FindOrCreateAssociatedTokenAccount(*privateKey, pda, tokenAccount) + + // deployer ata + ata := r.FindOrCreateAssociatedTokenAccount(*privateKey, privateKey.PublicKey(), tokenAccount) + + // deposit spl + seed := [][]byte{[]byte("whitelist"), tokenAccount.Bytes()} + whitelistEntryPDA, _, err := solana.FindProgramAddress(seed, r.GatewayProgram) + require.NoError(r, err) + + depositSPLInstruction := r.CreateDepositSPLInstruction( + amount, + privateKey.PublicKey(), + whitelistEntryPDA, + tokenAccount, + ata, + pdaAta, + receiver, + data, + ) + signedTx := r.CreateSignedTransaction( + []solana.Instruction{depositSPLInstruction}, + *privateKey, + []solana.PrivateKey{}, + ) + // broadcast the transaction and wait for finalization + sig, out := r.BroadcastTxSync(signedTx) + r.Logger.Info("deposit spl logs: %v", out.Meta.LogMessages) + + return sig +} + +func (r *E2ERunner) DeploySPL(privateKey *solana.PrivateKey, whitelist bool) *solana.Wallet { lamport, err := r.SolanaClient.GetMinimumBalanceForRentExemption(r.Ctx, token.MINT_SIZE, rpc.CommitmentFinalized) require.NoError(r, err) @@ -128,6 +247,53 @@ func (r *E2ERunner) DeploySPL(privateKey *solana.PrivateKey) *solana.Wallet { _, out := r.BroadcastTxSync(signedTx) 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()) + + mintToInstruction := token.NewMintToInstruction(uint64(1_000_000), tokenAccount.PublicKey(), ata, privateKey.PublicKey(), []solana.PublicKey{}). + Build() + signedTx = r.CreateSignedTransaction( + []solana.Instruction{mintToInstruction}, + *privateKey, + []solana.PrivateKey{}, + ) + + // broadcast the transaction and wait for finalization + _, out = r.BroadcastTxSync(signedTx) + r.Logger.Info("mint spl logs: %v", out.Meta.LogMessages) + + // optionally whitelist spl token in gateway + if whitelist { + seed := [][]byte{[]byte("whitelist"), tokenAccount.PublicKey().Bytes()} + whitelistEntryPDA, _, err := solana.FindProgramAddress(seed, r.GatewayProgram) + require.NoError(r, err) + + whitelistEntryInfo, err := r.SolanaClient.GetAccountInfo(r.Ctx, whitelistEntryPDA) + require.Error(r, err) + + // already whitelisted + if whitelistEntryInfo != nil { + return tokenAccount + } + + // create 'whitelist_spl_mint' instruction + instruction := r.CreateWhitelistSPLMintInstruction( + privateKey.PublicKey(), + whitelistEntryPDA, + tokenAccount.PublicKey(), + ) + // create and sign the transaction + signedTx := r.CreateSignedTransaction([]solana.Instruction{instruction}, *privateKey, []solana.PrivateKey{}) + + // broadcast the transaction and wait for finalization + _, out := r.BroadcastTxSync(signedTx) + r.Logger.Info("whitelist spl mint logs: %v", out.Meta.LogMessages) + + whitelistEntryInfo, err = r.SolanaClient.GetAccountInfo(r.Ctx, whitelistEntryPDA) + require.NoError(r, err) + require.NotNil(r, whitelistEntryInfo) + } + return tokenAccount } diff --git a/e2e/txserver/zeta_tx_server.go b/e2e/txserver/zeta_tx_server.go index 9b6e2d0b65..146dda4cf2 100644 --- a/e2e/txserver/zeta_tx_server.go +++ b/e2e/txserver/zeta_tx_server.go @@ -33,6 +33,8 @@ import ( stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" "github.com/cosmos/gogoproto/proto" + "github.com/ethereum/go-ethereum/common" + "github.com/gagliardetto/solana-go" "github.com/samber/lo" "github.com/zeta-chain/ethermint/crypto/hd" etherminttypes "github.com/zeta-chain/ethermint/types" @@ -56,6 +58,18 @@ type SystemContractAddresses struct { UniswapV2FactoryAddr, UniswapV2RouterAddr, ZEVMConnectorAddr, WZETAAddr, ERC20zrc20Addr string } +// ZRC20Deployment configures deployment of ZRC20 contracts +type ZRC20Deployment struct { + ERC20Addr common.Address + SPLAddr *solana.PublicKey // if nil - no SPL ZRC20 is deployed +} + +// ZRC20Addresses contains the addresses of deployed ZRC20 contracts +type ZRC20Addresses struct { + ERC20ZRC20Addr common.Address + SPLZRC20Addr common.Address +} + // EmissionsPoolAddress is the address of the emissions pool // This address is constant for all networks because it is derived from emissions name const EmissionsPoolAddress = "zeta1w43fn2ze2wyhu5hfmegr6vp52c3dgn0srdgymy" @@ -381,40 +395,40 @@ func (zts ZetaTxServer) DeploySystemContracts( } // DeployZRC20s deploys the ZRC20 contracts -// returns the addresses of erc20 zrc20 +// returns the addresses of erc20 and spl zrc20 func (zts ZetaTxServer) DeployZRC20s( - accountOperational, accountAdmin, erc20Addr string, + zrc20Deployment ZRC20Deployment, skipChain func(chainID int64) bool, -) (string, error) { +) (*ZRC20Addresses, error) { // retrieve account - accOperational, err := zts.clientCtx.Keyring.Key(accountOperational) + accOperational, err := zts.clientCtx.Keyring.Key(utils.OperationalPolicyName) if err != nil { - return "", err + return nil, err } addrOperational, err := accOperational.GetAddress() if err != nil { - return "", err + return nil, err } - accAdmin, err := zts.clientCtx.Keyring.Key(accountAdmin) + accAdmin, err := zts.clientCtx.Keyring.Key(utils.AdminPolicyName) if err != nil { - return "", err + return nil, err } addrAdmin, err := accAdmin.GetAddress() if err != nil { - return "", err + return nil, err } // authorization for deploying new ZRC20 has changed from accountOperational to accountAdmin in v19 // we use this query to check the current authorization for the message // if pre v19 the query is not implement and authorization is operational - deployerAccount := accountAdmin + deployerAccount := utils.AdminPolicyName deployerAddr := addrAdmin.String() authorization, preV19, err := zts.fetchMessagePermissions(&fungibletypes.MsgDeployFungibleCoinZRC20{}) if err != nil { - return "", fmt.Errorf("failed to fetch message permissions: %s", err.Error()) + return nil, fmt.Errorf("failed to fetch message permissions: %s", err.Error()) } if preV19 || authorization == authoritytypes.PolicyType_groupOperational { - deployerAccount = accountOperational + deployerAccount = utils.OperationalPolicyName deployerAddr = addrOperational.String() } @@ -461,7 +475,7 @@ func (zts ZetaTxServer) DeployZRC20s( ), fungibletypes.NewMsgDeployFungibleCoinZRC20( deployerAddr, - erc20Addr, + zrc20Deployment.ERC20Addr.Hex(), chains.GoerliLocalnet.ChainId, 6, "USDT", @@ -471,6 +485,19 @@ func (zts ZetaTxServer) DeployZRC20s( ), } + if zrc20Deployment.SPLAddr != nil { + deployMsgs = append(deployMsgs, fungibletypes.NewMsgDeployFungibleCoinZRC20( + deployerAddr, + zrc20Deployment.SPLAddr.String(), + chains.SolanaLocalnet.ChainId, + 9, + "USDT", + "USDT", + coin.CoinType_ERC20, + 100000, + )) + } + // apply skipChain filter and convert to sdk.Msg deployMsgsIface := lo.FilterMap( deployMsgs, @@ -484,12 +511,12 @@ func (zts ZetaTxServer) DeployZRC20s( res, err := zts.BroadcastTx(deployerAccount, deployMsgsIface...) if err != nil { - return "", fmt.Errorf("deploy zrc20s: %w", err) + return nil, fmt.Errorf("deploy zrc20s: %w", err) } deployedEvents, ok := EventsOfType[*fungibletypes.EventZRC20Deployed](res.Events) if !ok { - return "", fmt.Errorf("no EventZRC20Deployed in %s", res.TxHash) + return nil, fmt.Errorf("no EventZRC20Deployed in %s", res.TxHash) } zrc20Addrs := lo.Map(deployedEvents, func(ev *fungibletypes.EventZRC20Deployed, _ int) string { @@ -498,7 +525,7 @@ func (zts ZetaTxServer) DeployZRC20s( err = zts.InitializeLiquidityCaps(zrc20Addrs...) if err != nil { - return "", fmt.Errorf("initialize liquidity cap: %w", err) + return nil, fmt.Errorf("initialize liquidity cap: %w", err) } // find erc20 zrc20 @@ -506,10 +533,26 @@ func (zts ZetaTxServer) DeployZRC20s( return ev.ChainId == chains.GoerliLocalnet.ChainId && ev.CoinType == coin.CoinType_ERC20 }) if !ok { - return "", fmt.Errorf("unable to find erc20 zrc20") + return nil, fmt.Errorf("unable to find erc20 zrc20") } - return erc20zrc20.Contract, nil + // find spl zrc20 + splzrc20Addr := common.Address{} + if zrc20Deployment.SPLAddr != nil { + splzrc20, ok := lo.Find(deployedEvents, func(ev *fungibletypes.EventZRC20Deployed) bool { + return ev.ChainId == chains.SolanaLocalnet.ChainId && ev.CoinType == coin.CoinType_ERC20 + }) + if !ok { + return nil, fmt.Errorf("unable to find spl zrc20") + } + + splzrc20Addr = common.HexToAddress(splzrc20.Contract) + } + + return &ZRC20Addresses{ + ERC20ZRC20Addr: common.HexToAddress(erc20zrc20.Contract), + SPLZRC20Addr: splzrc20Addr, + }, nil } // FundEmissionsPool funds the emissions pool with the given amount diff --git a/pkg/contracts/solana/gateway.go b/pkg/contracts/solana/gateway.go index a3adcf5eae..12e3a10aa8 100644 --- a/pkg/contracts/solana/gateway.go +++ b/pkg/contracts/solana/gateway.go @@ -16,7 +16,11 @@ const ( // AccountsNumberOfDeposit is the number of accounts required for Solana gateway deposit instruction // [signer, pda, system_program] - AccountsNumDeposit = 3 + accountsNumDeposit = 3 + + // AccountsNumberOfDeposit is the number of accounts required for Solana gateway deposit spl instruction + // [signer, pda, whitelist_entry, mint_account, token_program, from, to] + accountsNumberDepositSPL = 7 ) var ( diff --git a/pkg/contracts/solana/inbound.go b/pkg/contracts/solana/inbound.go new file mode 100644 index 0000000000..766f84aa58 --- /dev/null +++ b/pkg/contracts/solana/inbound.go @@ -0,0 +1,127 @@ +package solana + +import ( + "fmt" + + "github.com/gagliardetto/solana-go" + "github.com/near/borsh-go" +) + +const ( + // MaxSignaturesPerTicker is the maximum number of signatures to process on a ticker + MaxSignaturesPerTicker = 100 +) + +type Deposit struct { + Sender string + Amount uint64 + Memo []byte + Slot uint64 + Asset string +} + +// ParseInboundAsDeposit tries to parse an instruction as a 'deposit'. +// It returns nil if the instruction can't be parsed as a 'deposit'. +func ParseInboundAsDeposit( + tx *solana.Transaction, + instructionIndex int, + slot uint64, +) (*Deposit, error) { + // get instruction by index + instruction := tx.Message.Instructions[instructionIndex] + + // try deserializing instruction as a 'deposit' + var inst DepositInstructionParams + err := borsh.Deserialize(&inst, instruction.Data) + if err != nil { + return nil, nil + } + + // check if the instruction is a deposit or not, if not, skip parsing + if inst.Discriminator != DiscriminatorDeposit { + return nil, nil + } + + // get the sender address (skip if unable to parse signer address) + sender, err := getSignerDeposit(tx, &instruction) + if err != nil { + return nil, err + } + + return &Deposit{ + Sender: sender, + Amount: inst.Amount, + Memo: inst.Memo, + Slot: slot, + Asset: "", // no asset for gas token SOL + }, nil +} + +// ParseInboundAsDepositSPL tries to parse an instruction as a 'deposit_spl_token'. +// It returns nil if the instruction can't be parsed as a 'deposit_spl_token'. +func ParseInboundAsDepositSPL( + tx *solana.Transaction, + instructionIndex int, + slot uint64, +) (*Deposit, error) { + // get instruction by index + instruction := tx.Message.Instructions[instructionIndex] + + // try deserializing instruction as a 'deposit_spl_token' + var inst DepositSPLInstructionParams + err := borsh.Deserialize(&inst, instruction.Data) + if err != nil { + return nil, nil + } + + // check if the instruction is a deposit spl or not, if not, skip parsing + if inst.Discriminator != DiscriminatorDepositSPL { + return nil, nil + } + + // get the sender and spl addresses + sender, spl, err := getSignerAndSPLFromDepositSPLAccounts(tx, &instruction) + if err != nil { + return nil, err + } + + return &Deposit{ + Sender: sender, + Amount: inst.Amount, + Memo: inst.Memo, + Slot: slot, + Asset: spl, + }, nil +} + +// GetSignerDeposit returns the signer address of the deposit instruction +// Note: solana-go is not able to parse the AccountMeta 'is_signer' ATM. This is a workaround. +func getSignerDeposit(tx *solana.Transaction, inst *solana.CompiledInstruction) (string, error) { + // there should be 3 accounts for a deposit instruction + if len(inst.Accounts) != accountsNumDeposit { + return "", fmt.Errorf("want %d accounts, got %d", accountsNumDeposit, len(inst.Accounts)) + } + + // sender is the signer account + return tx.Message.AccountKeys[0].String(), nil +} + +func getSignerAndSPLFromDepositSPLAccounts( + tx *solana.Transaction, + inst *solana.CompiledInstruction, +) (string, string, error) { + // there should be 7 accounts for a deposit spl instruction + if len(inst.Accounts) != accountsNumberDepositSPL { + return "", "", fmt.Errorf( + "want %d accounts, got %d", + accountsNumberDepositSPL, + len(inst.Accounts), + ) + } + + // the accounts are [signer, pda, whitelist_entry, mint_account, token_program, from, to] + signer := tx.Message.AccountKeys[0] + spl := tx.Message.AccountKeys[inst.Accounts[3]] + + return signer.String(), spl.String(), nil +} diff --git a/pkg/contracts/solana/inbound_test.go b/pkg/contracts/solana/inbound_test.go new file mode 100644 index 0000000000..00bb17f994 --- /dev/null +++ b/pkg/contracts/solana/inbound_test.go @@ -0,0 +1,105 @@ +package solana + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/gagliardetto/solana-go/rpc" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/node/pkg/chains" + "github.com/zeta-chain/node/testutil/sample" + "github.com/zeta-chain/node/zetaclient/testutils" +) + +func LoadObjectFromJSONFile(t *testing.T, obj interface{}, filename string) { + file, err := os.Open(filepath.Clean(filename)) + require.NoError(t, err) + defer file.Close() + + // read the struct from the file + decoder := json.NewDecoder(file) + err = decoder.Decode(&obj) + require.NoError(t, err) +} + +func LoadSolanaInboundTxResult( + t *testing.T, + txHash string, +) *rpc.GetTransactionResult { + txResult := &rpc.GetTransactionResult{} + LoadObjectFromJSONFile(t, txResult, fmt.Sprintf("testdata/%s.json", txHash)) + return txResult +} + +func Test_ParseInboundAsDeposit(t *testing.T) { + txHash := "MS3MPLN7hkbyCZFwKqXcg8fmEvQMD74fN6Ps2LSWXJoRxPW5ehaxBorK9q1JFVbqnAvu9jXm6ertj7kT7HpYw1j" + chain := chains.SolanaDevnet + + txResult := LoadSolanaInboundTxResult(t, txHash) + tx, err := txResult.Transaction.GetTransaction() + require.NoError(t, err) + + require.NoError(t, err) + + // create observer + chainParams := sample.ChainParams(chain.ChainId) + chainParams.GatewayAddress = testutils.GatewayAddresses[chain.ChainId] + require.NoError(t, err) + + // expected result + sender := "AS48jKNQsDGkEdDvfwu1QpqjtqbCadrAq9nGXjFmdX3Z" + expectedDeposit := &Deposit{ + Sender: sender, + Amount: 100000, + Memo: []byte("0x7F8ae2ABb69A558CE6bAd546f25F0464D9e09e5B4955a3F38ff86ae92A914445099caa8eA2B9bA32"), + Slot: txResult.Slot, + Asset: "", + } + + t.Run("should parse inbound event deposit SOL", func(t *testing.T) { + deposit, err := ParseInboundAsDeposit(tx, 0, txResult.Slot) + require.NoError(t, err) + + // check result + require.EqualValues(t, expectedDeposit, deposit) + }) +} + +func Test_ParseInboundAsDepositSPL(t *testing.T) { + txHash := "aY8yLDze6nHSRi7L5REozKAZY1aAyPJ6TfibiqQL5JGwgSBkYux5z5JfXs5ed8LZqpXUy4VijoU3x15mBd66ZGE" + chain := chains.SolanaDevnet + + txResult := LoadSolanaInboundTxResult(t, txHash) + tx, err := txResult.Transaction.GetTransaction() + require.NoError(t, err) + + // create observer + chainParams := sample.ChainParams(chain.ChainId) + chainParams.GatewayAddress = testutils.GatewayAddresses[chain.ChainId] + + // expected result + // solana e2e deployer account + sender := "37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ" + // solana e2e user evm account + expectedMemo, err := hex.DecodeString("103fd9224f00ce3013e95629e52dfc31d805d68d") + require.NoError(t, err) + expectedDeposit := &Deposit{ + Sender: sender, + Amount: 500000, + Memo: expectedMemo, + Slot: txResult.Slot, + Asset: "4GddKQ7baJpMyKna7bPPnhh7UQtpzfSGL1FgZ31hj4mp", // SPL address + } + + t.Run("should parse inbound event deposit SPL", func(t *testing.T) { + deposit, err := ParseInboundAsDepositSPL(tx, 0, txResult.Slot) + require.NoError(t, err) + + // check result + require.EqualValues(t, expectedDeposit, deposit) + }) +} diff --git a/pkg/contracts/solana/instruction.go b/pkg/contracts/solana/instruction.go index df5db0416b..65b6e6e4c3 100644 --- a/pkg/contracts/solana/instruction.go +++ b/pkg/contracts/solana/instruction.go @@ -34,6 +34,18 @@ type DepositInstructionParams struct { Memo []byte } +// DepositSPLInstructionParams contains the parameters for a gateway deposit spl instruction +type DepositSPLInstructionParams struct { + // Discriminator is the unique identifier for the deposit instruction + Discriminator [8]byte + + // Amount is the lamports amount for the deposit + Amount uint64 + + // Memo is the memo for the deposit + Memo []byte +} + // OutboundInstruction is the interface for all gateway outbound instructions type OutboundInstruction interface { // Signer returns the signer of the instruction diff --git a/pkg/contracts/solana/testdata/MS3MPLN7hkbyCZFwKqXcg8fmEvQMD74fN6Ps2LSWXJoRxPW5ehaxBorK9q1JFVbqnAvu9jXm6ertj7kT7HpYw1j.json b/pkg/contracts/solana/testdata/MS3MPLN7hkbyCZFwKqXcg8fmEvQMD74fN6Ps2LSWXJoRxPW5ehaxBorK9q1JFVbqnAvu9jXm6ertj7kT7HpYw1j.json new file mode 100644 index 0000000000..cf7edb3b81 --- /dev/null +++ b/pkg/contracts/solana/testdata/MS3MPLN7hkbyCZFwKqXcg8fmEvQMD74fN6Ps2LSWXJoRxPW5ehaxBorK9q1JFVbqnAvu9jXm6ertj7kT7HpYw1j.json @@ -0,0 +1,64 @@ +{ + "slot": 321701608, + "blockTime": 1724732369, + "transaction": { + "signatures": [ + "MS3MPLN7hkbyCZFwKqXcg8fmEvQMD74fN6Ps2LSWXJoRxPW5ehaxBorK9q1JFVbqnAvu9jXm6ertj7kT7HpYw1j" + ], + "message": { + "accountKeys": [ + "AS48jKNQsDGkEdDvfwu1QpqjtqbCadrAq9nGXjFmdX3Z", + "9dcAyYG4bawApZocwZSyJBi9Mynf5EuKAJfifXdfkqik", + "11111111111111111111111111111111", + "94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d" + ], + "header": { + "numRequiredSignatures": 1, + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 2 + }, + "recentBlockhash": "41txNvjedo2eu6aAofQfyLskAcgtrtgch9RpqnrKcv1a", + "instructions": [ + { + "programIdIndex": 3, + "accounts": [0, 1, 2], + "data": "4ALHYcAj3zFsNjmfeq7nDK1E8BsxRQRzhLjrqzmjYzL97Qkiz4rP1iQePmFAehfFEET7uczYLhhEVhtndBYNNm6ekHSkgsLzYDeSD2JSudHa6D5tqhVGjvXZ7qEouPiy9eptZfuYHE9X" + } + ] + } + }, + "meta": { + "err": null, + "fee": 5000, + "preBalances": [9999364000, 1001447680, 1, 1141440], + "postBalances": [9999259000, 1001547680, 1, 1141440], + "innerInstructions": [ + { + "index": 0, + "instructions": [ + { + "programIdIndex": 2, + "accounts": [0, 1], + "data": "3Bxs4ThwQbE4vyj5" + } + ] + } + ], + "preTokenBalances": [], + "postTokenBalances": [], + "logMessages": [ + "Program 94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d invoke [1]", + "Program log: Instruction: Deposit", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program log: AS48jKNQsDGkEdDvfwu1QpqjtqbCadrAq9nGXjFmdX3Z deposits 100000 lamports to PDA", + "Program 94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d consumed 17006 of 200000 compute units", + "Program 94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d success" + ], + "status": { "Ok": null }, + "rewards": [], + "loadedAddresses": { "readonly": [], "writable": [] }, + "computeUnitsConsumed": 17006 + }, + "version": 0 +} diff --git a/pkg/contracts/solana/testdata/aY8yLDze6nHSRi7L5REozKAZY1aAyPJ6TfibiqQL5JGwgSBkYux5z5JfXs5ed8LZqpXUy4VijoU3x15mBd66ZGE.json b/pkg/contracts/solana/testdata/aY8yLDze6nHSRi7L5REozKAZY1aAyPJ6TfibiqQL5JGwgSBkYux5z5JfXs5ed8LZqpXUy4VijoU3x15mBd66ZGE.json new file mode 100644 index 0000000000..2fb146776b --- /dev/null +++ b/pkg/contracts/solana/testdata/aY8yLDze6nHSRi7L5REozKAZY1aAyPJ6TfibiqQL5JGwgSBkYux5z5JfXs5ed8LZqpXUy4VijoU3x15mBd66ZGE.json @@ -0,0 +1,93 @@ +{ + "slot": 539, + "blockTime": 1730986363, + "transaction": { + "signatures": [ + "aY8yLDze6nHSRi7L5REozKAZY1aAyPJ6TfibiqQL5JGwgSBkYux5z5JfXs5ed8LZqpXUy4VijoU3x15mBd66ZGE" + ], + "message": { + "accountKeys": [ + "37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ", + "HX8BXQKVw1xpoyDXMr9ujyrrpSYRWcFZ4oB3fRHpM8V7", + "5EVPJV5hjwYGYko2pSykSdJG5ZfBbMEDhYci2PrqQmby", + "9dcAyYG4bawApZocwZSyJBi9Mynf5EuKAJfifXdfkqik", + "8vquQi8xNxxBTsohf1u8xQHYbo7Fv8BEWCJz63RJSaeE", + "4GddKQ7baJpMyKna7bPPnhh7UQtpzfSGL1FgZ31hj4mp", + "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d" + ], + "header": { + "numRequiredSignatures": 1, + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 5 + }, + "recentBlockhash": "5ZB2NWKwp86t47ojCHPjRyd5vMKSWBdhxqDdtSZVPUj", + "instructions": [ + { + "programIdIndex": 7, + "accounts": [ + 0, + 3, + 4, + 5, + 6, + 1, + 2 + ], + "data": "5JndgWCNHDyr2qQccK9VM1NxJFrVbUDvG2hfAxRC5z6nPPAVPsj7q3A" + } + ] + } + }, + "meta": { + "err": null, + "fee": 5000, + "preBalances": [ + 99992030600, + 2039280, + 2039280, + 1447680, + 946560, + 1461600, + 929020800, + 1141440 + ], + "postBalances": [ + 99992025600, + 2039280, + 2039280, + 1447680, + 946560, + 1461600, + 929020800, + 1141440 + ], + "innerInstructions": [ + { + "index": 0, + "instructions": [] + } + ], + "preTokenBalances": [], + "postTokenBalances": [], + "logMessages": [ + "Program 94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d invoke [1]", + "Program log: Instruction: DepositSplToken", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]", + "Program log: Instruction: Transfer Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 181038 compute units", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success Program log: deposit spl token successfully", + "Program 94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d consumed 24017 of 200000 compute units", + "Program 94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d success" + ], + "status": { + "Ok": null + }, + "rewards": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "computeUnitsConsumed": 274902869112 + }, + "version": 0 +} \ No newline at end of file diff --git a/x/fungible/keeper/foreign_coins.go b/x/fungible/keeper/foreign_coins.go index 4dd5ac72a4..23cd1b6375 100644 --- a/x/fungible/keeper/foreign_coins.go +++ b/x/fungible/keeper/foreign_coins.go @@ -5,7 +5,6 @@ import ( "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" - ethcommon "github.com/ethereum/go-ethereum/common" "github.com/zeta-chain/node/pkg/coin" "github.com/zeta-chain/node/x/fungible/types" @@ -110,15 +109,9 @@ func (k Keeper) GetGasCoinForForeignCoin(ctx sdk.Context, chainID int64) (types. // GetForeignCoinFromAsset returns the foreign coin for a given asset for a given chain func (k Keeper) GetForeignCoinFromAsset(ctx sdk.Context, asset string, chainID int64) (types.ForeignCoins, bool) { - if !ethcommon.IsHexAddress(asset) { - return types.ForeignCoins{}, false - } - assetAddr := ethcommon.HexToAddress(asset) - foreignCoinList := k.GetAllForeignCoinsForChain(ctx, chainID) for _, coin := range foreignCoinList { - coinAssetAddr := ethcommon.HexToAddress(coin.Asset) - if coinAssetAddr == assetAddr && coin.ForeignChainId == chainID { + if asset == coin.Asset && coin.ForeignChainId == chainID { return coin, true } } diff --git a/x/fungible/keeper/foreign_coins_test.go b/x/fungible/keeper/foreign_coins_test.go index d02c3a29c9..3ebc13f2e6 100644 --- a/x/fungible/keeper/foreign_coins_test.go +++ b/x/fungible/keeper/foreign_coins_test.go @@ -135,24 +135,6 @@ func TestKeeperGetForeignCoinFromAsset(t *testing.T) { fc, found = k.GetForeignCoinFromAsset(ctx, gasAsset, 3) require.False(t, found) }) - - t.Run("can get foreign coin with non-checksum address", func(t *testing.T) { - k, ctx, _, _ := keepertest.FungibleKeeper(t) - - setForeignCoins(ctx, k, - types.ForeignCoins{ - Zrc20ContractAddress: sample.EthAddress().String(), - Asset: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", - ForeignChainId: 1, - CoinType: coin.CoinType_ERC20, - Name: "foo", - }, - ) - - fc, found := k.GetForeignCoinFromAsset(ctx, "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", 1) - require.True(t, found) - require.Equal(t, "foo", fc.Name) - }) } func TestKeeperGetAllForeignCoinMap(t *testing.T) { diff --git a/zetaclient/chains/solana/observer/inbound.go b/zetaclient/chains/solana/observer/inbound.go index 4c93d95470..bd0e9a98b7 100644 --- a/zetaclient/chains/solana/observer/inbound.go +++ b/zetaclient/chains/solana/observer/inbound.go @@ -9,7 +9,6 @@ import ( cosmosmath "cosmossdk.io/math" "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" - "github.com/near/borsh-go" "github.com/pkg/errors" "github.com/rs/zerolog" @@ -197,12 +196,24 @@ func (ob *Observer) FilterInboundEvents(txResult *rpc.GetTransactionResult) ([]* // try parsing the instruction as a 'deposit' if not seen yet if !seenDeposit { - event, err := ob.ParseInboundAsDeposit(tx, i, txResult.Slot) + deposit, err := solanacontracts.ParseInboundAsDeposit(tx, i, txResult.Slot) if err != nil { return nil, errors.Wrap(err, "error ParseInboundAsDeposit") - } else if event != nil { + } else if deposit != nil { seenDeposit = true - events = append(events, event) + events = append(events, &clienttypes.InboundEvent{ + SenderChainID: ob.Chain().ChainId, + Sender: deposit.Sender, + Receiver: deposit.Sender, // receiver is pulled out from memo + TxOrigin: deposit.Sender, + Amount: deposit.Amount, + Memo: deposit.Memo, + BlockNumber: deposit.Slot, // instead of using block, Solana explorer uses slot for indexing + TxHash: tx.Signatures[0].String(), + Index: 0, // hardcode to 0 for Solana, not a EVM smart contract call + CoinType: coin.CoinType_Gas, + Asset: deposit.Asset, + }) ob.Logger().Inbound.Info(). Msgf("FilterInboundEvents: deposit detected in sig %s instruction %d", tx.Signatures[0], i) } @@ -213,12 +224,24 @@ func (ob *Observer) FilterInboundEvents(txResult *rpc.GetTransactionResult) ([]* // try parsing the instruction as a 'deposit_spl_token' if not seen yet if !seenDepositSPL { - event, err := ob.ParseInboundAsDepositSPL(tx, i, txResult.Slot) + deposit, err := solanacontracts.ParseInboundAsDepositSPL(tx, i, txResult.Slot) if err != nil { return nil, errors.Wrap(err, "error ParseInboundAsDepositSPL") - } else if event != nil { + } else if deposit != nil { seenDepositSPL = true - events = append(events, event) + events = append(events, &clienttypes.InboundEvent{ + SenderChainID: ob.Chain().ChainId, + Sender: deposit.Sender, + Receiver: deposit.Sender, // receiver is pulled out from memo + TxOrigin: deposit.Sender, + Amount: deposit.Amount, + Memo: deposit.Memo, + BlockNumber: deposit.Slot, // instead of using block, Solana explorer uses slot for indexing + TxHash: tx.Signatures[0].String(), + Index: 0, // hardcode to 0 for Solana, not a EVM smart contract call + CoinType: coin.CoinType_ERC20, + Asset: deposit.Asset, + }) ob.Logger().Inbound.Info(). Msgf("FilterInboundEvents: SPL deposit detected in sig %s instruction %d", tx.Signatures[0], i) } @@ -262,100 +285,3 @@ func (ob *Observer) BuildInboundVoteMsgFromEvent(event *clienttypes.InboundEvent 0, // not a smart contract call ) } - -// ParseInboundAsDeposit tries to parse an instruction as a 'deposit'. -// It returns nil if the instruction can't be parsed as a 'deposit'. -func (ob *Observer) ParseInboundAsDeposit( - tx *solana.Transaction, - instructionIndex int, - slot uint64, -) (*clienttypes.InboundEvent, error) { - // get instruction by index - instruction := tx.Message.Instructions[instructionIndex] - - // try deserializing instruction as a 'deposit' - var inst solanacontracts.DepositInstructionParams - err := borsh.Deserialize(&inst, instruction.Data) - if err != nil { - return nil, nil - } - - // check if the instruction is a deposit or not - if inst.Discriminator != solanacontracts.DiscriminatorDeposit { - return nil, nil - } - - // get the sender address (skip if unable to parse signer address) - sender, err := ob.GetSignerDeposit(tx, &instruction) - if err != nil { - ob.Logger(). - Inbound.Err(err). - Msgf("unable to get signer for sig %s instruction %d", tx.Signatures[0], instructionIndex) - return nil, nil - } - - // build inbound event - event := &clienttypes.InboundEvent{ - SenderChainID: ob.Chain().ChainId, - Sender: sender, - Receiver: sender, - TxOrigin: sender, - Amount: inst.Amount, - Memo: inst.Memo, - BlockNumber: slot, // instead of using block, Solana explorer uses slot for indexing - TxHash: tx.Signatures[0].String(), - Index: 0, // hardcode to 0 for Solana, not a EVM smart contract call - CoinType: coin.CoinType_Gas, - Asset: "", // no asset for gas token SOL - } - - return event, nil -} - -// ParseInboundAsDepositSPL tries to parse an instruction as a 'deposit_spl_token'. -// It returns nil if the instruction can't be parsed as a 'deposit_spl_token'. -func (ob *Observer) ParseInboundAsDepositSPL( - _ *solana.Transaction, - _ int, - _ uint64, -) (*clienttypes.InboundEvent, error) { - // not implemented yet - return nil, nil -} - -// GetSignerDeposit returns the signer address of the deposit instruction -// Note: solana-go is not able to parse the AccountMeta 'is_signer' ATM. This is a workaround. -func (ob *Observer) GetSignerDeposit(tx *solana.Transaction, inst *solana.CompiledInstruction) (string, error) { - // there should be 3 accounts for a deposit instruction - if len(inst.Accounts) != solanacontracts.AccountsNumDeposit { - return "", fmt.Errorf("want %d accounts, got %d", solanacontracts.AccountsNumDeposit, len(inst.Accounts)) - } - - // the accounts are [signer, pda, system_program] - signerIndex, pdaIndex, systemIndex := -1, -1, -1 - - // try to find the indexes of all above accounts - for _, accIndex := range inst.Accounts { - // #nosec G701 always in range - accIndexInt := int(accIndex) - accKey := tx.Message.AccountKeys[accIndexInt] - - switch accKey { - case ob.pda: - pdaIndex = accIndexInt - case solana.SystemProgramID: - systemIndex = accIndexInt - default: - // the last remaining account is the signer - signerIndex = accIndexInt - } - } - - // all above accounts must be found - if signerIndex == -1 || pdaIndex == -1 || systemIndex == -1 { - return "", fmt.Errorf("invalid accounts for deposit instruction") - } - - // sender is the signer account - return tx.Message.AccountKeys[signerIndex].String(), nil -} diff --git a/zetaclient/chains/solana/observer/inbound_test.go b/zetaclient/chains/solana/observer/inbound_test.go index 577c10ee9a..c38d3ae281 100644 --- a/zetaclient/chains/solana/observer/inbound_test.go +++ b/zetaclient/chains/solana/observer/inbound_test.go @@ -163,47 +163,3 @@ func Test_BuildInboundVoteMsgFromEvent(t *testing.T) { require.Nil(t, msg) }) } - -func Test_ParseInboundAsDeposit(t *testing.T) { - // load archived inbound deposit tx result - // https://explorer.solana.com/tx/MS3MPLN7hkbyCZFwKqXcg8fmEvQMD74fN6Ps2LSWXJoRxPW5ehaxBorK9q1JFVbqnAvu9jXm6ertj7kT7HpYw1j?cluster=devnet - txHash := "MS3MPLN7hkbyCZFwKqXcg8fmEvQMD74fN6Ps2LSWXJoRxPW5ehaxBorK9q1JFVbqnAvu9jXm6ertj7kT7HpYw1j" - chain := chains.SolanaDevnet - - txResult := testutils.LoadSolanaInboundTxResult(t, TestDataDir, chain.ChainId, txHash, false) - tx, err := txResult.Transaction.GetTransaction() - require.NoError(t, err) - - database, err := db.NewFromSqliteInMemory(true) - require.NoError(t, err) - - // create observer - chainParams := sample.ChainParams(chain.ChainId) - chainParams.GatewayAddress = testutils.GatewayAddresses[chain.ChainId] - ob, err := observer.NewObserver(chain, nil, *chainParams, nil, nil, 60, database, base.DefaultLogger(), nil) - require.NoError(t, err) - - // expected result - sender := "AS48jKNQsDGkEdDvfwu1QpqjtqbCadrAq9nGXjFmdX3Z" - eventExpected := &clienttypes.InboundEvent{ - SenderChainID: chain.ChainId, - Sender: sender, - Receiver: sender, - TxOrigin: sender, - Amount: 100000, - Memo: []byte("0x7F8ae2ABb69A558CE6bAd546f25F0464D9e09e5B4955a3F38ff86ae92A914445099caa8eA2B9bA32"), - BlockNumber: txResult.Slot, - TxHash: txHash, - Index: 0, // not a EVM smart contract call - CoinType: coin.CoinType_Gas, - Asset: "", // no asset for gas token SOL - } - - t.Run("should parse inbound event deposit SOL", func(t *testing.T) { - event, err := ob.ParseInboundAsDeposit(tx, 0, txResult.Slot) - require.NoError(t, err) - - // check result - require.EqualValues(t, eventExpected, event) - }) -} From ea4e7700aea09ed0b5e799e192c1752db80e5527 Mon Sep 17 00:00:00 2001 From: Alex Gartner Date: Fri, 8 Nov 2024 08:43:20 -0800 Subject: [PATCH 09/10] refactor(zetacore)!: remove rosetta api (#3130) * refactor(zetacore)!: remove rosetta api * remove from localnet --- cmd/zetacored/root.go | 3 -- contrib/localnet/docker-compose.yml | 17 ------- contrib/localnet/scripts/start-rosetta.sh | 14 ------ contrib/localnet/zetacored/common/app.toml | 20 -------- docs/cli/zetacored/cli.md | 40 ---------------- go.mod | 4 +- server/start.go | 54 +--------------------- x/observer/keeper/events.go | 19 +++++++- 8 files changed, 20 insertions(+), 151 deletions(-) delete mode 100644 contrib/localnet/scripts/start-rosetta.sh diff --git a/cmd/zetacored/root.go b/cmd/zetacored/root.go index 3880b12108..93ae4ba469 100644 --- a/cmd/zetacored/root.go +++ b/cmd/zetacored/root.go @@ -6,7 +6,6 @@ import ( "io" "os" - rosettaCmd "cosmossdk.io/tools/rosetta/cmd" dbm "github.com/cometbft/cometbft-db" tmcfg "github.com/cometbft/cometbft/config" tmcli "github.com/cometbft/cometbft/libs/cli" @@ -177,8 +176,6 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig types.EncodingConfig) { if err := SetEthereumHDPath(rootCmd); err != nil { fmt.Printf("warning: unable to set default HD path: %v\n", err) } - - rootCmd.AddCommand(rosettaCmd.RosettaCommand(encodingConfig.InterfaceRegistry, encodingConfig.Codec)) } func addModuleInitFlags(startCmd *cobra.Command) { diff --git a/contrib/localnet/docker-compose.yml b/contrib/localnet/docker-compose.yml index 670befa7b6..e0078230e7 100644 --- a/contrib/localnet/docker-compose.yml +++ b/contrib/localnet/docker-compose.yml @@ -5,7 +5,6 @@ # - An Ethereum node (eth) # - A secondary optional Ethereum node (eth2) enabled when profile is set to eth2 # - A Bitcoin node (bitcoin) -# - A Rosetta API (rosetta) # - An orchestrator to manage interaction with the localnet (orchestrator) # - An upgrade host to serve binaries for the upgrade tests (upgrade-host). Only enabled when profile is set to upgrade. # - An upgrade orchestrator to send the upgrade governance proposal (upgrade-orchestrator). Only enabled when profile is set to upgrade. @@ -19,22 +18,6 @@ networks: - subnet: 172.20.0.0/24 services: - rosetta: - image: zetanode:latest - container_name: rosetta - hostname: rosetta - depends_on: - zetacore0: - condition: service_healthy - ports: - - "8080:8080" - networks: - mynetwork: - ipv4_address: 172.20.0.200 - entrypoint: ["zetacored", "rosetta", "--tendermint", "zetacore0:26657", "--grpc", "zetacore0:9090", "--network", "athens_101-1", "--blockchain", "zetacore"] - volumes: - - ssh:/root/.ssh - zetacore0: image: zetanode:latest container_name: zetacore0 diff --git a/contrib/localnet/scripts/start-rosetta.sh b/contrib/localnet/scripts/start-rosetta.sh deleted file mode 100644 index e675da6a6a..0000000000 --- a/contrib/localnet/scripts/start-rosetta.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -# This script is used to start the Rosetta API server for the Zetacore network. - -echo "Waiting for network to start producing blocks" -CURRENT_HEIGHT=0 -WAIT_HEIGHT=1 -while [[ $CURRENT_HEIGHT -lt $WAIT_HEIGHT ]] -do - CURRENT_HEIGHT=$(curl -s zetacore0:26657/status | jq '.result.sync_info.latest_block_height' | tr -d '"') - sleep 5 -done - -zetacored rosetta --tendermint zetacore0:26657 --grpc zetacore0:9090 --network athens_101-1 --blockchain zetacore \ No newline at end of file diff --git a/contrib/localnet/zetacored/common/app.toml b/contrib/localnet/zetacored/common/app.toml index 7bbf48cf96..adee8fb76c 100644 --- a/contrib/localnet/zetacored/common/app.toml +++ b/contrib/localnet/zetacored/common/app.toml @@ -136,26 +136,6 @@ enabled-unsafe-cors = true ### Rosetta Configuration ### ############################################################################### -[rosetta] - -# Enable defines if the Rosetta API server should be enabled. -enable = false - -# Address defines the Rosetta API server to listen on. -address = ":8080" - -# Network defines the name of the blockchain that will be returned by Rosetta. -blockchain = "app" - -# Network defines the name of the network that will be returned by Rosetta. -network = "network" - -# Retries defines the number of retries when connecting to the node before failing. -retries = 3 - -# Offline defines if Rosetta server should run in offline mode. -offline = false - ############################################################################### ### gRPC Configuration ### ############################################################################### diff --git a/docs/cli/zetacored/cli.md b/docs/cli/zetacored/cli.md index 36440f2704..d7a594d94f 100644 --- a/docs/cli/zetacored/cli.md +++ b/docs/cli/zetacored/cli.md @@ -33,7 +33,6 @@ Zetacore Daemon (server) * [zetacored parse-genesis-file](#zetacored-parse-genesis-file) - Parse the provided genesis file and import the required data into the optionally provided genesis file * [zetacored query](#zetacored-query) - Querying subcommands * [zetacored rollback](#zetacored-rollback) - rollback cosmos-sdk and tendermint state by one height -* [zetacored rosetta](#zetacored-rosetta) - spin up a rosetta server * [zetacored snapshots](#zetacored-snapshots) - Manage local snapshots * [zetacored start](#zetacored-start) - Run the full node * [zetacored status](#zetacored-status) - Query remote node for status @@ -7363,45 +7362,6 @@ zetacored rollback [flags] * [zetacored](#zetacored) - Zetacore Daemon (server) -## zetacored rosetta - -spin up a rosetta server - -``` -zetacored rosetta [flags] -``` - -### Options - -``` - --addr string the address rosetta will bind to - --blockchain string the blockchain type - --denom-to-suggest string default denom for fee suggestion - --enable-fee-suggestion enable default fee suggestion - --gas-to-suggest int default gas for fee suggestion (default 200000) - --grpc string the app gRPC endpoint - -h, --help help for rosetta - --network string the network name - --offline run rosetta only with construction API - --prices-to-suggest string default prices for fee suggestion - --retries int the number of retries that will be done before quitting (default 5) - --tendermint string the tendermint rpc endpoint, without tcp:// -``` - -### 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 snapshots Manage local snapshots diff --git a/go.mod b/go.mod index adb9b1cdb7..795cfb4528 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.22.8 require ( cosmossdk.io/errors v1.0.1 cosmossdk.io/math v1.3.0 - cosmossdk.io/tools/rosetta v0.2.1 + cosmossdk.io/tools/rosetta v0.2.1 // indirect github.com/99designs/keyring v1.2.1 github.com/btcsuite/btcd v0.24.2 github.com/btcsuite/btcd/btcec/v2 v2.3.2 @@ -15,7 +15,7 @@ require ( github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 github.com/cenkalti/backoff/v4 v4.3.0 github.com/cockroachdb/errors v1.11.1 - github.com/coinbase/rosetta-sdk-go v0.7.9 + github.com/coinbase/rosetta-sdk-go v0.7.9 // indirect github.com/cometbft/cometbft v0.37.5 github.com/cometbft/cometbft-db v0.12.0 github.com/cosmos/btcutil v1.0.5 diff --git a/server/start.go b/server/start.go index 09bb9215c0..14c64482f8 100644 --- a/server/start.go +++ b/server/start.go @@ -27,8 +27,6 @@ import ( "time" errorsmod "cosmossdk.io/errors" - "cosmossdk.io/tools/rosetta" - crgserver "cosmossdk.io/tools/rosetta/lib/server" dbm "github.com/cometbft/cometbft-db" abciserver "github.com/cometbft/cometbft/abci/server" tcmd "github.com/cometbft/cometbft/cmd/cometbft/commands" @@ -50,7 +48,6 @@ import ( "github.com/cosmos/cosmos-sdk/server/types" pruningtypes "github.com/cosmos/cosmos-sdk/store/pruning/types" "github.com/cosmos/cosmos-sdk/telemetry" - sdk "github.com/cosmos/cosmos-sdk/types" ethmetricsexp "github.com/ethereum/go-ethereum/metrics/exp" "github.com/spf13/cobra" "github.com/zeta-chain/ethermint/indexer" @@ -626,61 +623,12 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, opts StartOpt } // At this point it is safe to block the process if we're in query only mode as - // we do not need to start Rosetta or handle any Tendermint related processes. + // we do not need to handle any Tendermint related processes. if gRPCOnly { // wait for signal capture and gracefully return return server.WaitForQuitSignals() } - var rosettaSrv crgserver.Server - if config.Rosetta.Enable { - offlineMode := config.Rosetta.Offline - - // If GRPC is not enabled rosetta cannot work in online mode, so it works in - // offline mode. - if !config.GRPC.Enable { - offlineMode = true - } - - minGasPrices, err := sdk.ParseDecCoins(config.MinGasPrices) - if err != nil { - ctx.Logger.Error("failed to parse minimum-gas-prices", "error", err.Error()) - return err - } - - conf := &rosetta.Config{ - Blockchain: config.Rosetta.Blockchain, - Network: config.Rosetta.Network, - TendermintRPC: ctx.Config.RPC.ListenAddress, - GRPCEndpoint: config.GRPC.Address, - Addr: config.Rosetta.Address, - Retries: config.Rosetta.Retries, - Offline: offlineMode, - GasToSuggest: config.Rosetta.GasToSuggest, - EnableFeeSuggestion: config.Rosetta.EnableFeeSuggestion, - GasPrices: minGasPrices.Sort(), - Codec: clientCtx.Codec.(*codec.ProtoCodec), - InterfaceRegistry: clientCtx.InterfaceRegistry, - } - - rosettaSrv, err = rosetta.ServerFromConfig(conf) - if err != nil { - return err - } - - errCh := make(chan error) - go func() { - if err := rosettaSrv.Start(); err != nil { - errCh <- err - } - }() - - select { - case err := <-errCh: - return err - case <-time.After(types.ServerStartTime): // assume server started successfully - } - } // Wait for SIGINT or SIGTERM signal return server.WaitForQuitSignals() } diff --git a/x/observer/keeper/events.go b/x/observer/keeper/events.go index d1ddbc44ff..aed3b726a9 100644 --- a/x/observer/keeper/events.go +++ b/x/observer/keeper/events.go @@ -1,9 +1,10 @@ package keeper import ( + "encoding/json" + "log" "strconv" - types2 "github.com/coinbase/rosetta-sdk-go/types" sdk "github.com/cosmos/cosmos-sdk/types" types "github.com/zeta-chain/node/x/observer/types" @@ -21,11 +22,25 @@ func EmitEventBallotCreated(ctx sdk.Context, ballot types.Ballot, observationHas } } +// vendor this code from github.com/coinbase/rosetta-sdk-go/types +func prettyPrintStruct(val interface{}) string { + prettyStruct, err := json.MarshalIndent( + val, + "", + " ", + ) + if err != nil { + log.Fatal(err) + } + + return string(prettyStruct) +} + func EmitEventKeyGenBlockUpdated(ctx sdk.Context, keygen *types.Keygen) { err := ctx.EventManager().EmitTypedEvents(&types.EventKeygenBlockUpdated{ MsgTypeUrl: sdk.MsgTypeURL(&types.MsgUpdateKeygen{}), KeygenBlock: strconv.Itoa(int(keygen.BlockNumber)), - KeygenPubkeys: types2.PrettyPrintStruct(keygen.GranteePubkeys), + KeygenPubkeys: prettyPrintStruct(keygen.GranteePubkeys), }) if err != nil { ctx.Logger().Error("Error emitting EventKeygenBlockUpdated :", err) From 7c70809b1024c288d74cef13860c33d7e9bad013 Mon Sep 17 00:00:00 2001 From: Charlie Chen <34498985+ws4charlie@users.noreply.github.com> Date: Fri, 8 Nov 2024 11:04:14 -0600 Subject: [PATCH 10/10] test: split bitcoin e2e into deposit group and withdraw group (#3105) * initial commit * split bitcoin tests into deposit and withdraw groups * add changelog entry * add balance check after Bitcoin e2e tests * use explicit deposit, withdraw names for test accounts; use two colors; * make changelog entry more explicit * rename method names to be Bitcoin specific --- changelog.md | 2 + cmd/zetae2e/bitcoin_address.go | 9 +- cmd/zetae2e/config/local.yml | 6 +- cmd/zetae2e/config/localnet.yml | 6 +- cmd/zetae2e/local/bitcoin.go | 135 +++++++++++++----- cmd/zetae2e/local/local.go | 31 +++- cmd/zetae2e/setup_bitcoin.go | 2 +- .../localnet/orchestrator/start-zetae2e.sh | 7 +- contrib/localnet/scripts/start-zetacored.sh | 7 +- e2e/config/config.go | 38 ++--- e2e/e2etests/test_bitcoin_deposit.go | 2 - .../test_bitcoin_deposit_and_call_revert.go | 4 - e2e/e2etests/test_bitcoin_deposit_call.go | 4 - e2e/e2etests/test_bitcoin_donation.go | 4 - e2e/e2etests/test_bitcoin_std_deposit.go | 3 - .../test_bitcoin_std_deposit_and_call.go | 3 - ...est_bitcoin_std_deposit_and_call_revert.go | 4 - ...d_deposit_and_call_revert_other_address.go | 4 - ...oin_std_memo_inscribed_deposit_and_call.go | 4 - .../test_bitcoin_withdraw_invalid_address.go | 2 - e2e/e2etests/test_bitcoin_withdraw_legacy.go | 2 - .../test_bitcoin_withdraw_multiple.go | 3 - e2e/e2etests/test_bitcoin_withdraw_p2sh.go | 2 - e2e/e2etests/test_bitcoin_withdraw_p2wsh.go | 2 - ...est_bitcoin_withdraw_restricted_address.go | 2 - e2e/e2etests/test_bitcoin_withdraw_segwit.go | 2 - e2e/e2etests/test_bitcoin_withdraw_taproot.go | 2 - e2e/e2etests/test_migrate_tss.go | 1 - e2e/e2etests/test_stress_btc_deposit.go | 2 - e2e/e2etests/test_stress_btc_withdraw.go | 2 - e2e/runner/accounting.go | 23 +-- e2e/runner/balances.go | 11 +- e2e/runner/bitcoin.go | 31 ++-- e2e/runner/logger.go | 2 +- e2e/runner/require.go | 4 +- e2e/runner/run.go | 6 +- e2e/runner/setup_bitcoin.go | 85 +++++------ x/observer/types/chain_params.go | 2 +- 38 files changed, 241 insertions(+), 220 deletions(-) diff --git a/changelog.md b/changelog.md index d71bded0a0..59e544fad6 100644 --- a/changelog.md +++ b/changelog.md @@ -8,7 +8,9 @@ * [3124](https://github.com/zeta-chain/node/pull/3124) - integrate SPL deposits ### Tests + * [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 ### Refactor * [3118](https://github.com/zeta-chain/node/pull/3118) - zetaclient: remove hsm signer diff --git a/cmd/zetae2e/bitcoin_address.go b/cmd/zetae2e/bitcoin_address.go index 70d64ab582..be1251d1d2 100644 --- a/cmd/zetae2e/bitcoin_address.go +++ b/cmd/zetae2e/bitcoin_address.go @@ -66,14 +66,11 @@ func runBitcoinAddress(cmd *cobra.Command, args []string) error { return err } - addr, privKey, err := r.GetBtcAddress() - if err != nil { - return err - } + addr, privKey := r.GetBtcAddress() - logger.Print("* BTC address: %s", addr) + logger.Print("* BTC address: %s", addr.EncodeAddress()) if showPrivKey { - logger.Print("* BTC privkey: %s", privKey) + logger.Print("* BTC privkey: %s", privKey.String()) } return nil diff --git a/cmd/zetae2e/config/local.yml b/cmd/zetae2e/config/local.yml index 2481ab31fa..5cc87be394 100644 --- a/cmd/zetae2e/config/local.yml +++ b/cmd/zetae2e/config/local.yml @@ -16,10 +16,14 @@ additional_accounts: bech32_address: "zeta13t3zjxvwec7g38q8mdjga37rpes9zkfvv7tkn2" evm_address: "0x8Ae229198eCE3c889C07DB648Ec7C30E6051592c" private_key: "105460aebf71b10bfdb710ef5aa6d2932ee6ff6fc317ac9c24e0979903b10a5d" - user_bitcoin: + user_bitcoin_deposit: bech32_address: "zeta19q7czqysah6qg0n4y3l2a08gfzqxydla492v80" evm_address: "0x283d810090EdF4043E75247eAeBcE848806237fD" private_key: "7bb523963ee2c78570fb6113d886a4184d42565e8847f1cb639f5f5e2ef5b37a" + user_bitcoin_withdraw: + bech32_address: "zeta17e77anpmzhuuam67hg6x3mtqrulqh80z9chv70" + evm_address: "0xf67deecc3B15F9CEeF5eba3468ed601f3e0B9de2" + private_key: "2b3306a8ac43dbf0e350b87876c131e7e12bd49563a16de9ce8aeb664b94d559" user_solana: bech32_address: "zeta1zqlajgj0qr8rqylf2c572t0ux8vqt45d4zngpm" evm_address: "0x103FD9224F00ce3013e95629e52DFc31D805D68d" diff --git a/cmd/zetae2e/config/localnet.yml b/cmd/zetae2e/config/localnet.yml index 5bd207a020..24e51223ef 100644 --- a/cmd/zetae2e/config/localnet.yml +++ b/cmd/zetae2e/config/localnet.yml @@ -16,10 +16,14 @@ additional_accounts: bech32_address: "zeta13t3zjxvwec7g38q8mdjga37rpes9zkfvv7tkn2" evm_address: "0x8Ae229198eCE3c889C07DB648Ec7C30E6051592c" private_key: "105460aebf71b10bfdb710ef5aa6d2932ee6ff6fc317ac9c24e0979903b10a5d" - user_bitcoin: + user_bitcoin_deposit: bech32_address: "zeta19q7czqysah6qg0n4y3l2a08gfzqxydla492v80" evm_address: "0x283d810090EdF4043E75247eAeBcE848806237fD" private_key: "7bb523963ee2c78570fb6113d886a4184d42565e8847f1cb639f5f5e2ef5b37a" + user_bitcoin_withdraw: + bech32_address: "zeta17e77anpmzhuuam67hg6x3mtqrulqh80z9chv70" + evm_address: "0xf67deecc3B15F9CEeF5eba3468ed601f3e0B9de2" + private_key: "2b3306a8ac43dbf0e350b87876c131e7e12bd49563a16de9ce8aeb664b94d559" user_solana: bech32_address: "zeta1zqlajgj0qr8rqylf2c572t0ux8vqt45d4zngpm" evm_address: "0x103FD9224F00ce3013e95629e52DFc31D805D68d" diff --git a/cmd/zetae2e/local/bitcoin.go b/cmd/zetae2e/local/bitcoin.go index f28f182877..900c9c3792 100644 --- a/cmd/zetae2e/local/bitcoin.go +++ b/cmd/zetae2e/local/bitcoin.go @@ -5,55 +5,116 @@ import ( "time" "github.com/fatih/color" + "github.com/stretchr/testify/require" "github.com/zeta-chain/node/e2e/config" "github.com/zeta-chain/node/e2e/e2etests" "github.com/zeta-chain/node/e2e/runner" + "github.com/zeta-chain/node/testutil" ) -// bitcoinTestRoutine runs Bitcoin related e2e tests -func bitcoinTestRoutine( +// initBitcoinTestRunners initializes Bitcoin deposit and withdraw test runners +func initBitcoinTestRunners( conf config.Config, deployerRunner *runner.E2ERunner, verbose bool, - initBitcoinNetwork bool, - testNames ...string, -) func() error { - return func() (err error) { - account := conf.AdditionalAccounts.UserBitcoin - // initialize runner for bitcoin test - bitcoinRunner, err := initTestRunner( - "bitcoin", - conf, - deployerRunner, - account, - runner.NewLogger(verbose, color.FgYellow, "bitcoin"), - ) - if err != nil { - return err - } + initNetwork bool, + depositTests []string, + withdrawTests []string, +) (func() error, func() error) { + // initialize runner for deposit tests + // deposit tests need Bitcoin node wallet to handle UTXOs + account := conf.AdditionalAccounts.UserBitcoinDeposit + runnerDeposit := initBitcoinRunner( + "btc_deposit", + account, + conf, + deployerRunner, + color.FgYellow, + verbose, + initNetwork, + true, + ) - bitcoinRunner.Logger.Print("🏃 starting Bitcoin tests") - startTime := time.Now() + // initialize runner for withdraw tests + // withdraw tests DON'T use Bitcoin node wallet + account = conf.AdditionalAccounts.UserBitcoinWithdraw + runnerWithdraw := initBitcoinRunner( + "btc_withdraw", + account, + conf, + deployerRunner, + color.FgHiYellow, + verbose, + initNetwork, + false, + ) + + // initialize funds + // send BTC to TSS for gas fees and to tester ZEVM address + if initNetwork { + // mine 101 blocks to ensure the BTC rewards are spendable + // Note: the block rewards can be sent to any address in here + _, err := runnerDeposit.GenerateToAddressIfLocalBitcoin(101, runnerDeposit.BTCDeployerAddress) + require.NoError(runnerDeposit, err) - // funding the account + // send BTC to ZEVM addresses + runnerDeposit.DepositBTC(runnerDeposit.EVMAddress()) + runnerDeposit.DepositBTC(runnerWithdraw.EVMAddress()) + } + + // create test routines + routineDeposit := createBitcoinTestRoutine(runnerDeposit, depositTests) + routineWithdraw := createBitcoinTestRoutine(runnerWithdraw, withdrawTests) + + return routineDeposit, routineWithdraw +} + +// initBitcoinRunner initializes the Bitcoin runner for given test name and account +func initBitcoinRunner( + name string, + account config.Account, + conf config.Config, + deployerRunner *runner.E2ERunner, + printColor color.Attribute, + verbose, initNetwork, createWallet bool, +) *runner.E2ERunner { + // initialize runner for bitcoin test + runner, err := initTestRunner(name, conf, deployerRunner, account, runner.NewLogger(verbose, printColor, name)) + testutil.NoError(err) + + // setup TSS address and setup deployer wallet + runner.SetupBitcoinAccounts(createWallet) + + // initialize funds + if initNetwork { + // send some BTC block rewards to the deployer address + _, err = runner.GenerateToAddressIfLocalBitcoin(4, runner.BTCDeployerAddress) + require.NoError(runner, err) + + // send ERC20 token on EVM txERC20Send := deployerRunner.SendERC20OnEvm(account.EVMAddress(), 1000) - bitcoinRunner.WaitForTxReceiptOnEvm(txERC20Send) + runner.WaitForTxReceiptOnEvm(txERC20Send) - // depositing the necessary tokens on ZetaChain - txEtherDeposit := bitcoinRunner.DepositEther() - txERC20Deposit := bitcoinRunner.DepositERC20() + // deposit ETH and ERC20 tokens on ZetaChain + txEtherDeposit := runner.DepositEther() + txERC20Deposit := runner.DepositERC20() - bitcoinRunner.WaitForMinedCCTX(txEtherDeposit) - bitcoinRunner.WaitForMinedCCTX(txERC20Deposit) + runner.WaitForMinedCCTX(txEtherDeposit) + runner.WaitForMinedCCTX(txERC20Deposit) + } - bitcoinRunner.SetupBitcoinAccount(initBitcoinNetwork) - bitcoinRunner.DepositBTC() + return runner +} - // run bitcoin test - // Note: due to the extensive block generation in Bitcoin localnet, block header test is run first - // to make it faster to catch up with the latest block header - testsToRun, err := bitcoinRunner.GetE2ETestsToRunByName( +// createBitcoinTestRoutine creates a test routine for given test names +func createBitcoinTestRoutine(r *runner.E2ERunner, testNames []string) func() error { + return func() (err error) { + r.Logger.Print("🏃 starting bitcoin tests") + startTime := time.Now() + + // run bitcoin tests + testsToRun, err := r.GetE2ETestsToRunByName( e2etests.AllE2ETests, testNames..., ) @@ -61,15 +122,11 @@ func bitcoinTestRoutine( return fmt.Errorf("bitcoin tests failed: %v", err) } - if err := bitcoinRunner.RunE2ETests(testsToRun); err != nil { + if err := r.RunE2ETests(testsToRun); err != nil { return fmt.Errorf("bitcoin tests failed: %v", err) } - if err := bitcoinRunner.CheckBtcTSSBalance(); err != nil { - return err - } - - bitcoinRunner.Logger.Print("🍾 Bitcoin tests completed in %s", time.Since(startTime).String()) + r.Logger.Print("🍾 bitcoin tests completed in %s", time.Since(startTime).String()) return err } diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index 9b818ea8c0..9858ac6382 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -313,7 +313,8 @@ func localE2ETest(cmd *cobra.Command, _ []string) { e2etests.TestMessagePassingEVMtoZEVMRevertFailName, } - bitcoinTests := []string{ + // btc withdraw tests are those that need a Bitcoin node wallet to send UTXOs + bitcoinDepositTests := []string{ e2etests.TestBitcoinDonationName, e2etests.TestBitcoinDepositName, e2etests.TestBitcoinDepositAndCallName, @@ -323,17 +324,19 @@ func localE2ETest(cmd *cobra.Command, _ []string) { e2etests.TestBitcoinStdMemoDepositAndCallRevertName, e2etests.TestBitcoinStdMemoDepositAndCallRevertOtherAddressName, e2etests.TestBitcoinStdMemoInscribedDepositAndCallName, + e2etests.TestCrosschainSwapName, + } + bitcoinWithdrawTests := []string{ e2etests.TestBitcoinWithdrawSegWitName, e2etests.TestBitcoinWithdrawInvalidAddressName, e2etests.TestZetaWithdrawBTCRevertName, - e2etests.TestCrosschainSwapName, } - bitcoinAdvancedTests := []string{ + bitcoinWithdrawTestsAdvanced := []string{ e2etests.TestBitcoinWithdrawTaprootName, e2etests.TestBitcoinWithdrawLegacyName, - e2etests.TestBitcoinWithdrawMultipleName, e2etests.TestBitcoinWithdrawP2SHName, e2etests.TestBitcoinWithdrawP2WSHName, + e2etests.TestBitcoinWithdrawMultipleName, e2etests.TestBitcoinWithdrawRestrictedName, } ethereumTests := []string{ @@ -367,7 +370,7 @@ func localE2ETest(cmd *cobra.Command, _ []string) { erc20Tests = append(erc20Tests, erc20AdvancedTests...) zetaTests = append(zetaTests, zetaAdvancedTests...) zevmMPTests = append(zevmMPTests, zevmMPAdvancedTests...) - bitcoinTests = append(bitcoinTests, bitcoinAdvancedTests...) + bitcoinWithdrawTests = append(bitcoinWithdrawTests, bitcoinWithdrawTestsAdvanced...) ethereumTests = append(ethereumTests, ethereumAdvancedTests...) } @@ -375,7 +378,16 @@ func localE2ETest(cmd *cobra.Command, _ []string) { eg.Go(erc20TestRoutine(conf, deployerRunner, verbose, erc20Tests...)) eg.Go(zetaTestRoutine(conf, deployerRunner, verbose, zetaTests...)) eg.Go(zevmMPTestRoutine(conf, deployerRunner, verbose, zevmMPTests...)) - eg.Go(bitcoinTestRoutine(conf, deployerRunner, verbose, !skipBitcoinSetup, bitcoinTests...)) + runnerDeposit, runnerWithdraw := initBitcoinTestRunners( + conf, + deployerRunner, + verbose, + !skipBitcoinSetup, + bitcoinDepositTests, + bitcoinWithdrawTests, + ) + eg.Go(runnerDeposit) + eg.Go(runnerWithdraw) eg.Go(ethereumTestRoutine(conf, deployerRunner, verbose, ethereumTests...)) } @@ -470,7 +482,7 @@ func localE2ETest(cmd *cobra.Command, _ []string) { } // if all tests pass, cancel txs priority monitoring and check if tx priority is not correct in some blocks - logger.Print("⏳ e2e tests passed,checking tx priority") + logger.Print("⏳ e2e tests passed, checking tx priority") monitorPriorityCancel() if err := <-txPriorityErrCh; err != nil && errors.Is(err, errWrongTxPriority) { logger.Print("❌ %v", err) @@ -483,10 +495,15 @@ func localE2ETest(cmd *cobra.Command, _ []string) { if testTSSMigration { TSSMigration(deployerRunner, logger, verbose, conf) } + // Verify that there are no trackers left over after tests complete if !skipTrackerCheck { deployerRunner.EnsureNoTrackers() } + + // Verify that the balance of restricted address is zero + deployerRunner.EnsureZeroBalanceOnRestrictedAddressZEVM() + // print and validate report networkReport, err := deployerRunner.GenerateNetworkReport() if err != nil { diff --git a/cmd/zetae2e/setup_bitcoin.go b/cmd/zetae2e/setup_bitcoin.go index 622855a027..a51d8880b0 100644 --- a/cmd/zetae2e/setup_bitcoin.go +++ b/cmd/zetae2e/setup_bitcoin.go @@ -58,7 +58,7 @@ func runSetupBitcoin(_ *cobra.Command, args []string) error { return err } - r.SetupBitcoinAccount(true) + r.SetupBitcoinAccounts(true) logger.Print("* BTC setup done") diff --git a/contrib/localnet/orchestrator/start-zetae2e.sh b/contrib/localnet/orchestrator/start-zetae2e.sh index 33f0d4e956..5d8c9c5586 100644 --- a/contrib/localnet/orchestrator/start-zetae2e.sh +++ b/contrib/localnet/orchestrator/start-zetae2e.sh @@ -98,8 +98,11 @@ fund_eth_from_config '.additional_accounts.user_zeta_test.evm_address' 10000 "ze # unlock zevm message passing tester accounts fund_eth_from_config '.additional_accounts.user_zevm_mp_test.evm_address' 10000 "zevm mp tester" -# unlock bitcoin tester accounts -fund_eth_from_config '.additional_accounts.user_bitcoin.evm_address' 10000 "bitcoin tester" +# unlock bitcoin deposit tester accounts +fund_eth_from_config '.additional_accounts.user_bitcoin_deposit.evm_address' 10000 "bitcoin deposit tester" + +# unlock bitcoin withdraw tester accounts +fund_eth_from_config '.additional_accounts.user_bitcoin_withdraw.evm_address' 10000 "bitcoin withdraw tester" # unlock solana tester accounts fund_eth_from_config '.additional_accounts.user_solana.evm_address' 10000 "solana tester" diff --git a/contrib/localnet/scripts/start-zetacored.sh b/contrib/localnet/scripts/start-zetacored.sh index c0e8424738..38047a6b46 100755 --- a/contrib/localnet/scripts/start-zetacored.sh +++ b/contrib/localnet/scripts/start-zetacored.sh @@ -242,8 +242,11 @@ then # zeta tester address=$(yq -r '.additional_accounts.user_zeta_test.bech32_address' /root/config.yml) zetacored add-genesis-account "$address" 100000000000000000000000000azeta -# bitcoin tester - address=$(yq -r '.additional_accounts.user_bitcoin.bech32_address' /root/config.yml) +# bitcoin deposit tester + address=$(yq -r '.additional_accounts.user_bitcoin_deposit.bech32_address' /root/config.yml) + zetacored add-genesis-account "$address" 100000000000000000000000000azeta +# bitcoin withdraw tester + address=$(yq -r '.additional_accounts.user_bitcoin_withdraw.bech32_address' /root/config.yml) zetacored add-genesis-account "$address" 100000000000000000000000000azeta # solana tester address=$(yq -r '.additional_accounts.user_solana.bech32_address' /root/config.yml) diff --git a/e2e/config/config.go b/e2e/config/config.go index 15ca4a1f2c..33a8c2bea8 100644 --- a/e2e/config/config.go +++ b/e2e/config/config.go @@ -61,20 +61,21 @@ type Account struct { // AdditionalAccounts are extra accounts required to run specific tests type AdditionalAccounts struct { - UserERC20 Account `yaml:"user_erc20"` - UserZetaTest Account `yaml:"user_zeta_test"` - UserZEVMMPTest Account `yaml:"user_zevm_mp_test"` - UserBitcoin Account `yaml:"user_bitcoin"` - UserSolana Account `yaml:"user_solana"` - UserEther Account `yaml:"user_ether"` - UserMisc Account `yaml:"user_misc"` - UserAdmin Account `yaml:"user_admin"` - UserMigration Account `yaml:"user_migration"` // used for TSS migration, TODO: rename (https://github.com/zeta-chain/node/issues/2780) - UserPrecompile Account `yaml:"user_precompile"` - UserV2Ether Account `yaml:"user_v2_ether"` - UserV2ERC20 Account `yaml:"user_v2_erc20"` - UserV2EtherRevert Account `yaml:"user_v2_ether_revert"` - UserV2ERC20Revert Account `yaml:"user_v2_erc20_revert"` + UserERC20 Account `yaml:"user_erc20"` + UserZetaTest Account `yaml:"user_zeta_test"` + UserZEVMMPTest Account `yaml:"user_zevm_mp_test"` + UserBitcoinDeposit Account `yaml:"user_bitcoin_deposit"` + UserBitcoinWithdraw Account `yaml:"user_bitcoin_withdraw"` + UserSolana Account `yaml:"user_solana"` + UserEther Account `yaml:"user_ether"` + UserMisc Account `yaml:"user_misc"` + UserAdmin Account `yaml:"user_admin"` + UserMigration Account `yaml:"user_migration"` // used for TSS migration, TODO: rename (https://github.com/zeta-chain/node/issues/2780) + UserPrecompile Account `yaml:"user_precompile"` + UserV2Ether Account `yaml:"user_v2_ether"` + UserV2ERC20 Account `yaml:"user_v2_erc20"` + UserV2EtherRevert Account `yaml:"user_v2_ether_revert"` + UserV2ERC20Revert Account `yaml:"user_v2_erc20_revert"` } type PolicyAccounts struct { @@ -235,7 +236,8 @@ func (a AdditionalAccounts) AsSlice() []Account { a.UserERC20, a.UserZetaTest, a.UserZEVMMPTest, - a.UserBitcoin, + a.UserBitcoinDeposit, + a.UserBitcoinWithdraw, a.UserSolana, a.UserEther, a.UserMisc, @@ -314,7 +316,11 @@ func (c *Config) GenerateKeys() error { if err != nil { return err } - c.AdditionalAccounts.UserBitcoin, err = generateAccount() + c.AdditionalAccounts.UserBitcoinDeposit, err = generateAccount() + if err != nil { + return err + } + c.AdditionalAccounts.UserBitcoinWithdraw, err = generateAccount() if err != nil { return err } diff --git a/e2e/e2etests/test_bitcoin_deposit.go b/e2e/e2etests/test_bitcoin_deposit.go index 590a5c81d8..59df40f9b6 100644 --- a/e2e/e2etests/test_bitcoin_deposit.go +++ b/e2e/e2etests/test_bitcoin_deposit.go @@ -13,8 +13,6 @@ func TestBitcoinDeposit(r *runner.E2ERunner, args []string) { depositAmount := parseFloat(r, args[0]) - r.SetBtcAddress(r.Name, false) - txHash := r.DepositBTCWithAmount(depositAmount, nil) // wait for the cctx to be mined diff --git a/e2e/e2etests/test_bitcoin_deposit_and_call_revert.go b/e2e/e2etests/test_bitcoin_deposit_and_call_revert.go index eed10485bf..0c0e45c77f 100644 --- a/e2e/e2etests/test_bitcoin_deposit_and_call_revert.go +++ b/e2e/e2etests/test_bitcoin_deposit_and_call_revert.go @@ -11,10 +11,6 @@ import ( ) func TestBitcoinDepositAndCallRevert(r *runner.E2ERunner, args []string) { - // ARRANGE - // Given BTC address - r.SetBtcAddress(r.Name, false) - // Given "Live" BTC network stop := r.MineBlocksIfLocalBitcoin() defer stop() diff --git a/e2e/e2etests/test_bitcoin_deposit_call.go b/e2e/e2etests/test_bitcoin_deposit_call.go index d3d6917c59..0ddbcb0b27 100644 --- a/e2e/e2etests/test_bitcoin_deposit_call.go +++ b/e2e/e2etests/test_bitcoin_deposit_call.go @@ -13,10 +13,6 @@ import ( ) func TestBitcoinDepositAndCall(r *runner.E2ERunner, args []string) { - // ARRANGE - // Given BTC address - r.SetBtcAddress(r.Name, false) - // Given "Live" BTC network stop := r.MineBlocksIfLocalBitcoin() defer stop() diff --git a/e2e/e2etests/test_bitcoin_donation.go b/e2e/e2etests/test_bitcoin_donation.go index 1dd5a34859..91b39ebff3 100644 --- a/e2e/e2etests/test_bitcoin_donation.go +++ b/e2e/e2etests/test_bitcoin_donation.go @@ -12,10 +12,6 @@ import ( ) func TestBitcoinDonation(r *runner.E2ERunner, args []string) { - // ARRANGE - // Given BTC address - r.SetBtcAddress(r.Name, false) - // Given "Live" BTC network stop := r.MineBlocksIfLocalBitcoin() defer stop() diff --git a/e2e/e2etests/test_bitcoin_std_deposit.go b/e2e/e2etests/test_bitcoin_std_deposit.go index e90b23d64a..b822487f21 100644 --- a/e2e/e2etests/test_bitcoin_std_deposit.go +++ b/e2e/e2etests/test_bitcoin_std_deposit.go @@ -14,9 +14,6 @@ import ( ) func TestBitcoinStdMemoDeposit(r *runner.E2ERunner, args []string) { - // setup deployer BTC address - r.SetBtcAddress(r.Name, false) - // start mining blocks if local bitcoin stop := r.MineBlocksIfLocalBitcoin() defer stop() diff --git a/e2e/e2etests/test_bitcoin_std_deposit_and_call.go b/e2e/e2etests/test_bitcoin_std_deposit_and_call.go index 7a9c6ca255..2cdd7caa3d 100644 --- a/e2e/e2etests/test_bitcoin_std_deposit_and_call.go +++ b/e2e/e2etests/test_bitcoin_std_deposit_and_call.go @@ -14,9 +14,6 @@ import ( ) func TestBitcoinStdMemoDepositAndCall(r *runner.E2ERunner, args []string) { - // setup deployer BTC address - r.SetBtcAddress(r.Name, false) - // start mining blocks if local bitcoin stop := r.MineBlocksIfLocalBitcoin() defer stop() diff --git a/e2e/e2etests/test_bitcoin_std_deposit_and_call_revert.go b/e2e/e2etests/test_bitcoin_std_deposit_and_call_revert.go index 76bf128aad..96b4186d8f 100644 --- a/e2e/e2etests/test_bitcoin_std_deposit_and_call_revert.go +++ b/e2e/e2etests/test_bitcoin_std_deposit_and_call_revert.go @@ -11,10 +11,6 @@ import ( ) func TestBitcoinStdMemoDepositAndCallRevert(r *runner.E2ERunner, args []string) { - // ARRANGE - // Given BTC address - r.SetBtcAddress(r.Name, false) - // Start mining blocks stop := r.MineBlocksIfLocalBitcoin() defer stop() diff --git a/e2e/e2etests/test_bitcoin_std_deposit_and_call_revert_other_address.go b/e2e/e2etests/test_bitcoin_std_deposit_and_call_revert_other_address.go index c6da1b1696..8ecb3b0d5f 100644 --- a/e2e/e2etests/test_bitcoin_std_deposit_and_call_revert_other_address.go +++ b/e2e/e2etests/test_bitcoin_std_deposit_and_call_revert_other_address.go @@ -12,10 +12,6 @@ import ( ) func TestBitcoinStdMemoDepositAndCallRevertOtherAddress(r *runner.E2ERunner, args []string) { - // ARRANGE - // Given BTC address - r.SetBtcAddress(r.Name, false) - // Start mining blocks stop := r.MineBlocksIfLocalBitcoin() defer stop() diff --git a/e2e/e2etests/test_bitcoin_std_memo_inscribed_deposit_and_call.go b/e2e/e2etests/test_bitcoin_std_memo_inscribed_deposit_and_call.go index c9a5d7af31..df23ae8383 100644 --- a/e2e/e2etests/test_bitcoin_std_memo_inscribed_deposit_and_call.go +++ b/e2e/e2etests/test_bitcoin_std_memo_inscribed_deposit_and_call.go @@ -14,10 +14,6 @@ import ( ) func TestBitcoinStdMemoInscribedDepositAndCall(r *runner.E2ERunner, args []string) { - // ARRANGE - // Given BTC address - r.SetBtcAddress(r.Name, false) - // Start mining blocks stop := r.MineBlocksIfLocalBitcoin() defer stop() diff --git a/e2e/e2etests/test_bitcoin_withdraw_invalid_address.go b/e2e/e2etests/test_bitcoin_withdraw_invalid_address.go index 5c7254a3a1..aae4b4a446 100644 --- a/e2e/e2etests/test_bitcoin_withdraw_invalid_address.go +++ b/e2e/e2etests/test_bitcoin_withdraw_invalid_address.go @@ -15,8 +15,6 @@ func TestBitcoinWithdrawToInvalidAddress(r *runner.E2ERunner, args []string) { withdrawalAmount := parseFloat(r, args[0]) amount := btcAmountFromFloat64(r, withdrawalAmount) - r.SetBtcAddress(r.Name, false) - withdrawToInvalidAddress(r, amount) } diff --git a/e2e/e2etests/test_bitcoin_withdraw_legacy.go b/e2e/e2etests/test_bitcoin_withdraw_legacy.go index 091bb63531..ba33c8b24d 100644 --- a/e2e/e2etests/test_bitcoin_withdraw_legacy.go +++ b/e2e/e2etests/test_bitcoin_withdraw_legacy.go @@ -11,8 +11,6 @@ func TestBitcoinWithdrawLegacy(r *runner.E2ERunner, args []string) { // check length of arguments require.Len(r, args, 2) - r.SetBtcAddress(r.Name, false) - // parse arguments and withdraw BTC receiver, amount := parseBitcoinWithdrawArgs(r, args, defaultReceiver) diff --git a/e2e/e2etests/test_bitcoin_withdraw_multiple.go b/e2e/e2etests/test_bitcoin_withdraw_multiple.go index 09d77fde80..642f1d49dd 100644 --- a/e2e/e2etests/test_bitcoin_withdraw_multiple.go +++ b/e2e/e2etests/test_bitcoin_withdraw_multiple.go @@ -18,9 +18,6 @@ func WithdrawBitcoinMultipleTimes(r *runner.E2ERunner, args []string) { times = parseInt(r, args[1]) ) - // Given BTC address set - r.SetBtcAddress(r.Name, false) - // Given a receiver receiver, err := chains.DecodeBtcAddress(defaultReceiver, r.GetBitcoinChainID()) require.NoError(r, err) diff --git a/e2e/e2etests/test_bitcoin_withdraw_p2sh.go b/e2e/e2etests/test_bitcoin_withdraw_p2sh.go index 248ce14ec5..e3eda5f9d5 100644 --- a/e2e/e2etests/test_bitcoin_withdraw_p2sh.go +++ b/e2e/e2etests/test_bitcoin_withdraw_p2sh.go @@ -11,8 +11,6 @@ func TestBitcoinWithdrawP2SH(r *runner.E2ERunner, args []string) { // check length of arguments require.Len(r, args, 2) - r.SetBtcAddress(r.Name, false) - // parse arguments and withdraw BTC defaultReceiver := "2N6AoUj3KPS7wNGZXuCckh8YEWcSYNsGbqd" receiver, amount := parseBitcoinWithdrawArgs(r, args, defaultReceiver) diff --git a/e2e/e2etests/test_bitcoin_withdraw_p2wsh.go b/e2e/e2etests/test_bitcoin_withdraw_p2wsh.go index 1865018628..41b65824bc 100644 --- a/e2e/e2etests/test_bitcoin_withdraw_p2wsh.go +++ b/e2e/e2etests/test_bitcoin_withdraw_p2wsh.go @@ -10,8 +10,6 @@ import ( func TestBitcoinWithdrawP2WSH(r *runner.E2ERunner, args []string) { require.Len(r, args, 2) - r.SetBtcAddress(r.Name, false) - // parse arguments and withdraw BTC defaultReceiver := "bcrt1qm9mzhyky4w853ft2ms6dtqdyyu3z2tmrq8jg8xglhyuv0dsxzmgs2f0sqy" receiver, amount := parseBitcoinWithdrawArgs(r, args, defaultReceiver) diff --git a/e2e/e2etests/test_bitcoin_withdraw_restricted_address.go b/e2e/e2etests/test_bitcoin_withdraw_restricted_address.go index 84bd11b8de..87610cac09 100644 --- a/e2e/e2etests/test_bitcoin_withdraw_restricted_address.go +++ b/e2e/e2etests/test_bitcoin_withdraw_restricted_address.go @@ -16,8 +16,6 @@ func TestBitcoinWithdrawRestricted(r *runner.E2ERunner, args []string) { withdrawalAmount := parseFloat(r, args[0]) amount := btcAmountFromFloat64(r, withdrawalAmount) - r.SetBtcAddress(r.Name, false) - withdrawBitcoinRestricted(r, amount) } diff --git a/e2e/e2etests/test_bitcoin_withdraw_segwit.go b/e2e/e2etests/test_bitcoin_withdraw_segwit.go index fe30c118f8..6d5c7d4516 100644 --- a/e2e/e2etests/test_bitcoin_withdraw_segwit.go +++ b/e2e/e2etests/test_bitcoin_withdraw_segwit.go @@ -10,8 +10,6 @@ import ( func TestBitcoinWithdrawSegWit(r *runner.E2ERunner, args []string) { require.Len(r, args, 2) - r.SetBtcAddress(r.Name, false) - // parse arguments defaultReceiver := r.BTCDeployerAddress.EncodeAddress() receiver, amount := parseBitcoinWithdrawArgs(r, args, defaultReceiver) diff --git a/e2e/e2etests/test_bitcoin_withdraw_taproot.go b/e2e/e2etests/test_bitcoin_withdraw_taproot.go index f675a88c43..5935bb1986 100644 --- a/e2e/e2etests/test_bitcoin_withdraw_taproot.go +++ b/e2e/e2etests/test_bitcoin_withdraw_taproot.go @@ -10,8 +10,6 @@ import ( func TestBitcoinWithdrawTaproot(r *runner.E2ERunner, args []string) { require.Len(r, args, 2) - r.SetBtcAddress(r.Name, false) - // parse arguments and withdraw BTC defaultReceiver := "bcrt1pqqqsyqcyq5rqwzqfpg9scrgwpugpzysnzs23v9ccrydpk8qarc0sj9hjuh" receiver, amount := parseBitcoinWithdrawArgs(r, args, defaultReceiver) diff --git a/e2e/e2etests/test_migrate_tss.go b/e2e/e2etests/test_migrate_tss.go index 3825228c14..067623a325 100644 --- a/e2e/e2etests/test_migrate_tss.go +++ b/e2e/e2etests/test_migrate_tss.go @@ -20,7 +20,6 @@ import ( ) func TestMigrateTSS(r *runner.E2ERunner, _ []string) { - r.SetBtcAddress(r.Name, false) stop := r.MineBlocksIfLocalBitcoin() defer stop() diff --git a/e2e/e2etests/test_stress_btc_deposit.go b/e2e/e2etests/test_stress_btc_deposit.go index bedf004bdf..ea15d4fa9b 100644 --- a/e2e/e2etests/test_stress_btc_deposit.go +++ b/e2e/e2etests/test_stress_btc_deposit.go @@ -20,8 +20,6 @@ func TestStressBTCDeposit(r *runner.E2ERunner, args []string) { depositAmount := parseFloat(r, args[0]) numDeposits := parseInt(r, args[1]) - r.SetBtcAddress(r.Name, false) - r.Logger.Print("starting stress test of %d deposits", numDeposits) // create a wait group to wait for all the deposits to complete diff --git a/e2e/e2etests/test_stress_btc_withdraw.go b/e2e/e2etests/test_stress_btc_withdraw.go index 52d60430d2..1c02d17bf0 100644 --- a/e2e/e2etests/test_stress_btc_withdraw.go +++ b/e2e/e2etests/test_stress_btc_withdraw.go @@ -22,8 +22,6 @@ func TestStressBTCWithdraw(r *runner.E2ERunner, args []string) { withdrawalAmount := parseFloat(r, args[0]) numWithdraws := parseInt(r, args[1]) - r.SetBtcAddress(r.Name, false) - r.Logger.Print("starting stress test of %d withdraws", numWithdraws) // create a wait group to wait for all the withdraws to complete diff --git a/e2e/runner/accounting.go b/e2e/runner/accounting.go index c47883c3f0..223ca9c8c4 100644 --- a/e2e/runner/accounting.go +++ b/e2e/runner/accounting.go @@ -33,15 +33,20 @@ type Response struct { Amount Amount `json:"amount"` } -func (r *E2ERunner) CheckZRC20ReserveAndSupply() error { - r.Logger.Info("Checking ZRC20 Reserve and Supply") - if err := r.checkEthTSSBalance(); err != nil { - return err - } - if err := r.checkERC20TSSBalance(); err != nil { - return err - } - return r.checkZetaTSSBalance() +func (r *E2ERunner) CheckZRC20BalanceAndSupply() { + r.Logger.Info("Checking ZRC20 Balance vs. Supply") + + err := r.checkEthTSSBalance() + require.NoError(r, err, "ETH balance check failed") + + err = r.checkERC20TSSBalance() + require.NoError(r, err, "ERC20 balance check failed") + + err = r.checkZetaTSSBalance() + require.NoError(r, err, "ZETA balance check failed") + + err = r.CheckBtcTSSBalance() + require.NoError(r, err, "BTC balance check failed") } func (r *E2ERunner) checkEthTSSBalance() error { diff --git a/e2e/runner/balances.go b/e2e/runner/balances.go index 5def04090f..38ac61d70c 100644 --- a/e2e/runner/balances.go +++ b/e2e/runner/balances.go @@ -107,16 +107,7 @@ func (r *E2ERunner) GetAccountBalances(skipBTC bool) (AccountBalances, error) { // GetBitcoinBalance returns the spendable BTC balance of the BTC address func (r *E2ERunner) GetBitcoinBalance() (string, error) { - addr, _, err := r.GetBtcAddress() - if err != nil { - return "", fmt.Errorf("failed to get BTC address: %w", err) - } - - address, err := btcutil.DecodeAddress(addr, r.BitcoinParams) - if err != nil { - return "", fmt.Errorf("failed to decode BTC address: %w", err) - } - + address, _ := r.GetBtcAddress() total, err := r.GetBitcoinBalanceByAddress(address) if err != nil { return "", err diff --git a/e2e/runner/bitcoin.go b/e2e/runner/bitcoin.go index 047e97139b..bec7a1e907 100644 --- a/e2e/runner/bitcoin.go +++ b/e2e/runner/bitcoin.go @@ -14,6 +14,7 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" "github.com/rs/zerolog/log" "github.com/stretchr/testify/require" @@ -106,7 +107,7 @@ func (r *E2ERunner) DepositBTCWithAmount(amount float64, memo *memo.InboundMemo) if memo != nil { txHash, err = r.DepositBTCWithStandardMemo(amount, utxos, memo) } else { - txHash, err = r.DepositBTCWithLegacyMemo(amount, utxos) + txHash, err = r.DepositBTCWithLegacyMemo(amount, utxos, r.EVMAddress()) } require.NoError(r, err) @@ -115,8 +116,9 @@ func (r *E2ERunner) DepositBTCWithAmount(amount float64, memo *memo.InboundMemo) return txHash } -// DepositBTC deposits BTC on ZetaChain -func (r *E2ERunner) DepositBTC() { +// DepositBTC deposits BTC from the Bitcoin node wallet into ZetaChain. +// Note: This function only works for node wallet based deployer account. +func (r *E2ERunner) DepositBTC(receiver common.Address) { r.Logger.Print("⏳ depositing BTC into ZEVM") startTime := time.Now() defer func() { @@ -130,6 +132,7 @@ func (r *E2ERunner) DepositBTC() { spendableAmount := 0.0 spendableUTXOs := 0 for _, utxo := range utxos { + // 'Spendable' indicates whether we have the private keys to spend this output if utxo.Spendable { spendableAmount += utxo.Amount spendableUTXOs++ @@ -142,27 +145,24 @@ func (r *E2ERunner) DepositBTC() { r.Logger.Info("ListUnspent:") r.Logger.Info(" spendableAmount: %f", spendableAmount) r.Logger.Info(" spendableUTXOs: %d", spendableUTXOs) - r.Logger.Info("Now sending two txs to TSS address...") - - // send two transactions to the TSS address - amount1 := 1.1 + zetabitcoin.DefaultDepositorFee - _, err = r.DepositBTCWithLegacyMemo(amount1, utxos[:2]) - require.NoError(r, err) + r.Logger.Info("Now sending two txs to TSS address and tester ZEVM address...") - amount2 := 0.05 + zetabitcoin.DefaultDepositorFee - txHash2, err := r.DepositBTCWithLegacyMemo(amount2, utxos[2:4]) + // send initial BTC to the tester ZEVM address + amount := 1.15 + zetabitcoin.DefaultDepositorFee + txHash, err := r.DepositBTCWithLegacyMemo(amount, utxos[:2], receiver) require.NoError(r, err) // send a donation to the TSS address to compensate for the funds minted automatically during pool creation // and prevent accounting errors - _, err = r.SendToTSSFromDeployerWithMemo(0.11, utxos[4:5], []byte(constant.DonationMessage)) + // it also serves as gas fee for the TSS to send BTC to other addresses + _, err = r.SendToTSSFromDeployerWithMemo(0.11, utxos[2:4], []byte(constant.DonationMessage)) require.NoError(r, err) r.Logger.Info("testing if the deposit into BTC ZRC20 is successful...") cctx := utils.WaitCctxMinedByInboundHash( r.Ctx, - txHash2.String(), + txHash.String(), r.CctxClient, r.Logger, r.CctxTimeout, @@ -180,11 +180,12 @@ func (r *E2ERunner) DepositBTC() { func (r *E2ERunner) DepositBTCWithLegacyMemo( amount float64, inputUTXOs []btcjson.ListUnspentResult, + receiver common.Address, ) (*chainhash.Hash, error) { r.Logger.Info("⏳ depositing BTC into ZEVM with legacy memo") // payload is not needed for pure deposit - memoBytes := r.EVMAddress().Bytes() + memoBytes := receiver.Bytes() return r.SendToTSSFromDeployerWithMemo(amount, inputUTXOs, memoBytes) } @@ -431,7 +432,7 @@ func (r *E2ERunner) MineBlocksIfLocalBitcoin() func() { _, err := r.GenerateToAddressIfLocalBitcoin(1, r.BTCDeployerAddress) require.NoError(r, err) - time.Sleep(3 * time.Second) + time.Sleep(6 * time.Second) } } }() diff --git a/e2e/runner/logger.go b/e2e/runner/logger.go index 24f9c8f7bc..ba8053de60 100644 --- a/e2e/runner/logger.go +++ b/e2e/runner/logger.go @@ -15,7 +15,7 @@ import ( const ( loggerSeparator = " | " - padding = 10 + padding = 12 ) // Logger is a wrapper around log.Logger that adds verbosity diff --git a/e2e/runner/require.go b/e2e/runner/require.go index 9e296bf748..98496c5f25 100644 --- a/e2e/runner/require.go +++ b/e2e/runner/require.go @@ -24,8 +24,8 @@ func (r *E2ERunner) EnsureNoTrackers() { require.Empty(r, res.OutboundTracker, "there should be no trackers at the end of the test") } -// EnsureZeroBalanceAddressZEVM ensures that the balance of the address is zero in the ZEVM -func (r *E2ERunner) EnsureZeroBalanceAddressZEVM() { +// EnsureZeroBalanceAddressZEVM ensures that the balance of the restricted address is zero in the ZEVM +func (r *E2ERunner) EnsureZeroBalanceOnRestrictedAddressZEVM() { restrictedAddress := ethcommon.HexToAddress(sample.RestrictedEVMAddressTest) // ensure ZETA balance is zero diff --git a/e2e/runner/run.go b/e2e/runner/run.go index c05129b775..fde3de2277 100644 --- a/e2e/runner/run.go +++ b/e2e/runner/run.go @@ -26,11 +26,9 @@ func (r *E2ERunner) RunE2ETest(e2eTest E2ETest, checkAccounting bool) error { } e2eTest.E2ETest(r, args) - //check supplies + // check zrc20 balance vs. supply if checkAccounting { - if err := r.CheckZRC20ReserveAndSupply(); err != nil { - return err - } + r.CheckZRC20BalanceAndSupply() } r.Logger.Print("✅ completed in %s - %s", time.Since(startTime), e2eTest.Description) diff --git a/e2e/runner/setup_bitcoin.go b/e2e/runner/setup_bitcoin.go index 7b4d794f87..88d516e1b7 100644 --- a/e2e/runner/setup_bitcoin.go +++ b/e2e/runner/setup_bitcoin.go @@ -26,78 +26,65 @@ func (r *E2ERunner) AddTSSToNode() { require.NoError(r, err) } -func (r *E2ERunner) SetupBitcoinAccount(initNetwork bool) { - r.Logger.Print("⚙️ setting up Bitcoin account") +// SetupBitcoinAccounts sets up the TSS account and deployer account +func (r *E2ERunner) SetupBitcoinAccounts(createWallet bool) { + r.Logger.Info("⚙️ setting up Bitcoin account") startTime := time.Now() defer func() { r.Logger.Print("✅ Bitcoin account setup in %s", time.Since(startTime)) }() - _, err := r.BtcRPCClient.CreateWallet(r.Name, rpcclient.WithCreateWalletBlank()) - if err != nil { - require.ErrorContains(r, err, "Database already exists") - } - - r.SetBtcAddress(r.Name, true) - - if initNetwork { - // import the TSS address - err = r.BtcRPCClient.ImportAddress(r.BTCTSSAddress.EncodeAddress()) - require.NoError(r, err) + // setup deployer address + r.SetupBtcAddress(r.Name, createWallet) - // mine some blocks to get some BTC into the deployer address - _, err = r.GenerateToAddressIfLocalBitcoin(101, r.BTCDeployerAddress) - require.NoError(r, err) + // import the TSS address to index TSS utxos and transactions + err := r.BtcRPCClient.ImportAddress(r.BTCTSSAddress.EncodeAddress()) + require.NoError(r, err) + r.Logger.Info("⚙️ imported BTC TSSAddress: %s", r.BTCTSSAddress.EncodeAddress()) - _, err = r.GenerateToAddressIfLocalBitcoin(4, r.BTCDeployerAddress) - require.NoError(r, err) - } + // import deployer address to index deployer utxos and transactions + err = r.BtcRPCClient.ImportAddress(r.BTCDeployerAddress.EncodeAddress()) + require.NoError(r, err) + r.Logger.Info("⚙️ imported BTCDeployerAddress: %s", r.BTCDeployerAddress.EncodeAddress()) } -// GetBtcAddress returns the BTC address of the deployer from its EVM private key -func (r *E2ERunner) GetBtcAddress() (string, string, error) { +// GetBtcAddress returns the BTC address of the deployer and private key in WIF format +func (r *E2ERunner) GetBtcAddress() (*btcutil.AddressWitnessPubKeyHash, *btcutil.WIF) { + // load configured private key skBytes, err := hex.DecodeString(r.Account.RawPrivateKey.String()) - if err != nil { - return "", "", err - } + require.NoError(r, err) + // create private key in WIF format sk, _ := btcec.PrivKeyFromBytes(skBytes) privkeyWIF, err := btcutil.NewWIF(sk, r.BitcoinParams, true) - if err != nil { - return "", "", err - } + require.NoError(r, err) + // derive address from private key address, err := btcutil.NewAddressWitnessPubKeyHash( btcutil.Hash160(privkeyWIF.SerializePubKey()), r.BitcoinParams, ) - if err != nil { - return "", "", err - } + require.NoError(r, err) - // return the string representation of the address - return address.EncodeAddress(), privkeyWIF.String(), nil + return address, privkeyWIF } -// SetBtcAddress imports the deployer's private key into the Bitcoin node -func (r *E2ERunner) SetBtcAddress(name string, rescan bool) { - skBytes, err := hex.DecodeString(r.Account.RawPrivateKey.String()) - require.NoError(r, err) +// SetupBtcAddress setups the deployer Bitcoin address +func (r *E2ERunner) SetupBtcAddress(name string, setupWallet bool) { + // set the deployer address + address, privkeyWIF := r.GetBtcAddress() + r.BTCDeployerAddress = address - sk, _ := btcec.PrivKeyFromBytes(skBytes) - privkeyWIF, err := btcutil.NewWIF(sk, r.BitcoinParams, true) - require.NoError(r, err) + r.Logger.Info("BTCDeployerAddress: %s, %v", r.BTCDeployerAddress.EncodeAddress(), setupWallet) - if rescan { - err := r.BtcRPCClient.ImportPrivKeyRescan(privkeyWIF, name, true) + // import the deployer private key as a Bitcoin node wallet + if setupWallet { + _, err := r.BtcRPCClient.CreateWallet(r.Name, rpcclient.WithCreateWalletBlank()) + if err != nil { + require.ErrorContains(r, err, "Database already exists") + } + + err = r.BtcRPCClient.ImportPrivKeyRescan(privkeyWIF, name, true) require.NoError(r, err, "failed to execute ImportPrivKeyRescan") } - - r.BTCDeployerAddress, err = btcutil.NewAddressWitnessPubKeyHash( - btcutil.Hash160(privkeyWIF.PrivKey.PubKey().SerializeCompressed()), - r.BitcoinParams, - ) - require.NoError(r, err) - - r.Logger.Info("BTCDeployerAddress: %s", r.BTCDeployerAddress.EncodeAddress()) } diff --git a/x/observer/types/chain_params.go b/x/observer/types/chain_params.go index b1a5335e58..b76af2da49 100644 --- a/x/observer/types/chain_params.go +++ b/x/observer/types/chain_params.go @@ -290,7 +290,7 @@ func GetDefaultBtcRegtestChainParams() *ChainParams { WatchUtxoTicker: 1, InboundTicker: 1, OutboundTicker: 2, - OutboundScheduleInterval: 2, + OutboundScheduleInterval: 1, OutboundScheduleLookahead: 5, BallotThreshold: DefaultBallotThreshold, MinObserverDelegation: DefaultMinObserverDelegation,