diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 0213c3fdfc..aab15adf8f 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -43,6 +43,10 @@ on: type: boolean required: false default: false + tss-migration-test: + type: boolean + required: false + default: false concurrency: group: e2e-${{ github.head_ref || github.sha }} @@ -61,13 +65,14 @@ jobs: ADMIN_TESTS: ${{ steps.matrix-conditionals.outputs.ADMIN_TESTS }} PERFORMANCE_TESTS: ${{ steps.matrix-conditionals.outputs.PERFORMANCE_TESTS }} STATEFUL_DATA_TESTS: ${{ steps.matrix-conditionals.outputs.STATEFUL_DATA_TESTS }} + TSS_MIGRATION_TESTS: ${{ steps.matrix-conditionals.outputs.TSS_MIGRATION_TESTS }} + steps: # use api rather than event context to avoid race conditions (label added after push) - id: matrix-conditionals uses: actions/github-script@v7 with: script: | - console.log(context); if (context.eventName === 'pull_request') { const { data: pr } = await github.rest.pulls.get({ owner: context.repo.owner, @@ -83,6 +88,7 @@ jobs: core.setOutput('ADMIN_TESTS', labels.includes('ADMIN_TESTS')); core.setOutput('PERFORMANCE_TESTS', labels.includes('PERFORMANCE_TESTS')); core.setOutput('STATEFUL_DATA_TESTS', labels.includes('STATEFUL_DATA_TESTS')); + core.setOutput('TSS_MIGRATION_TESTS', labels.includes('TSS_MIGRATION_TESTS')); } else if (context.eventName === 'merge_group') { core.setOutput('DEFAULT_TESTS', true); } else if (context.eventName === 'push' && context.ref === 'refs/heads/develop') { @@ -111,6 +117,7 @@ jobs: core.setOutput('ADMIN_TESTS', context.payload.inputs['admin-test']); core.setOutput('PERFORMANCE_TESTS', context.payload.inputs['performance-test']); core.setOutput('STATEFUL_DATA_TESTS', context.payload.inputs['stateful-data-test']); + core.setOutput('TSS_MIGRATION_TESTS', context.payload.inputs['tss-migration-test']); } e2e: @@ -140,6 +147,9 @@ jobs: - make-target: "start-e2e-import-mainnet-test" runs-on: buildjet-16vcpu-ubuntu-2204 run: ${{ needs.matrix-conditionals.outputs.STATEFUL_DATA_TESTS == 'true' }} + - make-target: "start-tss-migration-test" + runs-on: ubuntu-20.04 + run: ${{ needs.matrix-conditionals.outputs.TSS_MIGRATION_TESTS == 'true' }} name: ${{ matrix.make-target }} uses: ./.github/workflows/reusable-e2e.yml with: @@ -150,7 +160,7 @@ jobs: # this allows you to set a required status check e2e-ok: runs-on: ubuntu-22.04 - needs: + needs: - matrix-conditionals - e2e if: always() diff --git a/Makefile b/Makefile index 0d5dd69435..93a11ccdd7 100644 --- a/Makefile +++ b/Makefile @@ -87,7 +87,7 @@ build-testnet-ubuntu: go.sum docker rm temp-container install: go.sum - @echo "--> Installing zetacored & zetaclientd" + @echo "--> Installing zetacored, zetaclientd, and zetaclientd-supervisor" @go install -mod=readonly $(BUILD_FLAGS) ./cmd/zetacored @go install -mod=readonly $(BUILD_FLAGS) ./cmd/zetaclientd @go install -mod=readonly $(BUILD_FLAGS) ./cmd/zetaclientd-supervisor @@ -258,6 +258,11 @@ start-stress-test: zetanode @echo "--> Starting stress test" cd contrib/localnet/ && $(DOCKER) compose --profile stress -f docker-compose.yml up -d +start-tss-migration-test: zetanode + @echo "--> Starting migration test" + export E2E_ARGS="--test-tss-migration" && \ + cd contrib/localnet/ && $(DOCKER) compose up -d + ############################################################################### ### Upgrade Tests ### ############################################################################### diff --git a/changelog.md b/changelog.md index 75b546b9b3..494d52f63f 100644 --- a/changelog.md +++ b/changelog.md @@ -81,6 +81,8 @@ * [2369](https://github.com/zeta-chain/node/pull/2369) - fix random cross-chain swap failure caused by using tiny UTXO * [2549](https://github.com/zeta-chain/node/pull/2459) - add separate accounts for each policy in e2e tests * [2415](https://github.com/zeta-chain/node/pull/2415) - add e2e test for upgrade and test admin functionalities +* [2440](https://github.com/zeta-chain/node/pull/2440) - Add e2e test for TSS migration + ### Fixes diff --git a/cmd/zetaclientd-supervisor/lib.go b/cmd/zetaclientd-supervisor/lib.go index e8f1b54470..71f492e88b 100644 --- a/cmd/zetaclientd-supervisor/lib.go +++ b/cmd/zetaclientd-supervisor/lib.go @@ -12,6 +12,7 @@ import ( "runtime" "strings" "sync" + "syscall" "time" "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" @@ -20,6 +21,7 @@ import ( "github.com/rs/zerolog" "google.golang.org/grpc" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/config" ) @@ -66,6 +68,7 @@ type zetaclientdSupervisor struct { upgradesDir string upgradePlanName string enableAutoDownload bool + restartChan chan os.Signal } func newZetaclientdSupervisor( @@ -81,19 +84,24 @@ func newZetaclientdSupervisor( if err != nil { return nil, fmt.Errorf("grpc dial: %w", err) } - + // these signals will result in the supervisor process only restarting zetaclientd + restartChan := make(chan os.Signal, 1) return &zetaclientdSupervisor{ zetacoredConn: conn, logger: logger, reloadSignals: make(chan bool, 1), upgradesDir: defaultUpgradesDir, enableAutoDownload: enableAutoDownload, + restartChan: restartChan, }, nil } func (s *zetaclientdSupervisor) Start(ctx context.Context) { go s.watchForVersionChanges(ctx) go s.handleCoreUpgradePlan(ctx) + go s.handleNewKeygen(ctx) + go s.handleNewTSSKeyGeneration(ctx) + go s.handleTSSUpdate(ctx) } func (s *zetaclientdSupervisor) WaitForReloadSignal(ctx context.Context) { @@ -169,6 +177,125 @@ func (s *zetaclientdSupervisor) watchForVersionChanges(ctx context.Context) { } } +func (s *zetaclientdSupervisor) handleTSSUpdate(ctx context.Context) { + maxRetries := 11 + retryInterval := 5 * time.Second + + // TODO : use retry library under pkg/retry + // https://github.com/zeta-chain/node/issues/2492 + for i := 0; i < maxRetries; i++ { + client := observertypes.NewQueryClient(s.zetacoredConn) + tss, err := client.TSS(ctx, &observertypes.QueryGetTSSRequest{}) + if err != nil { + s.logger.Warn().Err(err).Msg("unable to get original tss") + time.Sleep(retryInterval) + continue + } + i = 0 + for { + select { + case <-time.After(time.Second): + case <-ctx.Done(): + return + } + tssNew, err := client.TSS(ctx, &observertypes.QueryGetTSSRequest{}) + if err != nil { + s.logger.Warn().Err(err).Msg("unable to get tss") + continue + } + + if tssNew.TSS.TssPubkey == tss.TSS.TssPubkey { + continue + } + + tss = tssNew + s.logger.Info(). + Msgf("tss address is updated from %s to %s", tss.TSS.TssPubkey, tssNew.TSS.TssPubkey) + time.Sleep(6 * time.Second) + s.logger.Info().Msg("restarting zetaclientd to update tss address") + s.restartChan <- syscall.SIGHUP + } + } + s.logger.Warn().Msg("handleTSSUpdate exiting without success") +} + +func (s *zetaclientdSupervisor) handleNewTSSKeyGeneration(ctx context.Context) { + maxRetries := 11 + retryInterval := 5 * time.Second + + // TODO : use retry library under pkg/retry + for i := 0; i < maxRetries; i++ { + client := observertypes.NewQueryClient(s.zetacoredConn) + alltss, err := client.TssHistory(ctx, &observertypes.QueryTssHistoryRequest{}) + if err != nil { + s.logger.Warn().Err(err).Msg("unable to get tss original history") + time.Sleep(retryInterval) + continue + } + i = 0 + tssLenCurrent := len(alltss.TssList) + for { + select { + case <-time.After(time.Second): + case <-ctx.Done(): + return + } + tssListNew, err := client.TssHistory(ctx, &observertypes.QueryTssHistoryRequest{}) + if err != nil { + s.logger.Warn().Err(err).Msg("unable to get tss new history") + continue + } + tssLenUpdated := len(tssListNew.TssList) + + if tssLenUpdated == tssLenCurrent { + continue + } + if tssLenUpdated < tssLenCurrent { + tssLenCurrent = len(tssListNew.TssList) + continue + } + + tssLenCurrent = tssLenUpdated + s.logger.Info().Msgf("tss list updated from %d to %d", tssLenCurrent, tssLenUpdated) + time.Sleep(5 * time.Second) + s.logger.Info().Msg("restarting zetaclientd to update tss list") + s.restartChan <- syscall.SIGHUP + } + } + s.logger.Warn().Msg("handleNewTSSKeyGeneration exiting without success") +} + +func (s *zetaclientdSupervisor) handleNewKeygen(ctx context.Context) { + client := observertypes.NewQueryClient(s.zetacoredConn) + prevKeygenBlock := int64(0) + for { + select { + case <-time.After(time.Second): + case <-ctx.Done(): + return + } + resp, err := client.Keygen(ctx, &observertypes.QueryGetKeygenRequest{}) + if err != nil { + s.logger.Warn().Err(err).Msg("unable to get keygen") + continue + } + if resp.Keygen == nil { + s.logger.Warn().Err(err).Msg("keygen is nil") + continue + } + + if resp.Keygen.Status != observertypes.KeygenStatus_PendingKeygen { + continue + } + keygenBlock := resp.Keygen.BlockNumber + if prevKeygenBlock == keygenBlock { + continue + } + prevKeygenBlock = keygenBlock + s.logger.Info().Msgf("got new keygen at block %d", keygenBlock) + s.restartChan <- syscall.SIGHUP + } +} func (s *zetaclientdSupervisor) handleCoreUpgradePlan(ctx context.Context) { client := upgradetypes.NewQueryClient(s.zetacoredConn) diff --git a/cmd/zetaclientd-supervisor/main.go b/cmd/zetaclientd-supervisor/main.go index 6017050986..ee1e247be4 100644 --- a/cmd/zetaclientd-supervisor/main.go +++ b/cmd/zetaclientd-supervisor/main.go @@ -36,10 +36,6 @@ func main() { shutdownChan := make(chan os.Signal, 1) signal.Notify(shutdownChan, syscall.SIGINT, syscall.SIGTERM) - // these signals will result in the supervisor process only restarting zetaclientd - restartChan := make(chan os.Signal, 1) - signal.Notify(restartChan, syscall.SIGHUP) - hotkeyPassword, tssPassword, err := promptPasswords() if err != nil { logger.Error().Err(err).Msg("unable to get passwords") @@ -53,6 +49,8 @@ func main() { os.Exit(1) } supervisor.Start(ctx) + // listen for SIGHUP to trigger a restart of zetaclientd + signal.Notify(supervisor.restartChan, syscall.SIGHUP) shouldRestart := true for shouldRestart { @@ -82,7 +80,7 @@ func main() { select { case <-ctx.Done(): return nil - case sig := <-restartChan: + case sig := <-supervisor.restartChan: logger.Info().Msgf("got signal %d, sending SIGINT to zetaclientd", sig) case sig := <-shutdownChan: logger.Info().Msgf("got signal %d, shutting down", sig) diff --git a/cmd/zetaclientd/keygen_tss.go b/cmd/zetaclientd/keygen_tss.go index 05e6d7f01f..8d677a2680 100644 --- a/cmd/zetaclientd/keygen_tss.go +++ b/cmd/zetaclientd/keygen_tss.go @@ -8,65 +8,36 @@ import ( "fmt" "time" - "github.com/cometbft/cometbft/crypto/secp256k1" "github.com/rs/zerolog" tsscommon "github.com/zeta-chain/go-tss/common" "github.com/zeta-chain/go-tss/keygen" - "github.com/zeta-chain/go-tss/p2p" + "github.com/zeta-chain/go-tss/tss" "golang.org/x/crypto/sha3" "github.com/zeta-chain/zetacore/pkg/chains" observertypes "github.com/zeta-chain/zetacore/x/observer/types" + "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" zctx "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/zetaclient/metrics" mc "github.com/zeta-chain/zetacore/zetaclient/tss" "github.com/zeta-chain/zetacore/zetaclient/zetacore" ) -func GenerateTss( +// GenerateTSS 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( ctx context.Context, logger zerolog.Logger, - client *zetacore.Client, - peers p2p.AddrList, - priKey secp256k1.PrivKey, - ts *metrics.TelemetryServer, - tssHistoricalList []observertypes.TSS, - tssPassword string, - hotkeyPassword string, -) (*mc.TSS, error) { - app, err := zctx.FromContext(ctx) - if err != nil { - return nil, err - } - + zetaCoreClient *zetacore.Client, + keygenTssServer *tss.TssServer) error { keygenLogger := logger.With().Str("module", "keygen").Logger() - - // Bitcoin chain ID is currently used for using the correct signature format - // TODO: remove this once we have a better way to determine the signature format - // https://github.com/zeta-chain/node/issues/1397 - bitcoinChainID := chains.BitcoinRegtest.ChainId - btcChain, _, btcEnabled := app.GetBTCChainAndConfig() - if btcEnabled { - bitcoinChainID = btcChain.ChainId - } - - tss, err := mc.NewTSS( - ctx, - app, - peers, - priKey, - preParams, - client, - tssHistoricalList, - bitcoinChainID, - tssPassword, - hotkeyPassword, - ) + app, err := zctx.FromContext(ctx) if err != nil { - keygenLogger.Error().Err(err).Msg("NewTSS error") - return nil, err + return err } - ts.SetP2PID(tss.Server.GetLocalPeerID()) // If Keygen block is set it will try to generate new TSS at the block // This is a blocking thread and will wait until the ceremony is complete successfully // If the TSS generation is unsuccessful , it will loop indefinitely until a new TSS is generated @@ -81,10 +52,9 @@ func GenerateTss( // Break out of loop only when TSS is generated successfully, either at the keygenBlock or if it has been generated already , Block set as zero in genesis file // This loop will try keygen at the keygen block and then wait for keygen to be successfully reported by all nodes before breaking out of the loop. // If keygen is unsuccessful, it will reset the triedKeygenAtBlock flag and try again at a new keygen block. - keyGen := app.GetKeygen() if keyGen.Status == observertypes.KeygenStatus_KeyGenSuccess { - return tss, nil + return nil } // Arrive at this stage only if keygen is unsuccessfully reported by every node . This will reset the flag and to try again at a new keygen block if keyGen.Status == observertypes.KeygenStatus_KeyGenFailed { @@ -94,7 +64,7 @@ func GenerateTss( // Try generating TSS at keygen block , only when status is pending keygen and generation has not been tried at the block if keyGen.Status == observertypes.KeygenStatus_PendingKeygen { // Return error if RPC is not working - currentBlock, err := client.GetBlockHeight(ctx) + currentBlock, err := zetaCoreClient.GetBlockHeight(ctx) if err != nil { keygenLogger.Error().Err(err).Msg("GetBlockHeight RPC error") continue @@ -115,50 +85,33 @@ func GenerateTss( } // Try keygen only once at a particular block, irrespective of whether it is successful or failure triedKeygenAtBlock = true - err = keygenTss(ctx, keyGen, tss, keygenLogger) + newPubkey, err := keygenTSS(ctx, keyGen, *keygenTssServer, zetaCoreClient, keygenLogger) if err != nil { - keygenLogger.Error().Err(err).Msg("keygenTss error") - tssFailedVoteHash, err := client.PostVoteTSS( - ctx, - "", - keyGen.BlockNumber, - chains.ReceiveStatus_failed, - ) + keygenLogger.Error().Err(err).Msg("keygenTSS error") + tssFailedVoteHash, err := zetaCoreClient.PostVoteTSS(ctx, + "", keyGen.BlockNumber, chains.ReceiveStatus_failed) if err != nil { keygenLogger.Error().Err(err).Msg("Failed to broadcast Failed TSS Vote to zetacore") - return nil, err + return err } keygenLogger.Info().Msgf("TSS Failed Vote: %s", tssFailedVoteHash) continue } - - newTss := mc.TSS{ - Server: tss.Server, - Keys: tss.Keys, - CurrentPubkey: tss.CurrentPubkey, - Signers: tss.Signers, - ZetacoreClient: nil, - } - - // If TSS is successful , broadcast the vote to zetacore and set Pubkey - tssSuccessVoteHash, err := client.PostVoteTSS( - ctx, - newTss.CurrentPubkey, + // If TSS is successful , broadcast the vote to zetacore and also set the Pubkey + tssSuccessVoteHash, err := zetaCoreClient.PostVoteTSS(ctx, + newPubkey, keyGen.BlockNumber, chains.ReceiveStatus_success, ) if err != nil { keygenLogger.Error().Err(err).Msg("TSS successful but unable to broadcast vote to zeta-core") - return nil, err + return err } keygenLogger.Info().Msgf("TSS successful Vote: %s", tssSuccessVoteHash) - err = SetTSSPubKey(tss, keygenLogger) - if err != nil { - keygenLogger.Error().Err(err).Msg("SetTSSPubKey error") - } - err = TestTSS(&newTss, keygenLogger) + + err = TestTSS(newPubkey, *keygenTssServer, keygenLogger) if err != nil { - keygenLogger.Error().Err(err).Msgf("TestTSS error: %s", newTss.CurrentPubkey) + keygenLogger.Error().Err(err).Msgf("TestTSS error: %s", newPubkey) } continue } @@ -166,31 +119,40 @@ func GenerateTss( keygenLogger.Debug(). Msgf("Waiting for TSS to be generated or Current Keygen to be be finalized. Keygen Block : %d ", keyGen.BlockNumber) } - return nil, errors.New("unexpected state for TSS generation") + return errors.New("unexpected state for TSS generation") } -func keygenTss(ctx context.Context, keyGen observertypes.Keygen, tss *mc.TSS, keygenLogger zerolog.Logger) error { +// keygenTSS generates a new TSS using the keygen request and the TSS server. +// If the keygen is successful, the function returns the new TSS pubkey. +// If the keygen is unsuccessful, the function posts blame and returns an error. +func keygenTSS( + ctx context.Context, + keyGen observertypes.Keygen, + tssServer tss.TssServer, + zetacoreClient interfaces.ZetacoreClient, + keygenLogger zerolog.Logger, +) (string, error) { keygenLogger.Info().Msgf("Keygen at blocknum %d , TSS signers %s ", keyGen.BlockNumber, keyGen.GranteePubkeys) var req keygen.Request req = keygen.NewRequest(keyGen.GranteePubkeys, keyGen.BlockNumber, "0.14.0") - res, err := tss.Server.Keygen(req) + res, err := tssServer.Keygen(req) if res.Status != tsscommon.Success || res.PubKey == "" { keygenLogger.Error().Msgf("keygen fail: reason %s blame nodes %s", res.Blame.FailReason, res.Blame.BlameNodes) // Need to broadcast keygen blame result here digest, err := digestReq(req) if err != nil { - return err + return "", err } index := fmt.Sprintf("keygen-%s-%d", digest, keyGen.BlockNumber) - zetaHash, err := tss.ZetacoreClient.PostVoteBlameData( + zetaHash, err := zetacoreClient.PostVoteBlameData( ctx, &res.Blame, - tss.ZetacoreClient.Chain().ChainId, + zetacoreClient.Chain().ChainId, index, ) if err != nil { keygenLogger.Error().Err(err).Msg("error sending blame data to core") - return err + return "", err } // Increment Blame counter @@ -199,35 +161,23 @@ func keygenTss(ctx context.Context, keyGen observertypes.Keygen, tss *mc.TSS, ke } keygenLogger.Info().Msgf("keygen posted blame data tx hash: %s", zetaHash) - return fmt.Errorf("keygen fail: reason %s blame nodes %s", res.Blame.FailReason, res.Blame.BlameNodes) + return "", fmt.Errorf("keygen fail: reason %s blame nodes %s", res.Blame.FailReason, res.Blame.BlameNodes) } if err != nil { keygenLogger.Error().Msgf("keygen fail: reason %s ", err.Error()) - return err + return "", err } - // Keeping this line here for now, but this is redundant as CurrentPubkey is updated from zeta-core - tss.CurrentPubkey = res.PubKey - tss.Signers = keyGen.GranteePubkeys - - // Keygen succeed! Report TSS address - keygenLogger.Debug().Msgf("Keygen success! keygen response: %v", res) - return nil + // Keygen succeed + keygenLogger.Info().Msgf("Keygen success! keygen response: %v", res) + return res.PubKey, nil } -func SetTSSPubKey(tss *mc.TSS, logger zerolog.Logger) error { - err := tss.InsertPubKey(tss.CurrentPubkey) - if err != nil { - logger.Error().Msgf("SetPubKey fail") - return err - } - logger.Info().Msgf("TSS address in hex: %s", tss.EVMAddress().Hex()) - return nil -} -func TestTSS(tss *mc.TSS, logger zerolog.Logger) error { +// TestTSS tests the TSS keygen by signing a sample message with the TSS key. +func TestTSS(pubkey string, tssServer tss.TssServer, logger zerolog.Logger) error { keygenLogger := logger.With().Str("module", "test-keygen").Logger() keygenLogger.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(tss.CurrentPubkey, tss.Server) + err := mc.TestKeysign(pubkey, tssServer) if err != nil { return err } diff --git a/cmd/zetaclientd/start.go b/cmd/zetaclientd/start.go index 4dfbeadf3e..fef035aee5 100644 --- a/cmd/zetaclientd/start.go +++ b/cmd/zetaclientd/start.go @@ -23,6 +23,7 @@ import ( "github.com/zeta-chain/go-tss/p2p" "github.com/zeta-chain/zetacore/pkg/authz" + "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/constant" observerTypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/chains/base" @@ -30,6 +31,7 @@ import ( zctx "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/zetaclient/metrics" "github.com/zeta-chain/zetacore/zetaclient/orchestrator" + mc "github.com/zeta-chain/zetacore/zetaclient/tss" ) type Multiaddr = core.Multiaddr @@ -200,22 +202,40 @@ func start(_ *cobra.Command, _ []string) error { } telemetryServer.SetIPAddress(cfg.PublicIP) - tss, err := GenerateTss( + // Create TSS server + server, err := mc.SetupTSSServer(peers, priKey, preParams, appContext.Config(), tssKeyPass, true) + if err != nil { + return fmt.Errorf("SetupTSSServer error: %w", err) + } + // Set P2P ID for telemetry + telemetryServer.SetP2PID(server.GetLocalPeerID()) + + // 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) + if err != nil { + return err + } + + bitcoinChainID := chains.BitcoinRegtest.ChainId + btcChain, _, btcEnabled := appContext.GetBTCChainAndConfig() + if btcEnabled { + bitcoinChainID = btcChain.ChainId + } + tss, err := mc.NewTSS( ctx, - masterLogger, zetacoreClient, - peers, - priKey, - telemetryServer, tssHistoricalList, - tssKeyPass, + bitcoinChainID, hotkeyPass, + server, ) if err != nil { + startLogger.Error().Err(err).Msg("NewTSS error") return err } if cfg.TestTssKeysign { - err = TestTSS(tss, masterLogger) + err = TestTSS(tss.CurrentPubkey, *tss.Server, masterLogger) if err != nil { startLogger.Error().Err(err).Msgf("TestTSS error : %s", tss.CurrentPubkey) } diff --git a/cmd/zetae2e/config/localnet.yml b/cmd/zetae2e/config/localnet.yml index ed3338a540..eb6ba8fbb9 100644 --- a/cmd/zetae2e/config/localnet.yml +++ b/cmd/zetae2e/config/localnet.yml @@ -37,6 +37,10 @@ additional_accounts: bech32_address: "zeta17w0adeg64ky0daxwd2ugyuneellmjgnx4e483s" evm_address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" private_key: "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" + user_migration: + bech32_address: "zeta1pvtxa708yvdmszn687nne6nl8qn704daf420xz" + evm_address: "0x0B166ef9e7231Bb80A7A3FA73CEA7F3827E7D5BD" + private_key: "0bcc2fa28b526f90e1d54648d612db901e860bf68248555593f91ea801c6b482" policy_accounts: emergency_policy_account: bech32_address: "zeta16m2cnrdwtgweq4njc6t470vl325gw4kp6s7tap" diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index 4007e4fc12..34f3c69540 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -8,6 +8,7 @@ import ( "github.com/fatih/color" "github.com/spf13/cobra" + "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" zetae2econfig "github.com/zeta-chain/zetacore/cmd/zetae2e/config" @@ -19,6 +20,7 @@ import ( "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/testutil" crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) const ( @@ -34,6 +36,7 @@ const ( flagLight = "light" flagSetupOnly = "setup-only" flagSkipSetup = "skip-setup" + flagTestTSSMigration = "test-tss-migration" flagSkipBitcoinSetup = "skip-bitcoin-setup" flagSkipHeaderProof = "skip-header-proof" ) @@ -66,6 +69,7 @@ func NewLocalCmd() *cobra.Command { cmd.Flags().Bool(flagSkipSetup, false, "set to true to skip setup") cmd.Flags().Bool(flagSkipBitcoinSetup, false, "set to true to skip bitcoin wallet setup") cmd.Flags().Bool(flagSkipHeaderProof, false, "set to true to skip header proof tests") + cmd.Flags().Bool(flagTestTSSMigration, false, "set to true to include a migration test at the end") return cmd } @@ -86,6 +90,7 @@ func localE2ETest(cmd *cobra.Command, _ []string) { skipSetup = must(cmd.Flags().GetBool(flagSkipSetup)) skipBitcoinSetup = must(cmd.Flags().GetBool(flagSkipBitcoinSetup)) skipHeaderProof = must(cmd.Flags().GetBool(flagSkipHeaderProof)) + testTSSMigration = must(cmd.Flags().GetBool(flagTestTSSMigration)) ) logger := runner.NewLogger(verbose, color.FgWhite, "setup") @@ -151,7 +156,7 @@ func localE2ETest(cmd *cobra.Command, _ []string) { // wait for keygen to be completed // if setup is skipped, we assume that the keygen is already completed if !skipSetup { - waitKeygenHeight(ctx, deployerRunner.CctxClient, logger) + waitKeygenHeight(ctx, deployerRunner.CctxClient, deployerRunner.ObserverClient, logger, 10) } // query and set the TSS @@ -202,6 +207,7 @@ func localE2ETest(cmd *cobra.Command, _ []string) { // run tests var eg errgroup.Group + if !skipRegular { // defines all tests, if light is enabled, only the most basic tests are run and advanced are skipped erc20Tests := []string{ @@ -281,6 +287,7 @@ func localE2ETest(cmd *cobra.Command, _ []string) { eg.Go(solanaTestRoutine(conf, deployerRunner, verbose, solanaTests...)) eg.Go(ethereumTestRoutine(conf, deployerRunner, verbose, testHeader, ethereumTests...)) } + if testAdmin { eg.Go(adminTestRoutine(conf, deployerRunner, verbose, e2etests.TestRateLimiterName, @@ -318,7 +325,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 { logger.Print("❌ %v", err) @@ -328,6 +335,10 @@ func localE2ETest(cmd *cobra.Command, _ []string) { logger.Print("✅ e2e tests completed in %s", time.Since(testStartTime).String()) + if testTSSMigration { + runTSSMigrationTest(deployerRunner, logger, verbose, conf) + } + // print and validate report networkReport, err := deployerRunner.GenerateNetworkReport() if err != nil { @@ -346,10 +357,24 @@ func localE2ETest(cmd *cobra.Command, _ []string) { func waitKeygenHeight( ctx context.Context, cctxClient crosschaintypes.QueryClient, + observerClient observertypes.QueryClient, logger *runner.Logger, + bufferBlocks int64, ) { // wait for keygen to be completed - keygenHeight := int64(35) + resp, err := observerClient.Keygen(ctx, &observertypes.QueryGetKeygenRequest{}) + if err != nil { + logger.Error("observerClient.Keygen error: %s", err) + return + } + if resp.Keygen == nil { + logger.Error("observerClient.Keygen keygen is nil") + return + } + if resp.Keygen.Status != observertypes.KeygenStatus_PendingKeygen { + return + } + keygenHeight := resp.Keygen.BlockNumber logger.Print("⏳ wait height %v for keygen to be completed", keygenHeight) for { time.Sleep(2 * time.Second) @@ -358,13 +383,55 @@ func waitKeygenHeight( logger.Error("cctxClient.LastZetaHeight error: %s", err) continue } - if response.Height >= keygenHeight { + if response.Height >= keygenHeight+bufferBlocks { break } logger.Info("Last ZetaHeight: %d", response.Height) } } +func runTSSMigrationTest(deployerRunner *runner.E2ERunner, logger *runner.Logger, verbose bool, conf config.Config) { + migrationStartTime := time.Now() + logger.Print("🏁 starting tss migration") + + response, err := deployerRunner.CctxClient.LastZetaHeight( + deployerRunner.Ctx, + &crosschaintypes.QueryLastZetaHeightRequest{}, + ) + require.NoError(deployerRunner, err) + err = deployerRunner.ZetaTxServer.UpdateKeygen(response.Height) + require.NoError(deployerRunner, err) + + // Generate new TSS + waitKeygenHeight(deployerRunner.Ctx, deployerRunner.CctxClient, deployerRunner.ObserverClient, logger, 0) + + // migration test is a blocking thread, we cannot run other tests in parallel + // The migration test migrates funds to a new TSS and then updates the TSS address on zetacore. + // The necessary restarts are done by the zetaclient supervisor + fn := migrationTestRoutine(conf, deployerRunner, verbose, e2etests.TestMigrateTSSName) + + if err := fn(); err != nil { + logger.Print("❌ %v", err) + logger.Print("❌ tss migration failed") + os.Exit(1) + } + + logger.Print("✅ migration completed in %s ", time.Since(migrationStartTime).String()) + logger.Print("🏁 starting post migration tests") + + tests := []string{ + e2etests.TestBitcoinWithdrawSegWitName, + e2etests.TestEtherWithdrawName, + } + fn = postMigrationTestRoutine(conf, deployerRunner, verbose, tests...) + + if err := fn(); err != nil { + logger.Print("❌ %v", err) + logger.Print("❌ post migration tests failed") + os.Exit(1) + } +} + func must[T any](v T, err error) T { return testutil.Must(v, err) } diff --git a/cmd/zetae2e/local/migration.go b/cmd/zetae2e/local/migration.go new file mode 100644 index 0000000000..d6fab1b709 --- /dev/null +++ b/cmd/zetae2e/local/migration.go @@ -0,0 +1,63 @@ +package local + +import ( + "fmt" + "time" + + "github.com/fatih/color" + + "github.com/zeta-chain/zetacore/e2e/config" + "github.com/zeta-chain/zetacore/e2e/e2etests" + "github.com/zeta-chain/zetacore/e2e/runner" +) + +// migrationTestRoutine runs migration related e2e tests +func migrationTestRoutine( + conf config.Config, + deployerRunner *runner.E2ERunner, + verbose bool, + testNames ...string, +) func() error { + return func() (err error) { + account := conf.AdditionalAccounts.UserMigration + // initialize runner for migration test + migrationTestRunner, err := initTestRunner( + "migration", + conf, + deployerRunner, + account, + runner.NewLogger(verbose, color.FgHiGreen, "migration"), + runner.WithZetaTxServer(deployerRunner.ZetaTxServer), + ) + if err != nil { + return err + } + + migrationTestRunner.Logger.Print("🏃 starting migration tests") + startTime := time.Now() + + if len(testNames) == 0 { + migrationTestRunner.Logger.Print("🍾 migration tests completed in %s", time.Since(startTime).String()) + return nil + } + // run migration test + testsToRun, err := migrationTestRunner.GetE2ETestsToRunByName( + e2etests.AllE2ETests, + testNames..., + ) + if err != nil { + return fmt.Errorf("migration tests failed: %v", err) + } + + if err := migrationTestRunner.RunE2ETests(testsToRun); err != nil { + return fmt.Errorf("migration tests failed: %v", err) + } + if err := migrationTestRunner.CheckBtcTSSBalance(); err != nil { + return err + } + + migrationTestRunner.Logger.Print("🍾 migration tests completed in %s", time.Since(startTime).String()) + + return err + } +} diff --git a/cmd/zetae2e/local/post_migration.go b/cmd/zetae2e/local/post_migration.go new file mode 100644 index 0000000000..f339ce0bc1 --- /dev/null +++ b/cmd/zetae2e/local/post_migration.go @@ -0,0 +1,58 @@ +package local + +import ( + "time" + + "github.com/fatih/color" + "github.com/pkg/errors" + + "github.com/zeta-chain/zetacore/e2e/config" + "github.com/zeta-chain/zetacore/e2e/e2etests" + "github.com/zeta-chain/zetacore/e2e/runner" +) + +// postMigrationTestRoutine runs post migration tests +func postMigrationTestRoutine( + conf config.Config, + deployerRunner *runner.E2ERunner, + verbose bool, + testNames ...string, +) func() error { + return func() (err error) { + account := conf.AdditionalAccounts.UserBitcoin + // initialize runner for post migration test + postMigrationRunner, err := initTestRunner( + "postMigration", + conf, + deployerRunner, + account, + runner.NewLogger(verbose, color.FgMagenta, "postMigrationRunner"), + ) + if err != nil { + return err + } + + postMigrationRunner.Logger.Print("🏃 starting postMigration tests") + startTime := time.Now() + + testsToRun, err := postMigrationRunner.GetE2ETestsToRunByName( + e2etests.AllE2ETests, + testNames..., + ) + if err != nil { + return errors.Wrap(err, "postMigrationRunner tests failed") + } + + if err := postMigrationRunner.RunE2ETests(testsToRun); err != nil { + return errors.Wrap(err, "postMigrationRunner tests failed") + } + + if err := postMigrationRunner.CheckBtcTSSBalance(); err != nil { + return err + } + + postMigrationRunner.Logger.Print("🍾 PostMigration tests completed in %s", time.Since(startTime).String()) + + return err + } +} diff --git a/contrib/localnet/orchestrator/start-zetae2e.sh b/contrib/localnet/orchestrator/start-zetae2e.sh index 676d55e26e..b51c1e6344 100644 --- a/contrib/localnet/orchestrator/start-zetae2e.sh +++ b/contrib/localnet/orchestrator/start-zetae2e.sh @@ -77,6 +77,11 @@ address=$(yq -r '.additional_accounts.user_admin.evm_address' config.yml) echo "funding admin tester address ${address} with 10000 Ether" geth --exec "eth.sendTransaction({from: eth.coinbase, to: '${address}', value: web3.toWei(10000,'ether')})" attach http://eth:8545 +# unlock migration tests accounts +address=$(yq -r '.additional_accounts.user_migration.evm_address' config.yml) +echo "funding migration tester address ${address} with 10000 Ether" +geth --exec "eth.sendTransaction({from: eth.coinbase, to: '${address}', value: web3.toWei(10000,'ether')})" attach http://eth:8545 + ### Run zetae2e command depending on the option passed if [ "$LOCALNET_MODE" == "upgrade" ]; then @@ -176,7 +181,7 @@ else exit 0 fi - echo "running e2e tests..." + echo "running e2e tests with arguments: $E2E_ARGS" zetae2e local $E2E_ARGS --skip-setup --config deployed.yml ZETAE2E_EXIT_CODE=$? diff --git a/contrib/localnet/scripts/start-zetacored.sh b/contrib/localnet/scripts/start-zetacored.sh index c3e53c6c93..fca8e25152 100755 --- a/contrib/localnet/scripts/start-zetacored.sh +++ b/contrib/localnet/scripts/start-zetacored.sh @@ -259,6 +259,9 @@ then # operational policy account address=$(yq -r '.policy_accounts.operational_policy_account.bech32_address' /root/config.yml) zetacored add-genesis-account "$address" 100000000000000000000000000azeta +# migration tester + address=$(yq -r '.additional_accounts.user_migration.bech32_address' /root/config.yml) + zetacored add-genesis-account "$address" 100000000000000000000000000azeta # 3. Copy the genesis.json to all the nodes .And use it to create a gentx for every node zetacored gentx operator 1000000000000000000000azeta --chain-id=$CHAINID --keyring-backend=$KEYRING --gas-prices 20000000000azeta diff --git a/docs/spec/fungible/messages.md b/docs/spec/fungible/messages.md index 93c7864aef..ce845608e6 100644 --- a/docs/spec/fungible/messages.md +++ b/docs/spec/fungible/messages.md @@ -56,7 +56,7 @@ Authorized: admin policy group 2. ```proto message MsgRemoveForeignCoin { string creator = 1; - string name = 2; + string zrc20_address = 2; } ``` diff --git a/e2e/config/config.go b/e2e/config/config.go index 8dd370730f..bd1478a581 100644 --- a/e2e/config/config.go +++ b/e2e/config/config.go @@ -67,6 +67,7 @@ type AdditionalAccounts struct { UserEther Account `yaml:"user_ether"` UserMisc Account `yaml:"user_misc"` UserAdmin Account `yaml:"user_admin"` + UserMigration Account `yaml:"user_migration"` } type PolicyAccounts struct { @@ -209,6 +210,7 @@ func (a AdditionalAccounts) AsSlice() []Account { a.UserEther, a.UserMisc, a.UserAdmin, + a.UserMigration, } } @@ -297,6 +299,11 @@ func (c *Config) GenerateKeys() error { if err != nil { return err } + c.AdditionalAccounts.UserMigration, err = generateAccount() + if err != nil { + return err + } + c.PolicyAccounts.EmergencyPolicyAccount, err = generateAccount() if err != nil { return err diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index d99f42c42a..6502afd564 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -106,6 +106,8 @@ const ( TestUpdateBytecodeConnectorName = "update_bytecode_connector" TestRateLimiterName = "rate_limiter" + TestMigrateTSSName = "migrate_TSS" + /* Special tests Not used to test functionalities but do various interactions with the netwoks @@ -558,4 +560,10 @@ var AllE2ETests = []runner.E2ETest{ }, TestDeployContract, ), + runner.NewE2ETest( + TestMigrateTSSName, + "migrate TSS funds", + []runner.ArgDefinition{}, + TestMigrateTSS, + ), } diff --git a/e2e/e2etests/test_migrate_tss.go b/e2e/e2etests/test_migrate_tss.go new file mode 100644 index 0000000000..c72b876f0f --- /dev/null +++ b/e2e/e2etests/test_migrate_tss.go @@ -0,0 +1,182 @@ +package e2etests + +import ( + "context" + "fmt" + "sort" + "strconv" + "time" + + sdkmath "cosmossdk.io/math" + "github.com/btcsuite/btcutil" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/utils" + "github.com/zeta-chain/zetacore/pkg/chains" + zetacrypto "github.com/zeta-chain/zetacore/pkg/crypto" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" +) + +func TestMigrateTSS(r *runner.E2ERunner, _ []string) { + r.SetBtcAddress(r.Name, false) + stop := r.MineBlocksIfLocalBitcoin() + defer stop() + + // Pause inbound procoessing for tss migration + r.Logger.Info("Pause inbound processing") + msg := observertypes.NewMsgDisableCCTX( + r.ZetaTxServer.MustGetAccountAddressFromName(utils.EmergencyPolicyName), + false, + true) + _, err := r.ZetaTxServer.BroadcastTx(utils.EmergencyPolicyName, msg) + require.NoError(r, err) + + // Migrate btc + // Fetch balance of BTC TSS address + utxos, err := r.GetTop20UTXOsForTssAddress() + require.NoError(r, err) + + var btcBalance float64 + for _, utxo := range utxos { + btcBalance += utxo.Amount + } + + btcTSSBalanceOld := btcBalance + // Use fixed fee of 0.01 for migration + btcBalance = btcBalance - 0.01 + btcChain := chains.BitcoinRegtest.ChainId + + //migrate btc funds + // #nosec G701 e2eTest - always in range + migrationAmountBTC := sdkmath.NewUint(uint64(btcBalance * 1e8)) + msgMigrateFunds := crosschaintypes.NewMsgMigrateTssFunds( + r.ZetaTxServer.MustGetAccountAddressFromName(utils.AdminPolicyName), + btcChain, + migrationAmountBTC, + ) + _, err = r.ZetaTxServer.BroadcastTx(utils.AdminPolicyName, msgMigrateFunds) + require.NoError(r, err) + + // Fetch migrator cctx for btc migration + migrator, err := r.ObserverClient.TssFundsMigratorInfo(r.Ctx, &observertypes.QueryTssFundsMigratorInfoRequest{ + ChainId: btcChain}) + require.NoError(r, err) + cctxBTCMigration := migrator.TssFundsMigrator.MigrationCctxIndex + + // ETH migration + // Fetch balance of ETH TSS address + tssBalance, err := r.EVMClient.BalanceAt(context.Background(), r.TSSAddress, nil) + require.NoError(r, err) + ethTSSBalanceOld := tssBalance + + tssBalanceUint := sdkmath.NewUintFromString(tssBalance.String()) + evmChainID, err := r.EVMClient.ChainID(r.Ctx) + require.NoError(r, err) + + // Migrate TSS funds for the eth chain + msgMigrateFunds = crosschaintypes.NewMsgMigrateTssFunds( + r.ZetaTxServer.MustGetAccountAddressFromName(utils.AdminPolicyName), + evmChainID.Int64(), + tssBalanceUint, + ) + _, err = r.ZetaTxServer.BroadcastTx(utils.AdminPolicyName, msgMigrateFunds) + require.NoError(r, err) + + // Fetch migrator cctx for eth migration + migrator, err = r.ObserverClient.TssFundsMigratorInfo( + r.Ctx, + &observertypes.QueryTssFundsMigratorInfoRequest{ChainId: evmChainID.Int64()}, + ) + require.NoError(r, err) + cctxETHMigration := migrator.TssFundsMigrator.MigrationCctxIndex + + cctxBTC := utils.WaitCCTXMinedByIndex(r.Ctx, cctxBTCMigration, r.CctxClient, r.Logger, r.CctxTimeout) + require.Equal(r, crosschaintypes.CctxStatus_OutboundMined, cctxBTC.CctxStatus.Status) + + cctxETH := utils.WaitCCTXMinedByIndex(r.Ctx, cctxETHMigration, r.CctxClient, r.Logger, r.CctxTimeout) + require.Equal(r, crosschaintypes.CctxStatus_OutboundMined, cctxETH.CctxStatus.Status) + + // Check if new TSS is added to list + allTss, err := r.ObserverClient.TssHistory(r.Ctx, &observertypes.QueryTssHistoryRequest{}) + require.NoError(r, err) + + require.Len(r, allTss.TssList, 2) + + // Update TSS to new address + sort.Slice(allTss.TssList, func(i, j int) bool { + return allTss.TssList[i].FinalizedZetaHeight < allTss.TssList[j].FinalizedZetaHeight + }) + msgUpdateTss := crosschaintypes.NewMsgUpdateTssAddress( + r.ZetaTxServer.MustGetAccountAddressFromName(utils.AdminPolicyName), + allTss.TssList[1].TssPubkey, + ) + _, err = r.ZetaTxServer.BroadcastTx(utils.AdminPolicyName, msgUpdateTss) + require.NoError(r, err) + + // Wait for atleast one block for the TSS to be updated + time.Sleep(8 * time.Second) + + currentTss, err := r.ObserverClient.TSS(r.Ctx, &observertypes.QueryGetTSSRequest{}) + require.NoError(r, err) + require.Equal(r, allTss.TssList[1].TssPubkey, currentTss.TSS.TssPubkey) + + newTss, err := r.ObserverClient.GetTssAddress(r.Ctx, &observertypes.QueryGetTssAddressRequest{}) + require.NoError(r, err) + + // Check balance of new TSS address to make sure all funds have been transferred + // BTC + btcTssAddress, err := zetacrypto.GetTssAddrBTC(currentTss.TSS.TssPubkey, r.BitcoinParams) + require.NoError(r, err) + + btcTssAddressNew, err := btcutil.DecodeAddress(btcTssAddress, r.BitcoinParams) + require.NoError(r, err) + + r.BTCTSSAddress = btcTssAddressNew + r.AddTSSToNode() + + utxos, err = r.GetTop20UTXOsForTssAddress() + require.NoError(r, err) + + var btcTSSBalanceNew float64 + // #nosec G701 e2eTest - always in range + for _, utxo := range utxos { + btcTSSBalanceNew += utxo.Amount + } + + r.Logger.Info(fmt.Sprintf("BTC Balance Old: %f", btcTSSBalanceOld*1e8)) + r.Logger.Info(fmt.Sprintf("BTC Balance New: %f", btcTSSBalanceNew*1e8)) + r.Logger.Info(fmt.Sprintf("Migrator amount : %s", cctxBTC.GetCurrentOutboundParam().Amount)) + + // btcTSSBalanceNew should be less than btcTSSBalanceOld as there is some loss of funds during migration + // #nosec G701 e2eTest - always in range + require.Equal( + r, + strconv.FormatInt(int64(btcTSSBalanceNew*1e8), 10), + cctxBTC.GetCurrentOutboundParam().Amount.String(), + ) + require.LessOrEqual(r, btcTSSBalanceNew*1e8, btcTSSBalanceOld*1e8) + + // ETH + + r.TSSAddress = common.HexToAddress(newTss.Eth) + ethTSSBalanceNew, err := r.EVMClient.BalanceAt(context.Background(), r.TSSAddress, nil) + require.NoError(r, err) + + r.Logger.Info(fmt.Sprintf("TSS Balance Old: %s", ethTSSBalanceOld.String())) + r.Logger.Info(fmt.Sprintf("TSS Balance New: %s", ethTSSBalanceNew.String())) + r.Logger.Info(fmt.Sprintf("Migrator amount : %s", cctxETH.GetCurrentOutboundParam().Amount.String())) + + // ethTSSBalanceNew should be less than ethTSSBalanceOld as there is some loss of funds during migration + require.Equal(r, ethTSSBalanceNew.String(), cctxETH.GetCurrentOutboundParam().Amount.String()) + require.True(r, ethTSSBalanceNew.Cmp(ethTSSBalanceOld) < 0) + + msgEnable := observertypes.NewMsgEnableCCTX( + r.ZetaTxServer.MustGetAccountAddressFromName(utils.OperationalPolicyName), + true, + true) + _, err = r.ZetaTxServer.BroadcastTx(utils.OperationalPolicyName, msgEnable) + require.NoError(r, err) +} diff --git a/e2e/runner/accounting.go b/e2e/runner/accounting.go index 4da570c3a2..6b70e5f19c 100644 --- a/e2e/runner/accounting.go +++ b/e2e/runner/accounting.go @@ -8,8 +8,12 @@ import ( "net/http" "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" "github.com/gagliardetto/solana-go/rpc" "github.com/stretchr/testify/require" + + zetacrypto "github.com/zeta-chain/zetacore/pkg/crypto" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) const ( @@ -41,30 +45,63 @@ func (r *E2ERunner) CheckZRC20ReserveAndSupply() error { } func (r *E2ERunner) checkEthTSSBalance() error { - tssBal, err := r.EVMClient.BalanceAt(r.Ctx, r.TSSAddress, nil) + allTssAddress, err := r.ObserverClient.TssHistory(r.Ctx, &observertypes.QueryTssHistoryRequest{}) if err != nil { return err } + + tssTotalBalance := big.NewInt(0) + + for _, tssAddress := range allTssAddress.TssList { + evmAddress, err := r.ObserverClient.GetTssAddressByFinalizedHeight( + r.Ctx, + &observertypes.QueryGetTssAddressByFinalizedHeightRequest{ + FinalizedZetaHeight: tssAddress.FinalizedZetaHeight, + }, + ) + if err != nil { + continue + } + + tssBal, err := r.EVMClient.BalanceAt(r.Ctx, common.HexToAddress(evmAddress.Eth), nil) + if err != nil { + continue + } + tssTotalBalance.Add(tssTotalBalance, tssBal) + } + zrc20Supply, err := r.ETHZRC20.TotalSupply(&bind.CallOpts{}) if err != nil { return err } - if tssBal.Cmp(zrc20Supply) < 0 { - return fmt.Errorf("ETH: TSS balance (%d) < ZRC20 TotalSupply (%d) ", tssBal, zrc20Supply) + if tssTotalBalance.Cmp(zrc20Supply) < 0 { + return fmt.Errorf("ETH: TSS balance (%d) < ZRC20 TotalSupply (%d) ", tssTotalBalance, zrc20Supply) } - r.Logger.Info("ETH: TSS balance (%d) >= ZRC20 TotalSupply (%d)", tssBal, zrc20Supply) + r.Logger.Info("ETH: TSS balance (%d) >= ZRC20 TotalSupply (%d)", tssTotalBalance, zrc20Supply) return nil } func (r *E2ERunner) CheckBtcTSSBalance() error { - utxos, err := r.BtcRPCClient.ListUnspent() + allTssAddress, err := r.ObserverClient.TssHistory(r.Ctx, &observertypes.QueryTssHistoryRequest{}) if err != nil { return err } - var btcBalance float64 - for _, utxo := range utxos { - if utxo.Address == r.BTCTSSAddress.EncodeAddress() { - btcBalance += utxo.Amount + + tssTotalBalance := float64(0) + + for _, tssAddress := range allTssAddress.TssList { + btcTssAddress, err := zetacrypto.GetTssAddrBTC(tssAddress.TssPubkey, r.BitcoinParams) + if err != nil { + continue + } + utxos, err := r.BtcRPCClient.ListUnspent() + if err != nil { + continue + } + for _, utxo := range utxos { + if utxo.Address == btcTssAddress { + tssTotalBalance += utxo.Amount + } } } @@ -75,19 +112,19 @@ func (r *E2ERunner) CheckBtcTSSBalance() error { // check the balance in TSS is greater than the total supply on ZetaChain // the amount minted to initialize the pool is subtracted from the total supply - // #nosec G115 test - always in range - if int64(btcBalance*1e8) < (zrc20Supply.Int64() - 10000000) { - // #nosec G115 test - always in range + // #nosec G701 test - always in range + if int64(tssTotalBalance*1e8) < (zrc20Supply.Int64() - 10000000) { + // #nosec G701 test - always in range return fmt.Errorf( "BTC: TSS Balance (%d) < ZRC20 TotalSupply (%d)", - int64(btcBalance*1e8), + int64(tssTotalBalance*1e8), zrc20Supply.Int64()-10000000, ) } // #nosec G115 test - always in range r.Logger.Print( "BTC: Balance (%d) >= ZRC20 TotalSupply (%d)", - int64(btcBalance*1e8), + int64(tssTotalBalance*1e8), zrc20Supply.Int64()-10000000, ) diff --git a/e2e/runner/bitcoin.go b/e2e/runner/bitcoin.go index 3165d745ca..868c344766 100644 --- a/e2e/runner/bitcoin.go +++ b/e2e/runner/bitcoin.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/hex" "fmt" + "sort" "time" "github.com/btcsuite/btcd/btcjson" @@ -24,6 +25,7 @@ import ( lightclienttypes "github.com/zeta-chain/zetacore/x/lightclient/types" zetabitcoin "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin" btcobserver "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/observer" + "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/signer" ) var blockHeaderBTCTimeout = 5 * time.Minute @@ -54,6 +56,29 @@ func (r *E2ERunner) ListDeployerUTXOs() ([]btcjson.ListUnspentResult, error) { return utxos, nil } +// GetTop20UTXOsForTssAddress returns the top 20 UTXOs for the TSS address. +// Top 20 utxos should be used for TSS migration, as we can only migrate at max 20 utxos at a time. +func (r *E2ERunner) GetTop20UTXOsForTssAddress() ([]btcjson.ListUnspentResult, error) { + // query UTXOs from node + utxos, err := r.BtcRPCClient.ListUnspentMinMaxAddresses( + 0, + 9999999, + []btcutil.Address{r.BTCTSSAddress}, + ) + if err != nil { + return nil, err + } + + sort.SliceStable(utxos, func(i, j int) bool { + return utxos[i].Amount < utxos[j].Amount + }) + + if len(utxos) > signer.MaxNoOfInputsPerTx { + utxos = utxos[:signer.MaxNoOfInputsPerTx] + } + return utxos, nil +} + // DepositBTCWithAmount deposits BTC on ZetaChain with a specific amount func (r *E2ERunner) DepositBTCWithAmount(amount float64) *chainhash.Hash { r.Logger.Print("⏳ depositing BTC into ZEVM") @@ -185,7 +210,7 @@ func (r *E2ERunner) SendToTSSFromDeployerWithMemo( scriptPubkeys[i] = utxo.ScriptPubKey } - feeSats := btcutil.Amount(0.0001 * btcutil.SatoshiPerBitcoin) + feeSats := btcutil.Amount(0.0005 * btcutil.SatoshiPerBitcoin) amountSats := btcutil.Amount(amount * btcutil.SatoshiPerBitcoin) change := inputSats - feeSats - amountSats diff --git a/e2e/runner/setup_bitcoin.go b/e2e/runner/setup_bitcoin.go index d97ed90067..ebca4d95a7 100644 --- a/e2e/runner/setup_bitcoin.go +++ b/e2e/runner/setup_bitcoin.go @@ -10,6 +10,22 @@ import ( "github.com/stretchr/testify/require" ) +func (r *E2ERunner) AddTSSToNode() { + r.Logger.Print("⚙️ add new tss to Bitcoin node") + startTime := time.Now() + defer func() { + r.Logger.Print("✅ Bitcoin account setup in %s\n", time.Since(startTime)) + }() + + // import the TSS address + err := r.BtcRPCClient.ImportAddress(r.BTCTSSAddress.EncodeAddress()) + require.NoError(r, err) + + // mine some blocks to get some BTC into the deployer address + _, err = r.GenerateToAddressIfLocalBitcoin(101, r.BTCDeployerAddress) + require.NoError(r, err) +} + func (r *E2ERunner) SetupBitcoinAccount(initNetwork bool) { r.Logger.Print("⚙️ setting up Bitcoin account") startTime := time.Now() diff --git a/e2e/txserver/zeta_tx_server.go b/e2e/txserver/zeta_tx_server.go index 5e111d8440..23ae24da4f 100644 --- a/e2e/txserver/zeta_tx_server.go +++ b/e2e/txserver/zeta_tx_server.go @@ -453,6 +453,16 @@ func (zts ZetaTxServer) FundEmissionsPool(account string, amount *big.Int) error return err } +// UpdateKeygen sets a new keygen height . The new height is the current height + 30 +func (zts ZetaTxServer) UpdateKeygen(height int64) error { + keygenHeight := height + 30 + _, err := zts.BroadcastTx(zts.GetAccountName(0), observertypes.NewMsgUpdateKeygen( + zts.GetAccountAddress(0), + keygenHeight, + )) + return err +} + // newCodec returns the codec for msg server func newCodec() (*codec.ProtoCodec, codectypes.InterfaceRegistry) { encodingConfig := app.MakeEncodingConfig() diff --git a/e2e/utils/require.go b/e2e/utils/require.go index ffedb62e59..7471bf9b4e 100644 --- a/e2e/utils/require.go +++ b/e2e/utils/require.go @@ -16,7 +16,7 @@ func RequireCCTXStatus( expected crosschaintypes.CctxStatus, msgAndArgs ...any, ) { - msg := fmt.Sprintf("cctx status is not %q", expected.String()) + msg := fmt.Sprintf("cctx status is not %q cctx index %s", expected.String(), cctx.Index) require.NotNil(t, cctx.CctxStatus) require.Equal(t, expected, cctx.CctxStatus.Status, msg+errSuffix(msgAndArgs...)) diff --git a/pkg/gas/gas_limits.go b/pkg/gas/gas_limits.go index d4a3938adb..b585918b10 100644 --- a/pkg/gas/gas_limits.go +++ b/pkg/gas/gas_limits.go @@ -7,7 +7,8 @@ import ( const ( // EVMSend is the gas limit required to transfer tokens on an EVM based chain - EVMSend = 21000 + EVMSend = 21_000 + // TODO: Move gas limits from zeta-client to this file // https://github.com/zeta-chain/node/issues/1606 ) diff --git a/proto/zetachain/zetacore/fungible/tx.proto b/proto/zetachain/zetacore/fungible/tx.proto index bdb9ee8939..a73dcc2350 100644 --- a/proto/zetachain/zetacore/fungible/tx.proto +++ b/proto/zetachain/zetacore/fungible/tx.proto @@ -73,7 +73,7 @@ message MsgDeployFungibleCoinZRC20Response { string address = 1; } message MsgRemoveForeignCoin { string creator = 1; - string name = 2; + string zrc20_address = 2; } message MsgRemoveForeignCoinResponse {} diff --git a/typescript/zetachain/zetacore/fungible/tx_pb.d.ts b/typescript/zetachain/zetacore/fungible/tx_pb.d.ts index a1038d4bfd..131f438020 100644 --- a/typescript/zetachain/zetacore/fungible/tx_pb.d.ts +++ b/typescript/zetachain/zetacore/fungible/tx_pb.d.ts @@ -276,9 +276,9 @@ export declare class MsgRemoveForeignCoin extends Message creator: string; /** - * @generated from field: string name = 2; + * @generated from field: string zrc20_address = 2; */ - name: string; + zrc20Address: string; constructor(data?: PartialMessage); diff --git a/x/crosschain/keeper/cctx_orchestrator_validate_inbound.go b/x/crosschain/keeper/cctx_orchestrator_validate_inbound.go index 4bb90ec915..4cda69a5c3 100644 --- a/x/crosschain/keeper/cctx_orchestrator_validate_inbound.go +++ b/x/crosschain/keeper/cctx_orchestrator_validate_inbound.go @@ -3,6 +3,8 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/crypto" "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) @@ -19,6 +21,11 @@ func (k Keeper) ValidateInbound( return nil, types.ErrCannotFindTSSKeys } + err := k.CheckIfTSSMigrationTransfer(ctx, msg) + if err != nil { + return nil, err + } + // Do not process if inbound is disabled if !k.zetaObserverKeeper.IsInboundEnabled(ctx) { return nil, observertypes.ErrInboundDisabled @@ -48,3 +55,49 @@ func (k Keeper) ValidateInbound( return &cctx, nil } + +// CheckIfTSSMigrationTransfer checks if the sender is a TSS address and returns an error if it is. +// If the sender is an older TSS address, this means that it is a migration transfer, and we do not need to treat this as a deposit and process the CCTX +func (k Keeper) CheckIfTSSMigrationTransfer(ctx sdk.Context, msg *types.MsgVoteInbound) error { + additionalChains := k.GetAuthorityKeeper().GetAdditionalChainList(ctx) + + historicalTSSList := k.zetaObserverKeeper.GetAllTSS(ctx) + chain, found := k.zetaObserverKeeper.GetSupportedChainFromChainID(ctx, msg.SenderChainId) + if !found { + return observertypes.ErrSupportedChains.Wrapf("chain not found for chainID %d", msg.SenderChainId) + } + + // the check is only necessary if the inbound is validated from observers from a connected chain + if chain.CctxGateway != chains.CCTXGateway_observers { + return nil + } + + switch { + case chains.IsEVMChain(chain.ChainId, additionalChains): + for _, tss := range historicalTSSList { + ethTssAddress, err := crypto.GetTssAddrEVM(tss.TssPubkey) + if err != nil { + continue + } + if ethTssAddress.Hex() == msg.Sender { + return types.ErrMigrationFromOldTss + } + } + case chains.IsBitcoinChain(chain.ChainId, additionalChains): + bitcoinParams, err := chains.BitcoinNetParamsFromChainID(chain.ChainId) + if err != nil { + return err + } + for _, tss := range historicalTSSList { + btcTssAddress, err := crypto.GetTssAddrBTC(tss.TssPubkey, bitcoinParams) + if err != nil { + continue + } + if btcTssAddress == msg.Sender { + return types.ErrMigrationFromOldTss + } + } + } + + return nil +} diff --git a/x/crosschain/keeper/cctx_orchestrator_validate_inbound_test.go b/x/crosschain/keeper/cctx_orchestrator_validate_inbound_test.go new file mode 100644 index 0000000000..0f09c61025 --- /dev/null +++ b/x/crosschain/keeper/cctx_orchestrator_validate_inbound_test.go @@ -0,0 +1,668 @@ +package keeper_test + +import ( + "testing" + + sdkmath "cosmossdk.io/math" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/coin" + "github.com/zeta-chain/zetacore/pkg/crypto" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/crosschain/types" + observerTypes "github.com/zeta-chain/zetacore/x/observer/types" +) + +func TestKeeper_ValidateInbound(t *testing.T) { + t.Run("successfully validate inbound", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, + keepertest.CrosschainMockOptions{ + UseObserverMock: true, + UseFungibleMock: true, + UseAuthorityMock: true, + }) + + // Setup mock data + observerMock := keepertest.GetCrosschainObserverMock(t, k) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + receiver := sample.EthAddress() + creator := sample.AccAddress() + amount := sdkmath.NewUint(42) + message := "test" + inboundBlockHeight := uint64(420) + inboundHash := sample.Hash() + gasLimit := uint64(100) + asset := "test-asset" + eventIndex := uint64(1) + cointType := coin.CoinType_ERC20 + tss := sample.Tss() + receiverChain := chains.Goerli + senderChain := chains.Goerli + sender := sample.EthAddress() + tssList := sample.TssList(3) + + // Set up mocks for CheckIfTSSMigrationTransfer + observerMock.On("GetAllTSS", ctx).Return(tssList) + observerMock.On("GetSupportedChainFromChainID", mock.Anything, senderChain.ChainId).Return(senderChain, true) + authorityMock.On("GetAdditionalChainList", ctx).Return([]chains.Chain{}) + // setup Mocks for GetTSS + observerMock.On("GetTSS", mock.Anything).Return(tss, true) + // setup Mocks for IsInboundEnabled + observerMock.On("IsInboundEnabled", ctx).Return(true) + // setup mocks for Initiate Outbound + observerMock.On("GetChainNonces", mock.Anything, mock.Anything). + Return(observerTypes.ChainNonces{Nonce: 1}, true) + observerMock.On("GetPendingNonces", mock.Anything, mock.Anything, mock.Anything). + Return(observerTypes.PendingNonces{NonceHigh: 1}, true) + observerMock.On("SetChainNonces", mock.Anything, mock.Anything).Return(nil) + observerMock.On("SetPendingNonces", mock.Anything, mock.Anything).Return(nil) + // setup Mocks for SetCctxAndNonceToCctxAndInboundHashToCctx + observerMock.On("SetNonceToCctx", mock.Anything, mock.Anything).Return(nil) + + k.SetGasPrice(ctx, types.GasPrice{ + ChainId: senderChain.ChainId, + MedianIndex: 0, + Prices: []uint64{100}, + }) + + // call InitiateOutbound + msg := types.MsgVoteInbound{ + Creator: creator, + Sender: sender.String(), + SenderChainId: senderChain.ChainId, + Receiver: receiver.String(), + ReceiverChain: receiverChain.ChainId, + Amount: amount, + Message: message, + InboundHash: inboundHash.String(), + InboundBlockHeight: inboundBlockHeight, + GasLimit: gasLimit, + CoinType: cointType, + TxOrigin: sender.String(), + Asset: asset, + EventIndex: eventIndex, + } + + _, err := k.ValidateInbound(ctx, &msg, false) + require.NoError(t, err) + require.Len(t, k.GetAllCrossChainTx(ctx), 1) + }) + + t.Run("fail if tss not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, + keepertest.CrosschainMockOptions{ + UseObserverMock: true, + UseFungibleMock: true, + UseAuthorityMock: true, + }) + + // Setup mock data + observerMock := keepertest.GetCrosschainObserverMock(t, k) + receiver := sample.EthAddress() + creator := sample.AccAddress() + amount := sdkmath.NewUint(42) + message := "test" + inboundBlockHeight := uint64(420) + inboundHash := sample.Hash() + gasLimit := uint64(100) + asset := "test-asset" + eventIndex := uint64(1) + cointType := coin.CoinType_ERC20 + tss := sample.Tss() + receiverChain := chains.Goerli + senderChain := chains.Goerli + sender := sample.EthAddress() + + // setup Mocks for GetTSS + observerMock.On("GetTSS", mock.Anything).Return(tss, false) + // setup Mocks for IsInboundEnabled + + k.SetGasPrice(ctx, types.GasPrice{ + ChainId: senderChain.ChainId, + MedianIndex: 0, + Prices: []uint64{100}, + }) + + // call InitiateOutbound + msg := types.MsgVoteInbound{ + Creator: creator, + Sender: sender.String(), + SenderChainId: senderChain.ChainId, + Receiver: receiver.String(), + ReceiverChain: receiverChain.ChainId, + Amount: amount, + Message: message, + InboundHash: inboundHash.String(), + InboundBlockHeight: inboundBlockHeight, + GasLimit: gasLimit, + CoinType: cointType, + TxOrigin: sender.String(), + Asset: asset, + EventIndex: eventIndex, + } + + _, err := k.ValidateInbound(ctx, &msg, false) + require.ErrorIs(t, err, types.ErrCannotFindTSSKeys) + }) + + t.Run("fail if InitiateOutbound fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, + keepertest.CrosschainMockOptions{ + UseObserverMock: true, + UseFungibleMock: true, + UseAuthorityMock: true, + }) + + // Setup mock data + observerMock := keepertest.GetCrosschainObserverMock(t, k) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + receiver := sample.EthAddress() + creator := sample.AccAddress() + amount := sdkmath.NewUint(42) + message := "test" + inboundBlockHeight := uint64(420) + inboundHash := sample.Hash() + gasLimit := uint64(100) + asset := "test-asset" + eventIndex := uint64(1) + cointType := coin.CoinType_ERC20 + tss := sample.Tss() + receiverChain := chains.Goerli + senderChain := chains.Goerli + sender := sample.EthAddress() + tssList := sample.TssList(3) + + // Set up mocks for CheckIfTSSMigrationTransfer + observerMock.On("GetAllTSS", ctx).Return(tssList) + observerMock.On("GetSupportedChainFromChainID", mock.Anything, senderChain.ChainId).Return(senderChain, true) + authorityMock.On("GetAdditionalChainList", ctx).Return([]chains.Chain{}) + // setup Mocks for GetTSS + observerMock.On("GetTSS", mock.Anything).Return(tss, true) + // setup Mocks for IsInboundEnabled + observerMock.On("IsInboundEnabled", ctx).Return(true) + // setup mocks for Initiate Outbound + observerMock.On("GetChainNonces", mock.Anything, mock.Anything). + Return(observerTypes.ChainNonces{Nonce: 1}, false) + + k.SetGasPrice(ctx, types.GasPrice{ + ChainId: senderChain.ChainId, + MedianIndex: 0, + Prices: []uint64{100}, + }) + + // call InitiateOutbound + msg := types.MsgVoteInbound{ + Creator: creator, + Sender: sender.String(), + SenderChainId: senderChain.ChainId, + Receiver: receiver.String(), + ReceiverChain: receiverChain.ChainId, + Amount: amount, + Message: message, + InboundHash: inboundHash.String(), + InboundBlockHeight: inboundBlockHeight, + GasLimit: gasLimit, + CoinType: cointType, + TxOrigin: sender.String(), + Asset: asset, + EventIndex: eventIndex, + } + + _, err := k.ValidateInbound(ctx, &msg, false) + require.ErrorIs(t, err, types.ErrCannotFindReceiverNonce) + }) + + t.Run("does not set cctx if SetCctxAndNonceToCctxAndInboundHashToCctx fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, + keepertest.CrosschainMockOptions{ + UseObserverMock: true, + UseFungibleMock: true, + UseAuthorityMock: true, + }) + + // Setup mock data + observerMock := keepertest.GetCrosschainObserverMock(t, k) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + receiver := sample.EthAddress() + creator := sample.AccAddress() + amount := sdkmath.NewUint(42) + message := "test" + inboundBlockHeight := uint64(420) + inboundHash := sample.Hash() + gasLimit := uint64(100) + asset := "test-asset" + eventIndex := uint64(1) + cointType := coin.CoinType_ERC20 + tss := sample.Tss() + receiverChain := chains.Goerli + senderChain := chains.Goerli + sender := sample.EthAddress() + tssList := sample.TssList(3) + + // Set up mocks for CheckIfTSSMigrationTransfer + observerMock.On("GetAllTSS", ctx).Return(tssList) + observerMock.On("GetSupportedChainFromChainID", mock.Anything, senderChain.ChainId).Return(senderChain, true) + authorityMock.On("GetAdditionalChainList", ctx).Return([]chains.Chain{}) + // setup Mocks for GetTSS + observerMock.On("GetTSS", mock.Anything).Return(tss, true).Twice() + // setup Mocks for IsInboundEnabled + observerMock.On("IsInboundEnabled", ctx).Return(true) + // setup mocks for Initiate Outbound + observerMock.On("GetChainNonces", mock.Anything, mock.Anything). + Return(observerTypes.ChainNonces{Nonce: 1}, true) + observerMock.On("GetPendingNonces", mock.Anything, mock.Anything, mock.Anything). + Return(observerTypes.PendingNonces{NonceHigh: 1}, true) + observerMock.On("SetChainNonces", mock.Anything, mock.Anything).Return(nil) + observerMock.On("SetPendingNonces", mock.Anything, mock.Anything).Return(nil) + // setup Mocks for SetCctxAndNonceToCctxAndInboundHashToCctx + observerMock.On("GetTSS", mock.Anything).Return(tss, false).Once() + + k.SetGasPrice(ctx, types.GasPrice{ + ChainId: senderChain.ChainId, + MedianIndex: 0, + Prices: []uint64{100}, + }) + + // call InitiateOutbound + msg := types.MsgVoteInbound{ + Creator: creator, + Sender: sender.String(), + SenderChainId: senderChain.ChainId, + Receiver: receiver.String(), + ReceiverChain: receiverChain.ChainId, + Amount: amount, + Message: message, + InboundHash: inboundHash.String(), + InboundBlockHeight: inboundBlockHeight, + GasLimit: gasLimit, + CoinType: cointType, + TxOrigin: sender.String(), + Asset: asset, + EventIndex: eventIndex, + } + + _, err := k.ValidateInbound(ctx, &msg, false) + require.NoError(t, err) + require.Len(t, k.GetAllCrossChainTx(ctx), 0) + }) + + t.Run("fail if inbound is disabled", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, + keepertest.CrosschainMockOptions{ + UseObserverMock: true, + UseFungibleMock: true, + UseAuthorityMock: true, + }) + + // Setup mock data + observerMock := keepertest.GetCrosschainObserverMock(t, k) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + receiver := sample.EthAddress() + creator := sample.AccAddress() + amount := sdkmath.NewUint(42) + message := "test" + inboundBlockHeight := uint64(420) + inboundHash := sample.Hash() + gasLimit := uint64(100) + asset := "test-asset" + eventIndex := uint64(1) + cointType := coin.CoinType_ERC20 + tss := sample.Tss() + receiverChain := chains.Goerli + senderChain := chains.Goerli + sender := sample.EthAddress() + tssList := sample.TssList(3) + + // Set up mocks for CheckIfTSSMigrationTransfer + observerMock.On("GetAllTSS", ctx).Return(tssList) + observerMock.On("GetSupportedChainFromChainID", mock.Anything, senderChain.ChainId).Return(senderChain, true) + authorityMock.On("GetAdditionalChainList", ctx).Return([]chains.Chain{}) + // setup Mocks for GetTSS + observerMock.On("GetTSS", mock.Anything).Return(tss, true) + // setup Mocks for IsInboundEnabled + observerMock.On("IsInboundEnabled", ctx).Return(false) + + k.SetGasPrice(ctx, types.GasPrice{ + ChainId: senderChain.ChainId, + MedianIndex: 0, + Prices: []uint64{100}, + }) + + // call InitiateOutbound + msg := types.MsgVoteInbound{ + Creator: creator, + Sender: sender.String(), + SenderChainId: senderChain.ChainId, + Receiver: receiver.String(), + ReceiverChain: receiverChain.ChainId, + Amount: amount, + Message: message, + InboundHash: inboundHash.String(), + InboundBlockHeight: inboundBlockHeight, + GasLimit: gasLimit, + CoinType: cointType, + TxOrigin: sender.String(), + Asset: asset, + EventIndex: eventIndex, + } + + _, err := k.ValidateInbound(ctx, &msg, false) + require.ErrorIs(t, err, observerTypes.ErrInboundDisabled) + }) + + t.Run("fails when CheckIfTSSMigrationTransfer fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, + keepertest.CrosschainMockOptions{ + UseObserverMock: true, + UseFungibleMock: true, + UseAuthorityMock: true, + }) + + // Setup mock data + observerMock := keepertest.GetCrosschainObserverMock(t, k) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + receiver := sample.EthAddress() + creator := sample.AccAddress() + amount := sdkmath.NewUint(42) + message := "test" + inboundBlockHeight := uint64(420) + inboundHash := sample.Hash() + gasLimit := uint64(100) + asset := "test-asset" + eventIndex := uint64(1) + cointType := coin.CoinType_ERC20 + receiverChain := chains.Goerli + senderChain := chains.Goerli + sender := sample.EthAddress() + tssList := sample.TssList(3) + + // setup Mocks for GetTSS + observerMock.On("GetTSS", mock.Anything).Return(tssList[0], true) + + // Set up mocks for CheckIfTSSMigrationTransfer + observerMock.On("GetAllTSS", ctx).Return(tssList) + observerMock.On("GetSupportedChainFromChainID", mock.Anything, senderChain.ChainId).Return(senderChain, false) + authorityMock.On("GetAdditionalChainList", ctx).Return([]chains.Chain{}) + + k.SetGasPrice(ctx, types.GasPrice{ + ChainId: senderChain.ChainId, + MedianIndex: 0, + Prices: []uint64{100}, + }) + + // call InitiateOutbound + msg := types.MsgVoteInbound{ + Creator: creator, + Sender: sender.String(), + SenderChainId: senderChain.ChainId, + Receiver: receiver.String(), + ReceiverChain: receiverChain.ChainId, + Amount: amount, + Message: message, + InboundHash: inboundHash.String(), + InboundBlockHeight: inboundBlockHeight, + GasLimit: gasLimit, + CoinType: cointType, + TxOrigin: sender.String(), + Asset: asset, + EventIndex: eventIndex, + } + + _, err := k.ValidateInbound(ctx, &msg, false) + require.ErrorIs(t, err, observerTypes.ErrSupportedChains) + }) +} +func TestKeeper_CheckMigration(t *testing.T) { + t.Run("Do not return error if sender is not a TSS address for evm chain", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, + keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + UseObserverMock: true, + }) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + chain := chains.Goerli + tssList := sample.TssList(3) + sender := sample.AccAddress() + + // Set up mocks + observerMock.On("GetAllTSS", ctx).Return(tssList) + observerMock.On("GetSupportedChainFromChainID", ctx, chain.ChainId).Return(chain, true) + authorityMock.On("GetAdditionalChainList", ctx).Return([]chains.Chain{}) + + msg := types.MsgVoteInbound{ + SenderChainId: chain.ChainId, + Sender: sender, + } + + err := k.CheckIfTSSMigrationTransfer(ctx, &msg) + require.NoError(t, err) + }) + + t.Run("Do not return error if sender is not a TSS address for btc chain", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, + keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + UseObserverMock: true, + }) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + chain := chains.BitcoinTestnet + tssList := sample.TssList(3) + sender := sample.AccAddress() + + // Set up mocks + observerMock.On("GetAllTSS", ctx).Return(tssList) + observerMock.On("GetSupportedChainFromChainID", ctx, chain.ChainId).Return(chain, true) + authorityMock.On("GetAdditionalChainList", ctx).Return([]chains.Chain{}) + + msg := types.MsgVoteInbound{ + SenderChainId: chain.ChainId, + Sender: sender, + } + + err := k.CheckIfTSSMigrationTransfer(ctx, &msg) + require.NoError(t, err) + }) + + t.Run("fails when chain is not supported", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, + keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + UseObserverMock: true, + }) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + chain := chains.Chain{ + ChainId: 999, + } + tssList := sample.TssList(3) + sender := sample.AccAddress() + + // Set up mocks + observerMock.On("GetAllTSS", ctx).Return(tssList) + observerMock.On("GetSupportedChainFromChainID", ctx, chain.ChainId).Return(chain, false) + authorityMock.On("GetAdditionalChainList", ctx).Return([]chains.Chain{}) + + msg := types.MsgVoteInbound{ + SenderChainId: chain.ChainId, + Sender: sender, + } + + err := k.CheckIfTSSMigrationTransfer(ctx, &msg) + require.ErrorIs(t, err, observerTypes.ErrSupportedChains) + }) + + t.Run("skips check when an older tss address is invalid for bitcoin chain", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, + keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + UseObserverMock: true, + }) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + chain := chains.BitcoinTestnet + tssList := sample.TssList(3) + tssList[0].TssPubkey = "invalid" + sender := sample.AccAddress() + + // Set up mocks + observerMock.On("GetAllTSS", ctx).Return(tssList) + observerMock.On("GetSupportedChainFromChainID", ctx, chain.ChainId).Return(chain, true) + authorityMock.On("GetAdditionalChainList", ctx).Return([]chains.Chain{}) + + msg := types.MsgVoteInbound{ + SenderChainId: chain.ChainId, + Sender: sender, + } + + err := k.CheckIfTSSMigrationTransfer(ctx, &msg) + require.NoError(t, err) + }) + + t.Run("skips check when an older tss address is invalid for evm chain", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, + keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + UseObserverMock: true, + }) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + chain := chains.Goerli + tssList := sample.TssList(3) + tssList[0].TssPubkey = "invalid" + sender := sample.AccAddress() + + // Set up mocks + observerMock.On("GetAllTSS", ctx).Return(tssList) + observerMock.On("GetSupportedChainFromChainID", ctx, chain.ChainId).Return(chain, true) + authorityMock.On("GetAdditionalChainList", ctx).Return([]chains.Chain{}) + + msg := types.MsgVoteInbound{ + SenderChainId: chain.ChainId, + Sender: sender, + } + + err := k.CheckIfTSSMigrationTransfer(ctx, &msg) + require.NoError(t, err) + }) + + t.Run("fails when sender is a TSS address for evm chain for evm chain", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, + keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + UseObserverMock: true, + }) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + chain := chains.Goerli + tssList := sample.TssList(3) + sender, err := crypto.GetTssAddrEVM(tssList[0].TssPubkey) + require.NoError(t, err) + + // Set up mocks + observerMock.On("GetAllTSS", ctx).Return(tssList) + observerMock.On("GetSupportedChainFromChainID", ctx, chain.ChainId).Return(chain, true) + authorityMock.On("GetAdditionalChainList", ctx).Return([]chains.Chain{}) + + msg := types.MsgVoteInbound{ + SenderChainId: chain.ChainId, + Sender: sender.String(), + } + + err = k.CheckIfTSSMigrationTransfer(ctx, &msg) + require.ErrorIs(t, err, types.ErrMigrationFromOldTss) + }) + + t.Run("fails when sender is a TSS address for btc chain for btc chain", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, + keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + UseObserverMock: true, + }) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + chain := chains.BitcoinTestnet + tssList := sample.TssList(3) + bitcoinParams, err := chains.BitcoinNetParamsFromChainID(chain.ChainId) + require.NoError(t, err) + sender, err := crypto.GetTssAddrBTC(tssList[0].TssPubkey, bitcoinParams) + require.NoError(t, err) + + // Set up mocks + observerMock.On("GetAllTSS", ctx).Return(tssList) + observerMock.On("GetSupportedChainFromChainID", ctx, chain.ChainId).Return(chain, true) + authorityMock.On("GetAdditionalChainList", ctx).Return([]chains.Chain{}) + + msg := types.MsgVoteInbound{ + SenderChainId: chain.ChainId, + Sender: sender, + } + + err = k.CheckIfTSSMigrationTransfer(ctx, &msg) + require.ErrorIs(t, err, types.ErrMigrationFromOldTss) + }) + + t.Run("fails if bitcoin network params not found for BTC chain", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, + keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + UseObserverMock: true, + }) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + chain := chains.Chain{ + ChainId: 999, + Consensus: chains.Consensus_bitcoin, + CctxGateway: chains.CCTXGateway_observers, + } + tssList := sample.TssList(3) + sender := sample.AccAddress() + + // Set up mocks + observerMock.On("GetAllTSS", ctx).Return(tssList) + observerMock.On("GetSupportedChainFromChainID", ctx, chain.ChainId).Return(chain, true) + authorityMock.On("GetAdditionalChainList", ctx).Return([]chains.Chain{chain}) + + msg := types.MsgVoteInbound{ + SenderChainId: chain.ChainId, + Sender: sender, + } + + err := k.CheckIfTSSMigrationTransfer(ctx, &msg) + require.ErrorContains(t, err, "no Bitcoin net params for chain ID: 999") + }) + + t.Run("fails if gateway is not observer ", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, + keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + chain := chains.GoerliLocalnet + chain.CctxGateway = chains.CCTXGateway_zevm + sender := sample.AccAddress() + + // Set up mocks + authorityMock.On("GetAdditionalChainList", ctx).Return([]chains.Chain{}) + + msg := types.MsgVoteInbound{ + SenderChainId: chain.ChainId, + Sender: sender, + } + + err := k.CheckIfTSSMigrationTransfer(ctx, &msg) + require.NoError(t, err) + }) +} diff --git a/x/crosschain/keeper/evm_hooks_test.go b/x/crosschain/keeper/evm_hooks_test.go index 36b8938a8a..12e01fba14 100644 --- a/x/crosschain/keeper/evm_hooks_test.go +++ b/x/crosschain/keeper/evm_hooks_test.go @@ -214,7 +214,8 @@ func TestKeeper_ProcessZRC20WithdrawalEvent(t *testing.T) { chain := chains.BitcoinMainnet chainID := chain.ChainId - setSupportedChain(ctx, zk, chainID) + senderChain := chains.ZetaChainMainnet + setSupportedChain(ctx, zk, []int64{chainID, senderChain.ChainId}...) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) event, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*sample.GetValidZRC20WithdrawToBTC(t).Logs[3]) @@ -239,7 +240,8 @@ func TestKeeper_ProcessZRC20WithdrawalEvent(t *testing.T) { chain := chains.Ethereum chainID := chain.ChainId - setSupportedChain(ctx, zk, chainID) + senderChain := chains.ZetaChainMainnet + setSupportedChain(ctx, zk, []int64{chainID, senderChain.ChainId}...) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) event, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*sample.GetValidZrc20WithdrawToETH(t).Logs[11]) @@ -378,7 +380,8 @@ func TestKeeper_ProcessZRC20WithdrawalEvent(t *testing.T) { chain := chains.Ethereum chainID := chain.ChainId - setSupportedChain(ctx, zk, chainID) + senderChain := chains.ZetaChainMainnet + setSupportedChain(ctx, zk, []int64{chainID, senderChain.ChainId}...) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) k.RemoveGasPrice(ctx, strconv.FormatInt(chainID, 10)) @@ -400,7 +403,8 @@ func TestKeeper_ProcessZRC20WithdrawalEvent(t *testing.T) { chain := chains.Ethereum chainID := chain.ChainId - setSupportedChain(ctx, zk, chainID) + senderChain := chains.ZetaChainMainnet + setSupportedChain(ctx, zk, []int64{chainID, senderChain.ChainId}...) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) zk.ObserverKeeper.SetChainNonces(ctx, observertypes.ChainNonces{ Index: chain.ChainName.String(), @@ -472,7 +476,8 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { chain := chains.Ethereum chainID := chain.ChainId - setSupportedChain(ctx, zk, chainID) + senderChain := chains.ZetaChainMainnet + setSupportedChain(ctx, zk, []int64{chainID, senderChain.ChainId}...) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) admin := keepertest.SetAdminPolicies(ctx, zk.AuthorityKeeper) @@ -604,7 +609,8 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { chain := chains.Ethereum chainID := chain.ChainId - setSupportedChain(ctx, zk, chainID) + senderChain := chains.ZetaChainMainnet + setSupportedChain(ctx, zk, []int64{chainID, senderChain.ChainId}...) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) amount, ok := sdkmath.NewIntFromString("20000000000000000000000") @@ -633,7 +639,8 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { chain := chains.Ethereum chainID := chain.ChainId - setSupportedChain(ctx, zk, chainID) + senderChain := chains.ZetaChainMainnet + setSupportedChain(ctx, zk, []int64{chainID, senderChain.ChainId}...) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) admin := keepertest.SetAdminPolicies(ctx, zk.AuthorityKeeper) @@ -673,7 +680,8 @@ func TestKeeper_ProcessLogs(t *testing.T) { chain := chains.BitcoinMainnet chainID := chain.ChainId - setSupportedChain(ctx, zk, chainID) + senderChain := chains.ZetaChainMainnet + setSupportedChain(ctx, zk, []int64{chainID, senderChain.ChainId}...) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) block := sample.GetValidZRC20WithdrawToBTC(t) @@ -699,7 +707,8 @@ func TestKeeper_ProcessLogs(t *testing.T) { chain := chains.Ethereum chainID := chain.ChainId - setSupportedChain(ctx, zk, chainID) + senderChain := chains.ZetaChainMainnet + setSupportedChain(ctx, zk, []int64{chainID, senderChain.ChainId}...) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) admin := keepertest.SetAdminPolicies(ctx, zk.AuthorityKeeper) SetupStateForProcessLogsZetaSent(t, ctx, k, zk, sdkk, chain, admin) @@ -799,7 +808,8 @@ func TestKeeper_ProcessLogs(t *testing.T) { chain := chains.BitcoinMainnet chainID := chain.ChainId - setSupportedChain(ctx, zk, chainID) + senderChain := chains.ZetaChainMainnet + setSupportedChain(ctx, zk, []int64{chainID, senderChain.ChainId}...) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) block := sample.GetValidZRC20WithdrawToBTC(t) diff --git a/x/crosschain/keeper/msg_server_migrate_tss_funds.go b/x/crosschain/keeper/msg_server_migrate_tss_funds.go index f5558fe689..8656cf2e80 100644 --- a/x/crosschain/keeper/msg_server_migrate_tss_funds.go +++ b/x/crosschain/keeper/msg_server_migrate_tss_funds.go @@ -181,7 +181,10 @@ func (k Keeper) MigrateTSSFundsForChain( ), ) } - cctx.GetCurrentOutboundParam().Amount = amount.Sub(evmFee) + + cctx.GetCurrentOutboundParam().Amount = amount.Sub( + evmFee.Add(sdkmath.NewUintFromString(types.TSSMigrationBufferAmountEVM)), + ) } // Set the sender and receiver addresses for Bitcoin chain if chains.IsBitcoinChain(chainID, additionalChains) { diff --git a/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go b/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go index 9da596f876..7e86307fc0 100644 --- a/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go +++ b/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go @@ -8,7 +8,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/gas" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" @@ -386,7 +385,8 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { cctx, found := k.GetCrossChainTx(ctx, index) require.True(t, found) feeCalculated := sdk.NewUint(cctx.GetCurrentOutboundParam().GasLimit). - Mul(sdkmath.NewUintFromString(cctx.GetCurrentOutboundParam().GasPrice)) + Mul(sdkmath.NewUintFromString(cctx.GetCurrentOutboundParam().GasPrice)). + Add(sdkmath.NewUintFromString(crosschaintypes.TSSMigrationBufferAmountEVM)) require.Equal(t, cctx.GetCurrentOutboundParam().Amount.String(), amount.Sub(feeCalculated).String()) }) diff --git a/x/crosschain/keeper/msg_server_update_tss.go b/x/crosschain/keeper/msg_server_update_tss.go index 56052b6671..78c3b833d7 100644 --- a/x/crosschain/keeper/msg_server_update_tss.go +++ b/x/crosschain/keeper/msg_server_update_tss.go @@ -38,10 +38,10 @@ func (k msgServer) UpdateTssAddress( tssMigrators := k.zetaObserverKeeper.GetAllTssFundMigrators(ctx) // Each connected chain should have its own tss migrator - if len(k.zetaObserverKeeper.GetSupportedChains(ctx)) != len(tssMigrators) { + if len(k.zetaObserverKeeper.GetSupportedForeignChains(ctx)) != len(tssMigrators) { return nil, errorsmod.Wrap( types.ErrUnableToUpdateTss, - "cannot update tss address not enough migrations have been created and completed", + "cannot update tss address incorrect number of migrations have been created and completed", ) } diff --git a/x/crosschain/keeper/msg_server_update_tss_test.go b/x/crosschain/keeper/msg_server_update_tss_test.go index 4e894e1586..761ff617c8 100644 --- a/x/crosschain/keeper/msg_server_update_tss_test.go +++ b/x/crosschain/keeper/msg_server_update_tss_test.go @@ -65,7 +65,7 @@ func TestMsgServer_UpdateTssAddress(t *testing.T) { k.GetObserverKeeper().SetTSSHistory(ctx, tssOld) k.GetObserverKeeper().SetTSSHistory(ctx, tssNew) k.GetObserverKeeper().SetTSS(ctx, tssOld) - for _, chain := range k.GetObserverKeeper().GetSupportedChains(ctx) { + for _, chain := range k.GetObserverKeeper().GetSupportedForeignChains(ctx) { index := chain.ChainName.String() + "_migration_tx_index" k.GetObserverKeeper().SetFundMigrator(ctx, types.TssFundMigratorInfo{ ChainId: chain.ChainId, @@ -78,7 +78,7 @@ func TestMsgServer_UpdateTssAddress(t *testing.T) { require.Equal( t, len(k.GetObserverKeeper().GetAllTssFundMigrators(ctx)), - len(k.GetObserverKeeper().GetSupportedChains(ctx)), + len(k.GetObserverKeeper().GetSupportedForeignChains(ctx)), ) msg := crosschaintypes.MsgUpdateTssAddress{ @@ -224,7 +224,11 @@ func TestMsgServer_UpdateTssAddress(t *testing.T) { } keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, nil) _, err := msgServer.UpdateTssAddress(ctx, &msg) - require.ErrorContains(t, err, "cannot update tss address not enough migrations have been created and completed") + require.ErrorContains( + t, + err, + "cannot update tss address incorrect number of migrations have been created and completed: unable to update TSS address", + ) require.ErrorIs(t, err, crosschaintypes.ErrUnableToUpdateTss) tss, found := k.GetObserverKeeper().GetTSS(ctx) require.True(t, found) diff --git a/x/crosschain/types/errors.go b/x/crosschain/types/errors.go index 6f430e5e1a..b468d2c962 100644 --- a/x/crosschain/types/errors.go +++ b/x/crosschain/types/errors.go @@ -50,4 +50,10 @@ var ( ErrMaxTxOutTrackerHashesReached = errorsmod.Register(ModuleName, 1153, "max tx out tracker hashes reached") ErrInitiatitingOutbound = errorsmod.Register(ModuleName, 1154, "cannot initiate outbound") ErrInvalidWithdrawalAmount = errorsmod.Register(ModuleName, 1155, "invalid withdrawal amount") + ErrMigrationFromOldTss = errorsmod.Register( + ModuleName, + 1156, + "migration tx from an old tss address detected", + ) + ErrValidatingInbound = errorsmod.Register(ModuleName, 1157, "unable to validate inbound") ) diff --git a/x/crosschain/types/keys.go b/x/crosschain/types/keys.go index da56a5e797..5bb89a61ea 100644 --- a/x/crosschain/types/keys.go +++ b/x/crosschain/types/keys.go @@ -27,6 +27,8 @@ const ( //TssMigrationGasMultiplierEVM is multiplied to the median gas price to get the gas price for the tss migration . This is done to avoid the tss migration tx getting stuck in the mempool TssMigrationGasMultiplierEVM = "2.5" + // TSSMigrationBufferAmountEVM is the buffer amount added to the gas price for the tss migration transaction + TSSMigrationBufferAmountEVM = "2100000000" // CCTXIndexLength is the length of a crosschain transaction index CCTXIndexLength = 66 diff --git a/x/fungible/keeper/msg_server_remove_foreign_coin.go b/x/fungible/keeper/msg_server_remove_foreign_coin.go index 6903531cd3..dbbcf36fe6 100644 --- a/x/fungible/keeper/msg_server_remove_foreign_coin.go +++ b/x/fungible/keeper/msg_server_remove_foreign_coin.go @@ -24,7 +24,7 @@ func (k msgServer) RemoveForeignCoin( if err != nil { return nil, cosmoserrors.Wrap(authoritytypes.ErrUnauthorized, err.Error()) } - index := msg.Name + index := msg.Zrc20Address _, found := k.GetForeignCoins(ctx, index) if !found { return nil, cosmoserrors.Wrapf(sdkerrors.ErrInvalidRequest, "foreign coin not found") diff --git a/x/fungible/types/message_remove_foreign_coin.go b/x/fungible/types/message_remove_foreign_coin.go index 0c7282dc4a..7fc1781b76 100644 --- a/x/fungible/types/message_remove_foreign_coin.go +++ b/x/fungible/types/message_remove_foreign_coin.go @@ -10,10 +10,10 @@ const TypeMsgRemoveForeignCoin = "remove_foreign_coin" var _ sdk.Msg = &MsgRemoveForeignCoin{} -func NewMsgRemoveForeignCoin(creator string, name string) *MsgRemoveForeignCoin { +func NewMsgRemoveForeignCoin(creator string, zrc20Address string) *MsgRemoveForeignCoin { return &MsgRemoveForeignCoin{ - Creator: creator, - Name: name, + Creator: creator, + Zrc20Address: zrc20Address, } } diff --git a/x/fungible/types/tx.pb.go b/x/fungible/types/tx.pb.go index f72a00bc24..8409ef6f36 100644 --- a/x/fungible/types/tx.pb.go +++ b/x/fungible/types/tx.pb.go @@ -473,8 +473,8 @@ func (m *MsgDeployFungibleCoinZRC20Response) GetAddress() string { } type MsgRemoveForeignCoin struct { - Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` + Zrc20Address string `protobuf:"bytes,2,opt,name=zrc20_address,json=zrc20Address,proto3" json:"zrc20_address,omitempty"` } func (m *MsgRemoveForeignCoin) Reset() { *m = MsgRemoveForeignCoin{} } @@ -517,9 +517,9 @@ func (m *MsgRemoveForeignCoin) GetCreator() string { return "" } -func (m *MsgRemoveForeignCoin) GetName() string { +func (m *MsgRemoveForeignCoin) GetZrc20Address() string { if m != nil { - return m.Name + return m.Zrc20Address } return "" } @@ -947,70 +947,69 @@ func init() { } var fileDescriptor_7bea9688d1d01113 = []byte{ - // 998 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x57, 0xdd, 0x6e, 0xdb, 0x46, - 0x13, 0x35, 0xed, 0xf8, 0x6f, 0x3e, 0xcb, 0xf6, 0xb7, 0x50, 0x62, 0x86, 0x2e, 0xe4, 0x84, 0x71, - 0x13, 0x37, 0x68, 0xc4, 0x54, 0x75, 0x1b, 0x14, 0x68, 0x52, 0xd4, 0x4a, 0xdc, 0x06, 0x88, 0x80, - 0x82, 0x71, 0x52, 0xd4, 0x37, 0xc4, 0x9a, 0x5c, 0x53, 0x84, 0xa5, 0x5d, 0x96, 0x4b, 0x55, 0x51, - 0xee, 0x0a, 0xf4, 0x2a, 0x40, 0x81, 0x00, 0x7d, 0x80, 0x3e, 0x43, 0xdf, 0x22, 0x97, 0xb9, 0x2c, - 0x82, 0x22, 0x28, 0xec, 0x17, 0x29, 0x76, 0xf9, 0x63, 0x52, 0x12, 0x25, 0x59, 0xed, 0x8d, 0xcd, - 0x1d, 0x9d, 0x39, 0x7b, 0x66, 0x76, 0x66, 0x96, 0x84, 0xed, 0x97, 0x24, 0xc4, 0x76, 0x13, 0x7b, - 0xd4, 0x90, 0x4f, 0x2c, 0x20, 0xc6, 0x71, 0x87, 0xba, 0xde, 0x51, 0x8b, 0x18, 0xe1, 0x8b, 0xaa, - 0x1f, 0xb0, 0x90, 0xa1, 0xcd, 0x14, 0x55, 0x4d, 0x50, 0xd5, 0x04, 0xa5, 0x95, 0x5d, 0xe6, 0x32, - 0x89, 0x33, 0xc4, 0x53, 0xe4, 0xa2, 0xdd, 0x1c, 0x42, 0xec, 0x9f, 0xb8, 0x86, 0xcd, 0x3c, 0x2a, - 0xff, 0x44, 0x38, 0x7d, 0x17, 0xd4, 0x06, 0x77, 0x1f, 0x12, 0xbf, 0xc5, 0x7a, 0x4f, 0x7b, 0x3c, - 0x24, 0xed, 0x3a, 0xa3, 0x61, 0x80, 0xed, 0x90, 0x23, 0x15, 0x16, 0xed, 0x80, 0xe0, 0x90, 0x05, - 0xaa, 0x72, 0x4d, 0xd9, 0x59, 0x36, 0x93, 0xa5, 0xfe, 0x97, 0x02, 0xd7, 0x8a, 0xdc, 0x4c, 0xc2, - 0x7d, 0x46, 0x39, 0x41, 0xb7, 0x61, 0xbd, 0x43, 0x3d, 0xde, 0xc5, 0xfe, 0xf3, 0xda, 0x3e, 0xb6, - 0x43, 0x16, 0xf4, 0x62, 0x9e, 0x01, 0x3b, 0x2a, 0xc3, 0x7c, 0x57, 0xe8, 0x54, 0x67, 0x25, 0x20, - 0x5a, 0xa0, 0x1d, 0x58, 0x4b, 0x91, 0x26, 0xeb, 0x84, 0x24, 0x50, 0xe7, 0xe4, 0xef, 0xfd, 0x66, - 0xb4, 0x0d, 0x25, 0x9b, 0x51, 0x4a, 0x04, 0xdb, 0xe1, 0xa3, 0xe7, 0x0d, 0xf5, 0x92, 0xc4, 0xe5, - 0x8d, 0xe8, 0x26, 0xac, 0xf2, 0x9c, 0x58, 0x75, 0x5e, 0xc2, 0xfa, 0xac, 0xfa, 0xab, 0x59, 0xb8, - 0xda, 0xe0, 0xee, 0x33, 0xdf, 0xc1, 0x21, 0x39, 0x34, 0xeb, 0xb5, 0xbb, 0xdf, 0x7b, 0x61, 0xd3, - 0x09, 0x70, 0x77, 0x9f, 0x90, 0xe2, 0xb4, 0xa0, 0x1b, 0x50, 0x7a, 0x19, 0xd8, 0xb5, 0xbb, 0x16, - 0x76, 0x9c, 0x80, 0x70, 0x1e, 0x47, 0xb3, 0x22, 0x8d, 0x5f, 0x47, 0x36, 0xf4, 0x03, 0xac, 0x53, - 0xd2, 0xb5, 0xba, 0x31, 0xa3, 0x75, 0x4c, 0x88, 0xba, 0x20, 0x70, 0x7b, 0xc6, 0x9b, 0xf7, 0x5b, - 0x33, 0xef, 0xde, 0x6f, 0xdd, 0x72, 0xbd, 0xb0, 0xd9, 0x39, 0xaa, 0xda, 0xac, 0x6d, 0xd8, 0x8c, - 0xb7, 0x19, 0x8f, 0xff, 0xdd, 0xe1, 0xce, 0x89, 0x11, 0xf6, 0x7c, 0xc2, 0xab, 0xcf, 0x3c, 0x1a, - 0x9a, 0xab, 0x94, 0x74, 0xb3, 0xca, 0x9e, 0x42, 0x49, 0x50, 0xbb, 0x98, 0x5b, 0x2d, 0xaf, 0xed, - 0x85, 0xea, 0xe2, 0x74, 0xbc, 0xff, 0xa3, 0xa4, 0xfb, 0x0d, 0xe6, 0x4f, 0x04, 0x87, 0x7e, 0x03, - 0xae, 0x17, 0xe6, 0x22, 0x39, 0x6b, 0x3d, 0x80, 0x8d, 0x14, 0x94, 0xaf, 0x87, 0x11, 0xe9, 0xba, - 0x0f, 0x9b, 0x42, 0x6e, 0x94, 0x7c, 0xcb, 0x8e, 0x1d, 0xfa, 0x92, 0xa7, 0x52, 0xd2, 0xcd, 0x33, - 0xc6, 0x89, 0xd4, 0xaf, 0xc3, 0x56, 0xc1, 0x9e, 0xa9, 0xac, 0xdf, 0x67, 0x41, 0x4b, 0xeb, 0x74, - 0x3f, 0xee, 0x98, 0x3a, 0xf3, 0xa8, 0x0c, 0x64, 0x84, 0xb4, 0x32, 0xcc, 0x3f, 0x12, 0x90, 0xa4, - 0x1e, 0xe5, 0x02, 0xed, 0xc0, 0xfa, 0x31, 0x0b, 0x88, 0xe7, 0x52, 0x4b, 0xb6, 0x96, 0xe5, 0x39, - 0xb2, 0x20, 0xe7, 0xcc, 0xd5, 0xd8, 0x5e, 0x17, 0xe6, 0xc7, 0x0e, 0xd2, 0x60, 0xc9, 0x21, 0xb6, - 0xd7, 0xc6, 0x2d, 0x2e, 0x4b, 0xb1, 0x64, 0xa6, 0x6b, 0x84, 0xe0, 0x12, 0xc5, 0x6d, 0x12, 0xd7, - 0x9e, 0x7c, 0x46, 0x57, 0x60, 0x81, 0xf7, 0xda, 0x47, 0xac, 0x15, 0x95, 0x82, 0x19, 0xaf, 0xd0, - 0x1e, 0x2c, 0x8b, 0x66, 0xb5, 0xc4, 0xe1, 0xc8, 0xd3, 0x5c, 0xad, 0x7d, 0x58, 0x1d, 0x32, 0x0d, - 0xfc, 0x13, 0xb7, 0x2a, 0xbb, 0x5a, 0x04, 0x77, 0xd0, 0xf3, 0x89, 0xb9, 0x64, 0xc7, 0x4f, 0x68, - 0x13, 0x96, 0xcf, 0x2b, 0x62, 0x49, 0xca, 0x5d, 0x72, 0x93, 0xd3, 0x7d, 0x00, 0x7a, 0x71, 0x82, - 0xd2, 0x56, 0x56, 0x61, 0x31, 0x39, 0x95, 0x38, 0x51, 0xf1, 0x52, 0x7f, 0x08, 0xe5, 0x06, 0x77, - 0x4d, 0xd2, 0x66, 0x3f, 0x91, 0xfd, 0x38, 0x07, 0xcc, 0xa3, 0x23, 0x52, 0x9b, 0x84, 0x3f, 0x7b, - 0x1e, 0xbe, 0x5e, 0x81, 0x0f, 0x86, 0xb1, 0xa4, 0xe7, 0xf8, 0x8b, 0x92, 0x69, 0xc8, 0xe4, 0x94, - 0xf7, 0x7a, 0x21, 0xb1, 0x99, 0x33, 0xaa, 0x21, 0x3f, 0x82, 0xf5, 0x82, 0xb2, 0x5a, 0xb3, 0xf3, - 0xd5, 0x84, 0xf4, 0xa8, 0x77, 0x04, 0xa1, 0xd5, 0xc4, 0xbc, 0x19, 0x4f, 0x1a, 0xd1, 0x0a, 0x75, - 0xe6, 0x90, 0x6f, 0x31, 0x6f, 0xe6, 0x5a, 0xa1, 0x5f, 0x45, 0xaa, 0xf5, 0x0f, 0x45, 0xd6, 0x5c, - 0xa6, 0x61, 0x9e, 0x78, 0x3f, 0x76, 0x3c, 0xc7, 0x0b, 0x7b, 0x75, 0xec, 0xff, 0xdb, 0xe9, 0x71, - 0x00, 0xa5, 0x56, 0x42, 0x67, 0xd9, 0xd8, 0x8f, 0x64, 0x5e, 0xbc, 0xc5, 0x57, 0x5a, 0x19, 0x51, - 0xfa, 0xb6, 0xac, 0x82, 0x02, 0xc9, 0x69, 0x64, 0x26, 0x94, 0x1a, 0xdc, 0xfd, 0x0e, 0x77, 0x38, - 0x19, 0xd7, 0x3f, 0xb7, 0x60, 0x2d, 0x17, 0x0b, 0x11, 0xd1, 0xcc, 0x89, 0x51, 0x9b, 0x8d, 0x86, - 0x70, 0x7d, 0x03, 0x2e, 0xe7, 0x38, 0xd3, 0xcd, 0x0e, 0x60, 0x4d, 0x48, 0xa2, 0xfe, 0x7f, 0xba, - 0xdd, 0xd5, 0x68, 0x4e, 0x65, 0x58, 0x93, 0x0d, 0x6b, 0xef, 0x96, 0x61, 0xae, 0xc1, 0x5d, 0xf4, - 0xab, 0x02, 0x97, 0x87, 0xdf, 0x87, 0x9f, 0x55, 0x47, 0xdc, 0xc3, 0xd5, 0xa2, 0xfb, 0x50, 0xbb, - 0x3f, 0x95, 0x5b, 0xda, 0x7b, 0xbf, 0x29, 0xb0, 0x51, 0x34, 0xc0, 0xee, 0x4d, 0x46, 0x3d, 0xe0, - 0xa8, 0x7d, 0x35, 0xa5, 0x63, 0xaa, 0xea, 0x67, 0x05, 0xfe, 0x3f, 0xd8, 0xf5, 0x9f, 0x8c, 0xa3, - 0x1d, 0x70, 0xd1, 0xbe, 0xb8, 0xb0, 0x4b, 0xaa, 0xe1, 0x95, 0x02, 0xe5, 0xa1, 0x57, 0xce, 0xee, - 0x38, 0xce, 0x61, 0x5e, 0xda, 0x97, 0xd3, 0x78, 0xa5, 0x62, 0x5e, 0x2b, 0x70, 0xa5, 0x60, 0x3e, - 0x7d, 0x3e, 0x19, 0x71, 0xbf, 0x9f, 0xf6, 0x60, 0x3a, 0xbf, 0x21, 0x92, 0x06, 0xde, 0x61, 0x26, - 0x94, 0xd4, 0xef, 0x37, 0xa9, 0xa4, 0xa2, 0xf7, 0x04, 0x59, 0xcc, 0x45, 0x93, 0xf1, 0xde, 0x05, - 0xb8, 0xb3, 0x8e, 0xe3, 0x8b, 0x79, 0xcc, 0x60, 0x43, 0x2d, 0x80, 0xcc, 0x54, 0xbb, 0x3d, 0x8e, - 0xee, 0x1c, 0xab, 0xd5, 0x26, 0xc7, 0xa6, 0xbb, 0x05, 0xb0, 0x92, 0x1b, 0x6b, 0x1f, 0x8f, 0x95, - 0x9f, 0x41, 0x6b, 0xbb, 0x17, 0x41, 0x27, 0x7b, 0xee, 0x3d, 0x7e, 0x73, 0x5a, 0x51, 0xde, 0x9e, - 0x56, 0x94, 0xbf, 0x4f, 0x2b, 0xca, 0xeb, 0xb3, 0xca, 0xcc, 0xdb, 0xb3, 0xca, 0xcc, 0x9f, 0x67, - 0x95, 0x99, 0x43, 0x23, 0x73, 0x63, 0x08, 0xbe, 0x3b, 0x7d, 0x1f, 0x0d, 0x2f, 0x32, 0xdf, 0x23, - 0xe2, 0xfa, 0x38, 0x5a, 0x90, 0x1f, 0x0e, 0x9f, 0xfe, 0x13, 0x00, 0x00, 0xff, 0xff, 0x6a, 0x35, - 0xf0, 0xc9, 0xbb, 0x0c, 0x00, 0x00, + // 992 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x57, 0xdd, 0x6e, 0xdb, 0x36, + 0x14, 0x8e, 0x92, 0xe6, 0xef, 0x2c, 0x4e, 0x32, 0xc2, 0x6d, 0x54, 0x65, 0x70, 0x5a, 0x35, 0x6b, + 0xb3, 0x62, 0xb5, 0x3a, 0x2f, 0x5b, 0x31, 0x60, 0xed, 0xb0, 0x78, 0xcd, 0x56, 0xa0, 0x06, 0x06, + 0x35, 0xe9, 0xb0, 0xdc, 0x08, 0x8c, 0xc4, 0xc8, 0x42, 0x6c, 0x52, 0x13, 0xe5, 0xb9, 0xee, 0xdd, + 0x80, 0x5d, 0x15, 0x18, 0x50, 0x60, 0x0f, 0xb0, 0x67, 0xd8, 0x5b, 0xf4, 0xb2, 0x97, 0x43, 0x31, + 0x14, 0x43, 0xf2, 0x22, 0x03, 0xa9, 0x9f, 0x4a, 0xb6, 0x15, 0x3b, 0x4e, 0x6f, 0x12, 0x91, 0xfe, + 0xce, 0xc7, 0x8f, 0x87, 0xe7, 0x3b, 0x94, 0x60, 0xf3, 0x39, 0x09, 0xb1, 0xdd, 0xc4, 0x1e, 0x35, + 0xe4, 0x13, 0x0b, 0x88, 0x71, 0xd4, 0xa1, 0xae, 0x77, 0xd8, 0x22, 0x46, 0xf8, 0xac, 0xea, 0x07, + 0x2c, 0x64, 0x68, 0x3d, 0x45, 0x55, 0x13, 0x54, 0x35, 0x41, 0x69, 0x65, 0x97, 0xb9, 0x4c, 0xe2, + 0x0c, 0xf1, 0x14, 0x85, 0x68, 0x37, 0x87, 0x10, 0xfb, 0xc7, 0xae, 0x61, 0x33, 0x8f, 0xca, 0x3f, + 0x11, 0x4e, 0xdf, 0x06, 0xb5, 0xc1, 0xdd, 0xef, 0x88, 0xdf, 0x62, 0xbd, 0x27, 0x3d, 0x1e, 0x92, + 0x76, 0x9d, 0xd1, 0x30, 0xc0, 0x76, 0xc8, 0x91, 0x0a, 0xf3, 0x76, 0x40, 0x70, 0xc8, 0x02, 0x55, + 0xb9, 0xa6, 0x6c, 0x2d, 0x9a, 0xc9, 0x50, 0xff, 0x57, 0x81, 0x6b, 0x45, 0x61, 0x26, 0xe1, 0x3e, + 0xa3, 0x9c, 0xa0, 0xdb, 0xb0, 0xda, 0xa1, 0x1e, 0xef, 0x62, 0xff, 0x69, 0x6d, 0x17, 0xdb, 0x21, + 0x0b, 0x7a, 0x31, 0xcf, 0xc0, 0x3c, 0x2a, 0xc3, 0x6c, 0x57, 0xe8, 0x54, 0xa7, 0x25, 0x20, 0x1a, + 0xa0, 0x2d, 0x58, 0x49, 0x91, 0x26, 0xeb, 0x84, 0x24, 0x50, 0x67, 0xe4, 0xef, 0xfd, 0xd3, 0x68, + 0x13, 0x4a, 0x36, 0xa3, 0x94, 0x08, 0xb6, 0x83, 0x87, 0x4f, 0x1b, 0xea, 0x25, 0x89, 0xcb, 0x4f, + 0xa2, 0x9b, 0xb0, 0xcc, 0x73, 0x62, 0xd5, 0x59, 0x09, 0xeb, 0x9b, 0xd5, 0x5f, 0x4c, 0xc3, 0xd5, + 0x06, 0x77, 0xf7, 0x7d, 0x07, 0x87, 0xe4, 0xc0, 0xac, 0xd7, 0xee, 0xfe, 0xe4, 0x85, 0x4d, 0x27, + 0xc0, 0xdd, 0x5d, 0x42, 0x8a, 0xd3, 0x82, 0x6e, 0x40, 0xe9, 0x79, 0x60, 0xd7, 0xee, 0x5a, 0xd8, + 0x71, 0x02, 0xc2, 0x79, 0xbc, 0x9b, 0x25, 0x39, 0xf9, 0x6d, 0x34, 0x87, 0x7e, 0x86, 0x55, 0x4a, + 0xba, 0x56, 0x37, 0x66, 0xb4, 0x8e, 0x08, 0x51, 0xe7, 0x04, 0x6e, 0xc7, 0x78, 0xf5, 0x76, 0x63, + 0xea, 0xcd, 0xdb, 0x8d, 0x5b, 0xae, 0x17, 0x36, 0x3b, 0x87, 0x55, 0x9b, 0xb5, 0x0d, 0x9b, 0xf1, + 0x36, 0xe3, 0xf1, 0xbf, 0x3b, 0xdc, 0x39, 0x36, 0xc2, 0x9e, 0x4f, 0x78, 0x75, 0xdf, 0xa3, 0xa1, + 0xb9, 0x4c, 0x49, 0x37, 0xab, 0xec, 0x09, 0x94, 0x04, 0xb5, 0x8b, 0xb9, 0xd5, 0xf2, 0xda, 0x5e, + 0xa8, 0xce, 0x4f, 0xc6, 0xfb, 0x01, 0x25, 0xdd, 0xef, 0x31, 0x7f, 0x2c, 0x38, 0xf4, 0x1b, 0x70, + 0xbd, 0x30, 0x17, 0xc9, 0x59, 0xeb, 0x01, 0xac, 0xa5, 0xa0, 0x7c, 0x3d, 0x9c, 0x91, 0xae, 0xfb, + 0xb0, 0x2e, 0xe4, 0x46, 0xc9, 0xb7, 0xec, 0x38, 0xa0, 0x2f, 0x79, 0x2a, 0x25, 0xdd, 0x3c, 0x63, + 0x9c, 0x48, 0xfd, 0x3a, 0x6c, 0x14, 0xac, 0x99, 0xca, 0xfa, 0x6b, 0x1a, 0xb4, 0xb4, 0x4e, 0x77, + 0x63, 0xc7, 0xd4, 0x99, 0x47, 0xe5, 0x46, 0xce, 0x90, 0x56, 0x86, 0xd9, 0x87, 0x02, 0x92, 0xd4, + 0xa3, 0x1c, 0xa0, 0x2d, 0x58, 0x3d, 0x62, 0x01, 0xf1, 0x5c, 0x6a, 0x49, 0x6b, 0x59, 0x9e, 0x23, + 0x0b, 0x72, 0xc6, 0x5c, 0x8e, 0xe7, 0xeb, 0x62, 0xfa, 0x91, 0x83, 0x34, 0x58, 0x70, 0x88, 0xed, + 0xb5, 0x71, 0x8b, 0xcb, 0x52, 0x2c, 0x99, 0xe9, 0x18, 0x21, 0xb8, 0x44, 0x71, 0x9b, 0xc4, 0xb5, + 0x27, 0x9f, 0xd1, 0x15, 0x98, 0xe3, 0xbd, 0xf6, 0x21, 0x6b, 0x45, 0xa5, 0x60, 0xc6, 0x23, 0xb4, + 0x03, 0x8b, 0xc2, 0xac, 0x96, 0x38, 0x1c, 0x79, 0x9a, 0xcb, 0xb5, 0x8f, 0xab, 0x43, 0xba, 0x81, + 0x7f, 0xec, 0x56, 0xa5, 0xab, 0xc5, 0xe6, 0xf6, 0x7a, 0x3e, 0x31, 0x17, 0xec, 0xf8, 0x09, 0xad, + 0xc3, 0xe2, 0xbb, 0x8a, 0x58, 0x90, 0x72, 0x17, 0xdc, 0xe4, 0x74, 0x1f, 0x80, 0x5e, 0x9c, 0xa0, + 0xd4, 0xca, 0x2a, 0xcc, 0x27, 0xa7, 0x12, 0x27, 0x2a, 0x1e, 0xea, 0xfb, 0x50, 0x6e, 0x70, 0xd7, + 0x24, 0x6d, 0xf6, 0x2b, 0xd9, 0x8d, 0x73, 0xc0, 0x3c, 0x7a, 0x41, 0x93, 0xe8, 0x15, 0xf8, 0x68, + 0x18, 0x6d, 0x7a, 0xb0, 0xbf, 0x2b, 0x19, 0x87, 0x26, 0xc7, 0xbe, 0xd3, 0x0b, 0x89, 0xcd, 0x9c, + 0xb3, 0x1c, 0xfa, 0x09, 0xac, 0x16, 0xd4, 0xd9, 0x8a, 0x9d, 0x2f, 0x2f, 0xa4, 0x47, 0x66, 0x12, + 0x84, 0x56, 0x13, 0xf3, 0x66, 0xdc, 0x7a, 0x84, 0x37, 0xea, 0xcc, 0x21, 0x3f, 0x60, 0xde, 0xcc, + 0x79, 0xa3, 0x5f, 0x45, 0xaa, 0xf5, 0x6f, 0x45, 0x16, 0x61, 0xc6, 0x41, 0x8f, 0xbd, 0x5f, 0x3a, + 0x9e, 0xe3, 0x85, 0xbd, 0x3a, 0xf6, 0x2f, 0xda, 0x4e, 0xf6, 0xa0, 0xd4, 0x4a, 0xe8, 0x2c, 0x1b, + 0xfb, 0x91, 0xcc, 0xf3, 0x7b, 0x7e, 0xa9, 0x95, 0x11, 0xa5, 0x6f, 0xca, 0xb2, 0x28, 0x90, 0x9c, + 0xee, 0xcc, 0x84, 0x52, 0x83, 0xbb, 0x3f, 0xe2, 0x0e, 0x27, 0xa3, 0x0c, 0x75, 0x0b, 0x56, 0x72, + 0x7b, 0x21, 0x62, 0x37, 0x33, 0xa2, 0xf7, 0x66, 0x77, 0x43, 0xb8, 0xbe, 0x06, 0x97, 0x73, 0x9c, + 0xe9, 0x62, 0x7b, 0xb0, 0x22, 0x24, 0x51, 0xff, 0xbd, 0x2e, 0x77, 0x35, 0x6a, 0x5c, 0x19, 0xd6, + 0x64, 0xc1, 0xda, 0x9b, 0x45, 0x98, 0x69, 0x70, 0x17, 0xfd, 0xa1, 0xc0, 0xe5, 0xe1, 0x17, 0xe4, + 0x17, 0xd5, 0x33, 0x2e, 0xe6, 0x6a, 0xd1, 0x05, 0xa9, 0xdd, 0x9f, 0x28, 0x2c, 0x35, 0xe3, 0x9f, + 0x0a, 0xac, 0x15, 0x75, 0xb4, 0x7b, 0xe3, 0x51, 0x0f, 0x04, 0x6a, 0xdf, 0x4c, 0x18, 0x98, 0xaa, + 0xfa, 0x4d, 0x81, 0x0f, 0x07, 0xdb, 0xc0, 0x67, 0xa3, 0x68, 0x07, 0x42, 0xb4, 0xaf, 0xce, 0x1d, + 0x92, 0x6a, 0x78, 0xa1, 0x40, 0x79, 0xe8, 0x1d, 0xb4, 0x3d, 0x8a, 0x73, 0x58, 0x94, 0xf6, 0xf5, + 0x24, 0x51, 0xa9, 0x98, 0x97, 0x0a, 0x5c, 0x29, 0xe8, 0x4f, 0x5f, 0x8e, 0x47, 0xdc, 0x1f, 0xa7, + 0x3d, 0x98, 0x2c, 0x6e, 0x88, 0xa4, 0x81, 0x97, 0x9a, 0x31, 0x25, 0xf5, 0xc7, 0x8d, 0x2b, 0xa9, + 0xe8, 0xc5, 0x41, 0x16, 0x73, 0x51, 0x67, 0xbc, 0x77, 0x0e, 0xee, 0x6c, 0xe0, 0xe8, 0x62, 0x1e, + 0xd1, 0xd8, 0x50, 0x0b, 0x20, 0xd3, 0xd5, 0x6e, 0x8f, 0xa2, 0x7b, 0x87, 0xd5, 0x6a, 0xe3, 0x63, + 0xd3, 0xd5, 0x02, 0x58, 0xca, 0xb5, 0xb5, 0x4f, 0x47, 0xca, 0xcf, 0xa0, 0xb5, 0xed, 0xf3, 0xa0, + 0x93, 0x35, 0x77, 0x1e, 0xbd, 0x3a, 0xa9, 0x28, 0xaf, 0x4f, 0x2a, 0xca, 0x7f, 0x27, 0x15, 0xe5, + 0xe5, 0x69, 0x65, 0xea, 0xf5, 0x69, 0x65, 0xea, 0x9f, 0xd3, 0xca, 0xd4, 0x81, 0x91, 0xb9, 0x31, + 0x04, 0xdf, 0x9d, 0xbe, 0xaf, 0x88, 0x67, 0x99, 0x0f, 0x14, 0x71, 0x7d, 0x1c, 0xce, 0xc9, 0x2f, + 0x89, 0xcf, 0xff, 0x0f, 0x00, 0x00, 0xff, 0xff, 0xe6, 0xe8, 0x3c, 0x66, 0xcc, 0x0c, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1730,10 +1729,10 @@ func (m *MsgRemoveForeignCoin) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if len(m.Name) > 0 { - i -= len(m.Name) - copy(dAtA[i:], m.Name) - i = encodeVarintTx(dAtA, i, uint64(len(m.Name))) + if len(m.Zrc20Address) > 0 { + i -= len(m.Zrc20Address) + copy(dAtA[i:], m.Zrc20Address) + i = encodeVarintTx(dAtA, i, uint64(len(m.Zrc20Address))) i-- dAtA[i] = 0x12 } @@ -2200,7 +2199,7 @@ func (m *MsgRemoveForeignCoin) Size() (n int) { if l > 0 { n += 1 + l + sovTx(uint64(l)) } - l = len(m.Name) + l = len(m.Zrc20Address) if l > 0 { n += 1 + l + sovTx(uint64(l)) } @@ -3423,7 +3422,7 @@ func (m *MsgRemoveForeignCoin) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Zrc20Address", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -3451,7 +3450,7 @@ func (m *MsgRemoveForeignCoin) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Name = string(dAtA[iNdEx:postIndex]) + m.Zrc20Address = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex diff --git a/zetaclient/chains/bitcoin/observer/inbound.go b/zetaclient/chains/bitcoin/observer/inbound.go index ce0d74062a..47a8177cff 100644 --- a/zetaclient/chains/bitcoin/observer/inbound.go +++ b/zetaclient/chains/bitcoin/observer/inbound.go @@ -86,6 +86,7 @@ func (ob *Observer) ObserveInbound(ctx context.Context) error { } // #nosec G115 checked positive lastBlock := uint64(cnt) + if lastBlock < ob.LastBlock() { return fmt.Errorf( "observeInboundBTC: block number should not decrease: current %d last %d", @@ -130,7 +131,6 @@ func (ob *Observer) ObserveInbound(ctx context.Context) error { ob.logger.Inbound.Warn().Err(err).Msgf("observeInboundBTC: error posting block header %d", blockNumber) } } - if len(res.Block.Tx) > 1 { // get depositor fee depositorFee := bitcoin.CalcDepositorFee(res.Block, ob.Chain().ChainId, ob.netParams, ob.logger.Inbound) diff --git a/zetaclient/chains/bitcoin/observer/outbound.go b/zetaclient/chains/bitcoin/observer/outbound.go index 7bae9de963..c7c1c649d7 100644 --- a/zetaclient/chains/bitcoin/observer/outbound.go +++ b/zetaclient/chains/bitcoin/observer/outbound.go @@ -184,6 +184,11 @@ func (ob *Observer) IsOutboundProcessed( // It's safe to use cctx's amount to post confirmation because it has already been verified in observeOutbound() amountInSat := params.Amount.BigInt() if res.Confirmations < ob.ConfirmationsThreshold(amountInSat) { + ob.logger.Outbound.Debug(). + Int64("currentConfirmations", res.Confirmations). + Int64("requiredConfirmations", ob.ConfirmationsThreshold(amountInSat)). + Msg("IsOutboundProcessed: outbound not confirmed yet") + return true, false, nil } @@ -467,7 +472,6 @@ func (ob *Observer) setIncludedTx(nonce uint64, getTxResult *btcjson.GetTransact ob.Mu().Lock() defer ob.Mu().Unlock() res, found := ob.includedTxResults[outboundID] - if !found { // not found. ob.includedTxHashes[txHash] = true ob.includedTxResults[outboundID] = getTxResult // include new outbound and enforce rigid 1-to-1 mapping: nonce <===> txHash @@ -476,7 +480,7 @@ func (ob *Observer) setIncludedTx(nonce uint64, getTxResult *btcjson.GetTransact } ob.logger.Outbound.Info(). Msgf("setIncludedTx: included new bitcoin outbound %s outboundID %s pending nonce %d", txHash, outboundID, ob.pendingNonce) - } else if txHash == res.TxID { // found same hash. + } else if txHash == res.TxID { // found same hash ob.includedTxResults[outboundID] = getTxResult // update tx result as confirmations may increase if getTxResult.Confirmations > res.Confirmations { ob.logger.Outbound.Info().Msgf("setIncludedTx: bitcoin outbound %s got confirmations %d", txHash, getTxResult.Confirmations) diff --git a/zetaclient/chains/bitcoin/signer/signer.go b/zetaclient/chains/bitcoin/signer/signer.go index 93b2fac800..a6a5a3e51d 100644 --- a/zetaclient/chains/bitcoin/signer/signer.go +++ b/zetaclient/chains/bitcoin/signer/signer.go @@ -34,7 +34,7 @@ import ( const ( // the maximum number of inputs per outbound - maxNoOfInputsPerTx = 20 + MaxNoOfInputsPerTx = 20 // the rank below (or equal to) which we consolidate UTXOs consolidationRank = 10 @@ -198,7 +198,7 @@ func (signer *Signer) SignWithdrawTx( prevOuts, total, consolidatedUtxo, consolidatedValue, err := observer.SelectUTXOs( ctx, amount+estimateFee+float64(nonceMark)*1e-8, - maxNoOfInputsPerTx, + MaxNoOfInputsPerTx, nonce, consolidationRank, false, diff --git a/zetaclient/chains/evm/observer/inbound.go b/zetaclient/chains/evm/observer/inbound.go index f1ca7a79b4..3dba2aa62e 100644 --- a/zetaclient/chains/evm/observer/inbound.go +++ b/zetaclient/chains/evm/observer/inbound.go @@ -781,7 +781,6 @@ func (ob *Observer) ObserveTSSReceiveInBlock(ctx context.Context, blockNumber ui if err != nil { return errors.Wrapf(err, "error getting block %d for chain %d", blockNumber, ob.Chain().ChainId) } - for i := range block.Transactions { tx := block.Transactions[i] if ethcommon.HexToAddress(tx.To) == ob.TSS().EVMAddress() { diff --git a/zetaclient/chains/evm/observer/outbound.go b/zetaclient/chains/evm/observer/outbound.go index 54ac2aab1d..7b4f200d52 100644 --- a/zetaclient/chains/evm/observer/outbound.go +++ b/zetaclient/chains/evm/observer/outbound.go @@ -9,7 +9,6 @@ import ( "time" "cosmossdk.io/math" - "github.com/ethereum/go-ethereum" ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" @@ -390,11 +389,18 @@ func (ob *Observer) checkConfirmedTx( if err != nil { log.Error(). Err(err). - Msgf("confirmTxByHash: error getting transaction for outbound %s chain %d", txHash, ob.Chain().ChainId) + Str("function", "confirmTxByHash"). + Str("outboundTxHash", txHash). + Int64("chainID", ob.Chain().ChainId). + Msg("error getting transaction for outbound") return nil, nil, false } if transaction == nil { // should not happen - log.Error().Msgf("confirmTxByHash: transaction is nil for txHash %s nonce %d", txHash, nonce) + log.Error(). + Str("function", "confirmTxByHash"). + Str("outboundTxHash", txHash). + Uint64("nonce", nonce). + Msg("transaction is nil for txHash") return nil, nil, false } @@ -404,17 +410,51 @@ func (ob *Observer) checkConfirmedTx( if err != nil { log.Error(). Err(err). - Msgf("confirmTxByHash: local recovery of sender address failed for outbound %s chain %d", transaction.Hash().Hex(), ob.Chain().ChainId) + Str("function", "confirmTxByHash"). + Str("outboundTxHash", transaction.Hash().Hex()). + Int64("chainID", ob.Chain().ChainId). + Msg("local recovery of sender address failed for outbound") return nil, nil, false } if from != ob.TSS().EVMAddress() { // must be TSS address - log.Error().Msgf("confirmTxByHash: sender %s for outbound %s chain %d is not TSS address %s", - from.Hex(), transaction.Hash().Hex(), ob.Chain().ChainId, ob.TSS().EVMAddress().Hex()) - return nil, nil, false + // If from is not TSS address, check if it is one of the previous TSS addresses We can still try to confirm a tx which was broadcast by an old TSS + // This is to handle situations where the outbound has already been broad-casted by an older TSS address and the zetacore is waiting for the all the required block confirmations + // to go through before marking the cctx into a finalized state + + // TODO : improve this logic to verify that the correct TSS address is the from address. + // https://github.com/zeta-chain/node/issues/2487 + log.Info(). + Str("function", "confirmTxByHash"). + Str("sender", from.Hex()). + Str("outboundTxHash", transaction.Hash().Hex()). + Int64("chainID", ob.Chain().ChainId). + Str("currentTSSAddress", ob.TSS().EVMAddress().Hex()). + Msg("sender is not current TSS address") + addressList := ob.TSS().EVMAddressList() + isOldTssAddress := false + for _, addr := range addressList { + if from == addr { + isOldTssAddress = true + } + } + if !isOldTssAddress { + log.Error(). + Str("function", "confirmTxByHash"). + Str("sender", from.Hex()). + Str("outboundTxHash", transaction.Hash().Hex()). + Int64("chainID", ob.Chain().ChainId). + Str("currentTSSAddress", ob.TSS().EVMAddress().Hex()). + Msg("sender is not current or old TSS address") + return nil, nil, false + } } if transaction.Nonce() != nonce { // must match cctx nonce log.Error(). - Msgf("confirmTxByHash: outbound %s nonce mismatch: wanted %d, got tx nonce %d", txHash, nonce, transaction.Nonce()) + Str("function", "confirmTxByHash"). + Str("outboundTxHash", txHash). + Uint64("wantedNonce", nonce). + Uint64("gotTxNonce", transaction.Nonce()). + Msg("outbound nonce mismatch") return nil, nil, false } @@ -427,21 +467,41 @@ func (ob *Observer) checkConfirmedTx( // query receipt receipt, err := ob.evmClient.TransactionReceipt(ctx, ethcommon.HexToHash(txHash)) if err != nil { - if err != ethereum.NotFound { - log.Warn().Err(err).Msgf("confirmTxByHash: TransactionReceipt error, txHash %s nonce %d", txHash, nonce) - } + log.Error(). + Err(err). + Str("function", "confirmTxByHash"). + Str("outboundTxHash", txHash). + Uint64("nonce", nonce). + Msg("transactionReceipt error") return nil, nil, false } if receipt == nil { // should not happen - log.Error().Msgf("confirmTxByHash: receipt is nil for txHash %s nonce %d", txHash, nonce) + log.Error(). + Str("function", "confirmTxByHash"). + Str("outboundTxHash", txHash). + Uint64("nonce", nonce). + Msg("receipt is nil") return nil, nil, false } - + ob.LastBlock() // check confirmations - if !ob.HasEnoughConfirmations(receipt, ob.LastBlock()) { + lastHeight, err := ob.evmClient.BlockNumber(ctx) + if err != nil { + log.Error(). + Str("function", "confirmTxByHash"). + Err(err). + Int64("chainID", ob.GetChainParams().ChainId). + Msg("error getting block number for chain") + return nil, nil, false + } + if !ob.HasEnoughConfirmations(receipt, lastHeight) { log.Debug(). - Msgf("confirmTxByHash: txHash %s nonce %d included but not confirmed: receipt block %d, current block %d", - txHash, nonce, receipt.BlockNumber, ob.LastBlock()) + Str("function", "confirmTxByHash"). + Str("txHash", txHash). + Uint64("nonce", nonce). + Uint64("receiptBlock", receipt.BlockNumber.Uint64()). + Uint64("currentBlock", lastHeight). + Msg("txHash included but not confirmed") return nil, nil, false } @@ -449,7 +509,13 @@ func (ob *Observer) checkConfirmedTx( // Note: a guard for false BlockNumber in receipt. The blob-carrying tx won't come here err = ob.CheckTxInclusion(transaction, receipt) if err != nil { - log.Error().Err(err).Msgf("confirmTxByHash: checkTxInclusion error for txHash %s nonce %d", txHash, nonce) + log.Error(). + Err(err). + Str("function", "confirmTxByHash"). + Str("errorContext", "checkTxInclusion"). + Str("txHash", txHash). + Uint64("nonce", nonce). + Msg("checkTxInclusion error") return nil, nil, false } diff --git a/zetaclient/chains/interfaces/interfaces.go b/zetaclient/chains/interfaces/interfaces.go index 8d9bc549db..6964aac3b4 100644 --- a/zetaclient/chains/interfaces/interfaces.go +++ b/zetaclient/chains/interfaces/interfaces.go @@ -233,6 +233,8 @@ type TSSSigner interface { SignBatch(ctx context.Context, digests [][]byte, height uint64, nonce uint64, chainID int64) ([][65]byte, error) EVMAddress() ethcommon.Address + + EVMAddressList() []ethcommon.Address BTCAddress() string BTCAddressWitnessPubkeyHash() *btcutil.AddressWitnessPubKeyHash PubKeyCompressedBytes() []byte diff --git a/zetaclient/testutils/mocks/tss_signer.go b/zetaclient/testutils/mocks/tss_signer.go index a7a9690293..b4f989aaf4 100644 --- a/zetaclient/testutils/mocks/tss_signer.go +++ b/zetaclient/testutils/mocks/tss_signer.go @@ -108,6 +108,10 @@ func (s *TSS) EVMAddress() ethcommon.Address { return crypto.PubkeyToAddress(s.PrivKey.PublicKey) } +func (s *TSS) EVMAddressList() []ethcommon.Address { + return []ethcommon.Address{s.EVMAddress()} +} + func (s *TSS) BTCAddress() string { // force use btcAddress if set if s.btcAddress != "" { diff --git a/zetaclient/tss/tss_signer.go b/zetaclient/tss/tss_signer.go index dc813eb784..7db6f27a8e 100644 --- a/zetaclient/tss/tss_signer.go +++ b/zetaclient/tss/tss_signer.go @@ -33,7 +33,7 @@ import ( observertypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" "github.com/zeta-chain/zetacore/zetaclient/config" - appcontext "github.com/zeta-chain/zetacore/zetaclient/context" + zctx "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/zetaclient/keys" "github.com/zeta-chain/zetacore/zetaclient/metrics" ) @@ -89,41 +89,37 @@ type TSS struct { BitcoinChainID int64 } -// NewTSS creates a new TSS instance +// NewTSS creates a new TSS instance which can be used to sign transactions func NewTSS( ctx context.Context, - appContext *appcontext.AppContext, - peer p2p.AddrList, - privkey tmcrypto.PrivKey, - preParams *keygen.LocalPreParams, client interfaces.ZetacoreClient, tssHistoricalList []observertypes.TSS, bitcoinChainID int64, - tssPassword string, hotkeyPassword string, + tssServer *tss.TssServer, ) (*TSS, error) { logger := log.With().Str("module", "tss_signer").Logger() - server, err := SetupTSSServer(peer, privkey, preParams, appContext.Config(), tssPassword) + app, err := zctx.FromContext(ctx) if err != nil { - return nil, fmt.Errorf("SetupTSSServer error: %w", err) + return nil, err } newTss := TSS{ - Server: server, + Server: tssServer, Keys: make(map[string]*Key), - CurrentPubkey: appContext.GetCurrentTssPubKey(), + CurrentPubkey: app.GetCurrentTssPubKey(), logger: logger, ZetacoreClient: client, KeysignsTracker: NewKeysignsTracker(logger), BitcoinChainID: bitcoinChainID, } - err = newTss.LoadTssFilesFromDirectory(appContext.Config().TssPath) + err = newTss.LoadTssFilesFromDirectory(app.Config().TssPath) if err != nil { return nil, err } - _, pubkeyInBech32, err := keys.GetKeyringKeybase(appContext.Config(), hotkeyPassword) + _, pubkeyInBech32, err := keys.GetKeyringKeybase(app.Config(), hotkeyPassword) if err != nil { return nil, err } @@ -144,6 +140,8 @@ func NewTSS( } metrics.NumActiveMsgSigns.Set(0) + newTss.Signers = app.GetKeygen().GranteePubkeys + return &newTss, nil } @@ -155,6 +153,7 @@ func SetupTSSServer( preParams *keygen.LocalPreParams, cfg config.Config, tssPassword string, + enableMonitor bool, ) (*tss.TssServer, error) { bootstrapPeers := peer log.Info().Msgf("Peers AddrList %v", bootstrapPeers) @@ -183,7 +182,7 @@ func SetupTSSServer( "MetaMetaOpenTheDoor", tsspath, thorcommon.TssConfig{ - EnableMonitor: true, + EnableMonitor: enableMonitor, KeyGenTimeout: 300 * time.Second, // must be shorter than constants.JailTimeKeygen KeySignTimeout: 30 * time.Second, // must be shorter than constants.JailTimeKeysign PartyTimeout: 30 * time.Second, @@ -446,6 +445,19 @@ func (tss *TSS) EVMAddress() ethcommon.Address { return addr } +func (tss *TSS) EVMAddressList() []ethcommon.Address { + addresses := make([]ethcommon.Address, 0) + for _, key := range tss.Keys { + addr, err := GetTssAddrEVM(key.PubkeyInBech32) + if err != nil { + log.Error().Err(err).Msg("getKeyAddr error") + return nil + } + addresses = append(addresses, addr) + } + return addresses +} + // BTCAddress generates a bech32 p2wpkh address from pubkey func (tss *TSS) BTCAddress() string { addr, err := GetTssAddrBTC(tss.CurrentPubkey, tss.BitcoinChainID) @@ -585,7 +597,8 @@ func GetTssAddrEVM(tssPubkey string) (ethcommon.Address, error) { // TestKeysign tests the keysign // it is called when a new TSS is generated to ensure the network works as expected // TODO(revamp): move to a test package -func TestKeysign(tssPubkey string, tssServer *tss.TssServer) error { + +func TestKeysign(tssPubkey string, tssServer tss.TssServer) error { log.Info().Msg("trying keysign...") data := []byte("hello meta") H := crypto.Keccak256Hash(data) diff --git a/zetaclient/zetacore/client_monitor.go b/zetaclient/zetacore/client_monitor.go index 6c124ced48..b505af90a1 100644 --- a/zetaclient/zetacore/client_monitor.go +++ b/zetaclient/zetacore/client_monitor.go @@ -168,15 +168,11 @@ func retryWithBackoff(call func() error, attempts int, minInternal, maxInterval if attempts < 1 { return errors.New("attempts must be positive") } + bo := backoff.NewExponentialBackOff() + bo.InitialInterval = minInternal + bo.MaxInterval = maxInterval - bo := backoff.WithMaxRetries( - backoff.NewExponentialBackOff( - backoff.WithInitialInterval(minInternal), - backoff.WithMaxInterval(maxInterval), - ), - // #nosec G115 always positive - uint64(attempts), - ) + backoffWithRetry := backoff.WithMaxRetries(bo, uint64(attempts)) - return retry.DoWithBackoff(call, bo) + return retry.DoWithBackoff(call, backoffWithRetry) }