diff --git a/.github/actions/install-dependencies/action.yml b/.github/actions/install-dependencies/action.yml index 580c60e08d..3cac451e88 100644 --- a/.github/actions/install-dependencies/action.yml +++ b/.github/actions/install-dependencies/action.yml @@ -33,7 +33,7 @@ runs: - uses: actions/setup-go@v5 if: ${{ inputs.skip_go == 'false' }} with: - go-version: '1.20' + go-version: '1.22' cache: false - uses: actions/setup-python@v4 diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 97dc97aa9a..44e3fa0538 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -48,7 +48,7 @@ jobs: if: ${{ github.event.inputs.skip_checks != 'true' }} uses: actions/setup-go@v5 with: - go-version: '1.20' + go-version: '1.22' - name: Run Gosec Security Scanner if: ${{ github.event.inputs.skip_checks != 'true' }} @@ -79,7 +79,7 @@ jobs: if: ${{ github.event.inputs.skip_checks != 'true' }} uses: actions/setup-go@v5 with: - go-version: '1.20' + go-version: '1.22' - name: Run Cosmos Gosec Security Scanner if: ${{ github.event.inputs.skip_checks != 'true' }} @@ -109,7 +109,7 @@ jobs: if: ${{ github.event.inputs.skip_checks != 'true' }} uses: actions/setup-go@v5 with: - go-version: '1.20' + go-version: '1.22' - name: Run golangci-lint if: ${{ github.event.inputs.skip_checks != 'true' }} diff --git a/.github/workflows/sast-linters.yml b/.github/workflows/sast-linters.yml index 08c545fd3a..30a8ab1f33 100644 --- a/.github/workflows/sast-linters.yml +++ b/.github/workflows/sast-linters.yml @@ -25,33 +25,10 @@ jobs: with: fetch-depth: 0 - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: '1.20' - - name: Run Gosec Security Scanner - uses: securego/gosec@v2.19.0 - with: - args: ./... - - gosec-cosmos: - runs-on: ubuntu-22.04 - env: - GO111MODULE: on - steps: - - name: Checkout Source - uses: actions/checkout@v4 + uses: zeta-chain/gosec@v2.21.0-zeta with: - fetch-depth: 0 - - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: '1.20' - - - name: Run Cosmos Gosec Security Scanner - run: make lint-cosmos-gosec + args: -exclude-generated -exclude-dir testutil ./... lint: runs-on: ubuntu-22.04 @@ -67,7 +44,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: '1.20' + go-version: '1.22' - name: Run golangci-lint uses: golangci/golangci-lint-action@v6 diff --git a/Dockerfile-localnet b/Dockerfile-localnet index 24b98adf53..efd2ac8b4f 100644 --- a/Dockerfile-localnet +++ b/Dockerfile-localnet @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1.7-labs -FROM golang:1.20.14-bookworm AS base-build +FROM golang:1.22.5-bookworm AS base-build ENV GOPATH /go ENV GOOS=linux @@ -22,10 +22,10 @@ COPY --exclude=*.sh --exclude=*.md --exclude=*.yml . . RUN --mount=type=cache,target="/root/.cache/go-build" make install RUN --mount=type=cache,target="/root/.cache/go-build" make install-zetae2e -FROM golang:1.20.14-bookworm AS cosmovisor-build +FROM golang:1.22.5-bookworm AS cosmovisor-build RUN go install cosmossdk.io/tools/cosmovisor/cmd/cosmovisor@v1.5.0 -FROM golang:1.20.14-bookworm AS base-runtime +FROM golang:1.22.5-bookworm AS base-runtime RUN apt update && \ apt install -yq jq yq curl tmux python3 openssh-server iputils-ping iproute2 bind9-host && \ diff --git a/Makefile b/Makefile index 471c9af947..9f1482f882 100644 --- a/Makefile +++ b/Makefile @@ -139,8 +139,8 @@ lint-pre: lint: lint-pre @golangci-lint run -lint-cosmos-gosec: - @bash ./scripts/cosmos-gosec.sh +lint-gosec: + @bash ./scripts/gosec.sh gosec: gosec -exclude-dir=localnet ./... @@ -271,6 +271,7 @@ zetanode-upgrade: zetanode .PHONY: zetanode-upgrade endif + start-upgrade-test: zetanode-upgrade @echo "--> Starting upgrade test" export LOCALNET_MODE=upgrade && \ @@ -283,6 +284,14 @@ start-upgrade-test-light: zetanode-upgrade export UPGRADE_HEIGHT=90 && \ cd contrib/localnet/ && $(DOCKER) compose --profile upgrade -f docker-compose.yml -f docker-compose-upgrade.yml up -d + +start-upgrade-test-admin: zetanode-upgrade + @echo "--> Starting admin upgrade test" + export LOCALNET_MODE=upgrade && \ + export UPGRADE_HEIGHT=90 && \ + export E2E_ARGS="--skip-regular --test-admin" && \ + cd contrib/localnet/ && $(DOCKER) compose --profile upgrade -f docker-compose.yml -f docker-compose-upgrade.yml up -d + start-upgrade-import-mainnet-test: zetanode-upgrade @echo "--> Starting import-data upgrade test" export LOCALNET_MODE=upgrade && \ diff --git a/changelog.md b/changelog.md index 887230bd31..473901a55b 100644 --- a/changelog.md +++ b/changelog.md @@ -4,7 +4,7 @@ ### Breaking Changes -* [List of the breaking changes can be found in this document](docs/releases/v17_breaking_changes.md) +* [2460](https://github.com/zeta-chain/node/pull/2460) - Upgrade to go 1.22. This required us to temporarily remove the QUIC backend from [go-libp2p](https://github.com/libp2p/go-libp2p). If you are a zetaclient operator and have configured quic peers, you need to switch to tcp peers. ### Features @@ -58,6 +58,7 @@ * [2375](https://github.com/zeta-chain/node/pull/2375) - improve & speedup code formatting * [2380](https://github.com/zeta-chain/node/pull/2380) - use `ChainInfo` in `authority` to allow dynamically support new chains * [2395](https://github.com/zeta-chain/node/pull/2395) - converge AppContext with ZetaCoreContext in zetaclient +* [2428](https://github.com/zeta-chain/node/pull/2428) - propagate context across codebase & refactor zetacore client ### Tests @@ -76,7 +77,8 @@ * [2349](https://github.com/zeta-chain/node/pull/2349) - add TestBitcoinDepositRefund and WithdrawBitcoinMultipleTimes E2E tests * [2368](https://github.com/zeta-chain/node/pull/2368) - eliminate panic usage across testing suite * [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 ### Fixes @@ -89,6 +91,7 @@ * [2327](https://github.com/zeta-chain/node/pull/2327) - partially cherry picked the fix to Bitcoin outbound dust amount * [2362](https://github.com/zeta-chain/node/pull/2362) - set 1000 satoshis as minimum BTC amount that can be withdrawn from zEVM * [2382](https://github.com/zeta-chain/node/pull/2382) - add tx input and gas in rpc methods for synthetic eth txs +* [2396](https://github.com/zeta-chain/node/issues/2386) - special handle bitcoin testnet gas price estimator * [2434](https://github.com/zeta-chain/node/pull/2434) - the default database when running `zetacored init` is now pebbledb ### CI diff --git a/cmd/zetaclientd/debug.go b/cmd/zetaclientd/debug.go index 28a3932a8d..d28f5cb898 100644 --- a/cmd/zetaclientd/debug.go +++ b/cmd/zetaclientd/debug.go @@ -21,7 +21,7 @@ import ( btcobserver "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/observer" evmobserver "github.com/zeta-chain/zetacore/zetaclient/chains/evm/observer" "github.com/zeta-chain/zetacore/zetaclient/config" - clientcontext "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/zetacore" ) @@ -57,7 +57,8 @@ func debugCmd(_ *cobra.Command, args []string) error { return err } - appContext := clientcontext.New(cfg, zerolog.Nop()) + appContext := zctx.New(cfg, zerolog.Nop()) + ctx := zctx.WithAppContext(context.Background(), appContext) chainID, err := strconv.ParseInt(args[1], 10, 64) if err != nil { @@ -74,15 +75,16 @@ func debugCmd(_ *cobra.Command, args []string) error { "", debugArgs.zetaChainID, false, - nil) + zerolog.Nop(), + ) if err != nil { return err } - chainParams, err := client.GetChainParams() + chainParams, err := client.GetChainParams(ctx) if err != nil { return err } - tssEthAddress, err := client.GetEthTssAddress() + tssEthAddress, err := client.GetEVMTSSAddress(ctx) if err != nil { return err } @@ -148,19 +150,19 @@ func debugCmd(_ *cobra.Command, args []string) error { switch coinType { case coin.CoinType_Zeta: - ballotIdentifier, err = evmObserver.CheckAndVoteInboundTokenZeta(tx, receipt, false) + ballotIdentifier, err = evmObserver.CheckAndVoteInboundTokenZeta(ctx, tx, receipt, false) if err != nil { return err } case coin.CoinType_ERC20: - ballotIdentifier, err = evmObserver.CheckAndVoteInboundTokenERC20(tx, receipt, false) + ballotIdentifier, err = evmObserver.CheckAndVoteInboundTokenERC20(ctx, tx, receipt, false) if err != nil { return err } case coin.CoinType_Gas: - ballotIdentifier, err = evmObserver.CheckAndVoteInboundTokenGas(tx, receipt, false) + ballotIdentifier, err = evmObserver.CheckAndVoteInboundTokenGas(ctx, tx, receipt, false) if err != nil { return err } @@ -186,7 +188,7 @@ func debugCmd(_ *cobra.Command, args []string) error { return err } btcObserver.WithBtcClient(btcClient) - ballotIdentifier, err = btcObserver.CheckReceiptForBtcTxHash(inboundHash, false) + ballotIdentifier, err = btcObserver.CheckReceiptForBtcTxHash(ctx, inboundHash, false) if err != nil { return err } @@ -194,7 +196,7 @@ func debugCmd(_ *cobra.Command, args []string) error { fmt.Println("BallotIdentifier : ", ballotIdentifier) // query ballot - ballot, err := client.GetBallot(ballotIdentifier) + ballot, err := client.GetBallot(ctx, ballotIdentifier) if err != nil { return err } diff --git a/cmd/zetaclientd/init.go b/cmd/zetaclientd/init.go index c41f669c44..c6c231bf77 100644 --- a/cmd/zetaclientd/init.go +++ b/cmd/zetaclientd/init.go @@ -78,7 +78,7 @@ func Initialize(_ *cobra.Command, _ []string) error { } //Create new config struct - configData := config.New() + configData := config.New(true) //Validate Peer eg. /ip4/172.0.2.1/tcp/6668/p2p/16Uiu2HAmACG5DtqmQsHtXg4G2sLS65ttv84e7MrL4kapkjfmhxAp if len(initArgs.peer) != 0 { diff --git a/cmd/zetaclientd/keygen_tss.go b/cmd/zetaclientd/keygen_tss.go index 63b5d98041..05e6d7f01f 100644 --- a/cmd/zetaclientd/keygen_tss.go +++ b/cmd/zetaclientd/keygen_tss.go @@ -1,6 +1,7 @@ package main import ( + "context" "encoding/hex" "encoding/json" "errors" @@ -16,14 +17,14 @@ import ( "github.com/zeta-chain/zetacore/pkg/chains" observertypes "github.com/zeta-chain/zetacore/x/observer/types" - "github.com/zeta-chain/zetacore/zetaclient/context" + 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( - appContext *context.AppContext, + ctx context.Context, logger zerolog.Logger, client *zetacore.Client, peers p2p.AddrList, @@ -31,20 +32,27 @@ func GenerateTss( ts *metrics.TelemetryServer, tssHistoricalList []observertypes.TSS, tssPassword string, - hotkeyPassword string) (*mc.TSS, error) { + hotkeyPassword string, +) (*mc.TSS, error) { + app, err := zctx.FromContext(ctx) + if err != nil { + return nil, err + } + 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 := appContext.GetBTCChainAndConfig() + btcChain, _, btcEnabled := app.GetBTCChainAndConfig() if btcEnabled { bitcoinChainID = btcChain.ChainId } tss, err := mc.NewTSS( - appContext, + ctx, + app, peers, priKey, preParams, @@ -74,7 +82,7 @@ func GenerateTss( // 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 := appContext.GetKeygen() + keyGen := app.GetKeygen() if keyGen.Status == observertypes.KeygenStatus_KeyGenSuccess { return tss, nil } @@ -86,7 +94,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() + currentBlock, err := client.GetBlockHeight(ctx) if err != nil { keygenLogger.Error().Err(err).Msg("GetBlockHeight RPC error") continue @@ -101,16 +109,21 @@ func GenerateTss( if currentBlock > lastBlock { lastBlock = currentBlock keygenLogger.Info(). - Msgf("Waiting For Keygen Block to arrive or new keygen block to be set. Keygen Block : %d Current Block : %d ChainID %s ", keyGen.BlockNumber, currentBlock, appContext.Config().ChainID) + Msgf("Waiting For Keygen Block to arrive or new keygen block to be set. Keygen Block : %d Current Block : %d ChainID %s ", keyGen.BlockNumber, currentBlock, app.Config().ChainID) } continue } // Try keygen only once at a particular block, irrespective of whether it is successful or failure triedKeygenAtBlock = true - err = keygenTss(keyGen, tss, keygenLogger) + err = keygenTss(ctx, keyGen, tss, keygenLogger) if err != nil { keygenLogger.Error().Err(err).Msg("keygenTss error") - tssFailedVoteHash, err := client.SetTSS("", keyGen.BlockNumber, chains.ReceiveStatus_failed) + tssFailedVoteHash, err := client.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 @@ -128,7 +141,8 @@ func GenerateTss( } // If TSS is successful , broadcast the vote to zetacore and set Pubkey - tssSuccessVoteHash, err := client.SetTSS( + tssSuccessVoteHash, err := client.PostVoteTSS( + ctx, newTss.CurrentPubkey, keyGen.BlockNumber, chains.ReceiveStatus_success, @@ -155,7 +169,7 @@ func GenerateTss( return nil, errors.New("unexpected state for TSS generation") } -func keygenTss(keyGen observertypes.Keygen, tss *mc.TSS, keygenLogger zerolog.Logger) error { +func keygenTss(ctx context.Context, keyGen observertypes.Keygen, tss *mc.TSS, keygenLogger zerolog.Logger) 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") @@ -168,7 +182,12 @@ func keygenTss(keyGen observertypes.Keygen, tss *mc.TSS, keygenLogger zerolog.Lo return err } index := fmt.Sprintf("keygen-%s-%d", digest, keyGen.BlockNumber) - zetaHash, err := tss.ZetacoreClient.PostBlameData(&res.Blame, tss.ZetacoreClient.Chain().ChainId, index) + zetaHash, err := tss.ZetacoreClient.PostVoteBlameData( + ctx, + &res.Blame, + tss.ZetacoreClient.Chain().ChainId, + index, + ) if err != nil { keygenLogger.Error().Err(err).Msg("error sending blame data to core") return err diff --git a/cmd/zetaclientd/start.go b/cmd/zetaclientd/start.go index 3546cec8ad..4dfbeadf3e 100644 --- a/cmd/zetaclientd/start.go +++ b/cmd/zetaclientd/start.go @@ -2,6 +2,7 @@ package main import ( "bufio" + "context" "encoding/json" "fmt" "io" @@ -26,7 +27,7 @@ import ( observerTypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/chains/base" "github.com/zeta-chain/zetacore/zetaclient/config" - "github.com/zeta-chain/zetacore/zetaclient/context" + zctx "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/zetaclient/metrics" "github.com/zeta-chain/zetacore/zetaclient/orchestrator" ) @@ -44,8 +45,7 @@ func init() { } func start(_ *cobra.Command, _ []string) error { - err := setHomeDir() - if err != nil { + if err := setHomeDir(); err != nil { return err } @@ -62,24 +62,25 @@ func start(_ *cobra.Command, _ []string) error { if err != nil { return err } + logger, err := base.InitLogger(cfg) if err != nil { - log.Error().Err(err).Msg("InitLogger failed") - return err + return errors.Wrap(err, "initLogger failed") } - //Wait until zetacore has started + // Wait until zetacore has started if len(cfg.Peer) != 0 { - err := validatePeer(cfg.Peer) - if err != nil { - log.Error().Err(err).Msg("invalid peer") - return err + if err := validatePeer(cfg.Peer); err != nil { + return errors.Wrap(err, "unable to validate peer") } } masterLogger := logger.Std startLogger := masterLogger.With().Str("module", "startup").Logger() + appContext := zctx.New(cfg, masterLogger) + ctx := zctx.WithAppContext(context.Background(), appContext) + // Wait until zetacore is up waitForZetaCore(cfg, startLogger) startLogger.Info().Msgf("Zetacore is ready, trying to connect to %s", cfg.Peer) @@ -95,15 +96,14 @@ func start(_ *cobra.Command, _ []string) error { // CreateZetacoreClient: zetacore client is used for all communication to zetacore , which this client connects to. // Zetacore accumulates votes , and provides a centralized source of truth for all clients - zetacoreClient, err := CreateZetacoreClient(cfg, telemetryServer, hotkeyPass) + zetacoreClient, err := CreateZetacoreClient(cfg, hotkeyPass, masterLogger) if err != nil { startLogger.Error().Err(err).Msg("CreateZetacoreClient error") return err } // Wait until zetacore is ready to create blocks - err = zetacoreClient.WaitForZetacoreToCreateBlocks() - if err != nil { + if err = zetacoreClient.WaitForZetacoreToCreateBlocks(ctx); err != nil { startLogger.Error().Err(err).Msg("WaitForZetacoreToCreateBlocks error") return err } @@ -117,7 +117,7 @@ func start(_ *cobra.Command, _ []string) error { } // cross-check chainid - res, err := zetacoreClient.GetNodeInfo() + res, err := zetacoreClient.GetNodeInfo(ctx) if err != nil { startLogger.Error().Err(err).Msg("GetNodeInfo error") return err @@ -144,15 +144,14 @@ func start(_ *cobra.Command, _ []string) error { startLogger.Debug().Msgf("CreateAuthzSigner is ready") // Initialize core parameters from zetacore - appContext := context.New(cfg, masterLogger) - err = zetacoreClient.UpdateZetacoreContext(appContext, true, startLogger) + err = zetacoreClient.UpdateZetacoreContext(ctx, appContext, true, startLogger) if err != nil { startLogger.Error().Err(err).Msg("Error getting core parameters") return err } startLogger.Info().Msgf("Config is updated from zetacore %s", maskCfg(cfg)) - go zetacoreClient.ZetacoreContextUpdater(appContext) + go zetacoreClient.UpdateZetacoreContextWorker(ctx, appContext) // Generate TSS address . The Tss address is generated through Keygen ceremony. The TSS key is used to sign all outbound transactions . // The hotkeyPk is private key for the Hotkey. The Hotkey is used to sign all inbound transactions @@ -195,14 +194,14 @@ func start(_ *cobra.Command, _ []string) error { metrics.LastStartTime.SetToCurrentTime() var tssHistoricalList []observerTypes.TSS - tssHistoricalList, err = zetacoreClient.GetTssHistory() + tssHistoricalList, err = zetacoreClient.GetTSSHistory(ctx) if err != nil { startLogger.Error().Err(err).Msg("GetTssHistory error") } telemetryServer.SetIPAddress(cfg.PublicIP) tss, err := GenerateTss( - appContext, + ctx, masterLogger, zetacoreClient, peers, @@ -237,7 +236,7 @@ func start(_ *cobra.Command, _ []string) error { // Update Current TSS value from zetacore, if TSS keygen is successful, the TSS address is set on zeta-core // Returns err if the RPC call fails as zeta client needs the current TSS address to be set // This is only needed in case of a new Keygen , as the TSS address is set on zetacore only after the keygen is successful i.e enough votes have been broadcast - currentTss, err := zetacoreClient.GetCurrentTss() + currentTss, err := zetacoreClient.GetCurrentTSS(ctx) if err != nil { startLogger.Error().Err(err).Msg("GetCurrentTSS error") return err @@ -254,7 +253,7 @@ func start(_ *cobra.Command, _ []string) error { startLogger.Error().Msgf("No chains enabled in updated config %s ", cfg.String()) } - observerList, err := zetacoreClient.GetObserverList() + observerList, err := zetacoreClient.GetObserverList(ctx) if err != nil { startLogger.Error().Err(err).Msg("GetObserverList error") return err @@ -268,7 +267,7 @@ func start(_ *cobra.Command, _ []string) error { } // CreateSignerMap: This creates a map of all signers for each chain . Each signer is responsible for signing transactions for a particular chain - signerMap, err := CreateSignerMap(appContext, tss, logger, telemetryServer) + signerMap, err := CreateSignerMap(ctx, appContext, tss, logger, telemetryServer) if err != nil { log.Error().Err(err).Msg("CreateSignerMap") return err @@ -282,7 +281,7 @@ func start(_ *cobra.Command, _ []string) error { dbpath := filepath.Join(userDir, ".zetaclient/chainobserver") // Creates a map of all chain observers for each chain. Each chain observer is responsible for observing events on the chain and processing them. - observerMap, err := CreateChainObserverMap(appContext, zetacoreClient, tss, dbpath, logger, telemetryServer) + observerMap, err := CreateChainObserverMap(ctx, appContext, zetacoreClient, tss, dbpath, logger, telemetryServer) if err != nil { startLogger.Err(err).Msg("CreateChainObserverMap") return err @@ -294,13 +293,20 @@ func start(_ *cobra.Command, _ []string) error { } else { startLogger.Debug().Msgf("Node %s is an active observer starting external chain observers", zetacoreClient.GetKeys().GetOperatorAddress().String()) for _, observer := range observerMap { - observer.Start() + observer.Start(ctx) } } // Orchestrator wraps the zetacore client and adds the observers and signer maps to it . This is the high level object used for CCTX interactions - orchestrator := orchestrator.NewOrchestrator(zetacoreClient, signerMap, observerMap, masterLogger, telemetryServer) - err = orchestrator.MonitorCore(appContext) + orchestrator := orchestrator.NewOrchestrator( + ctx, + zetacoreClient, + signerMap, + observerMap, + masterLogger, + telemetryServer, + ) + err = orchestrator.MonitorCore(ctx) if err != nil { startLogger.Error().Err(err).Msg("Orchestrator failed to start") return err diff --git a/cmd/zetaclientd/utils.go b/cmd/zetaclientd/utils.go index 99f6e03e59..01021d4e74 100644 --- a/cmd/zetaclientd/utils.go +++ b/cmd/zetaclientd/utils.go @@ -1,11 +1,13 @@ package main import ( + gocontext "context" "fmt" sdk "github.com/cosmos/cosmos-sdk/types" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" + "github.com/rs/zerolog" "github.com/zeta-chain/zetacore/zetaclient/authz" "github.com/zeta-chain/zetacore/zetaclient/chains/base" @@ -26,11 +28,7 @@ func CreateAuthzSigner(granter string, grantee sdk.AccAddress) { authz.SetupAuthZSignerList(granter, grantee) } -func CreateZetacoreClient( - cfg config.Config, - telemetry *metrics.TelemetryServer, - hotkeyPassword string, -) (*zetacore.Client, error) { +func CreateZetacoreClient(cfg config.Config, hotkeyPassword string, logger zerolog.Logger) (*zetacore.Client, error) { hotKey := cfg.AuthzHotkey if cfg.HsmMode { hotKey = cfg.HsmHotKey @@ -50,7 +48,7 @@ func CreateZetacoreClient( k := keys.NewKeysWithKeybase(kb, granterAddreess, cfg.AuthzHotkey, hotkeyPassword) - client, err := zetacore.NewClient(k, chainIP, hotKey, cfg.ChainID, cfg.HsmMode, telemetry) + client, err := zetacore.NewClient(k, chainIP, hotKey, cfg.ChainID, cfg.HsmMode, logger) if err != nil { return nil, err } @@ -60,6 +58,7 @@ func CreateZetacoreClient( // CreateSignerMap creates a map of ChainSigners for all chains in the config func CreateSignerMap( + ctx gocontext.Context, appContext *context.AppContext, tss interfaces.TSSSigner, logger base.Logger, @@ -77,11 +76,14 @@ func CreateSignerMap( logger.Std.Error().Msgf("ChainParam not found for chain %s", evmConfig.Chain.String()) continue } + + chainName := evmConfig.Chain.ChainName.String() mpiAddress := ethcommon.HexToAddress(evmChainParams.ConnectorContractAddress) erc20CustodyAddress := ethcommon.HexToAddress(evmChainParams.Erc20CustodyContractAddress) + signer, err := evmsigner.NewSigner( + ctx, evmConfig.Chain, - appContext, tss, ts, logger, @@ -89,21 +91,28 @@ func CreateSignerMap( config.GetConnectorABI(), config.GetERC20CustodyABI(), mpiAddress, - erc20CustodyAddress) + erc20CustodyAddress, + ) if err != nil { - logger.Std.Error().Err(err).Msgf("NewEVMSigner error for chain %s", evmConfig.Chain.String()) + logger.Std.Error().Err(err).Msgf("NewSigner error for EVM chain %q", chainName) continue } + signerMap[evmConfig.Chain.ChainId] = signer + logger.Std.Info().Msgf("NewSigner succeeded for EVM chain %q", chainName) } + // BTC signer - btcChain, btcConfig, enabled := appContext.GetBTCChainAndConfig() - if enabled { - signer, err := btcsigner.NewSigner(btcChain, appContext, tss, ts, logger, btcConfig) + btcChain, btcConfig, btcEnabled := appContext.GetBTCChainAndConfig() + if btcEnabled { + chainName := btcChain.ChainName.String() + + signer, err := btcsigner.NewSigner(btcChain, tss, ts, logger, btcConfig) if err != nil { - logger.Std.Error().Err(err).Msgf("NewBTCSigner error for chain %s", btcChain.String()) + logger.Std.Error().Err(err).Msgf("NewSigner error for BTC chain %q", chainName) } else { signerMap[btcChain.ChainId] = signer + logger.Std.Info().Msgf("NewSigner succeeded for BTC chain %q", chainName) } } @@ -112,6 +121,7 @@ func CreateSignerMap( // CreateChainObserverMap creates a map of ChainObservers for all chains in the config func CreateChainObserverMap( + ctx gocontext.Context, appContext *context.AppContext, zetacoreClient *zetacore.Client, tss interfaces.TSSSigner, @@ -134,16 +144,16 @@ func CreateChainObserverMap( // create EVM client evmClient, err := ethclient.Dial(evmConfig.Endpoint) if err != nil { - logger.Std.Error().Err(err).Msgf("error dailing endpoint %s", evmConfig.Endpoint) + logger.Std.Error().Err(err).Msgf("error dailing endpoint %q", evmConfig.Endpoint) continue } // create EVM chain observer observer, err := evmobserver.NewObserver( + ctx, evmConfig, evmClient, *chainParams, - appContext, zetacoreClient, tss, dbpath, @@ -175,7 +185,6 @@ func CreateChainObserverMap( btcChain, btcClient, *chainParams, - appContext, zetacoreClient, tss, dbpath, diff --git a/cmd/zetae2e/config/localnet.yml b/cmd/zetae2e/config/localnet.yml index abb780db39..114f1c4f32 100644 --- a/cmd/zetae2e/config/localnet.yml +++ b/cmd/zetae2e/config/localnet.yml @@ -32,10 +32,19 @@ additional_accounts: bech32_address: "zeta17w0adeg64ky0daxwd2ugyuneellmjgnx4e483s" evm_address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" private_key: "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" - user_fungible_admin: - bech32_address: "zeta1svzuz982w09vf2y08xsh8qplj36phyz466krj3" - evm_address: "0x8305C114Ea73cAc4A88f39A173803F94741b9055" - private_key: "d88d09a7d6849c15a36eb6931f9dd616091a63e9849a2cc86f309ba11fb8fec5" +policy_accounts: + emergency_policy_account: + bech32_address: "zeta16m2cnrdwtgweq4njc6t470vl325gw4kp6s7tap" + evm_address: "0xd6d5898dAE5A1D905672c6975F3d9f8aA88756C1" + private_key: "88BE93D11624B794F4BCC77BEA7385AF7EAD0B183B913485C74F0A803ABBC3F0" + operational_policy_account: + bech32_address: "zeta1pgx85vzx4fzh5zzyjqgs6a6cmaujd0xs8efrkc" + evm_address: "0x0A0c7a3046AA457A084490110d7758Df7926bcd0" + private_key: "59D1B982BD446545A1740ABD01F1ED9C162B72ACC1522B9B71B6DB5A9C37FA7D" + admin_policy_account: + bech32_address: "zeta142ds9x7raljv2qz9euys93e64gjmgdfnc47dwq" + evm_address: "0xAa9b029BC3EFe4c50045Cf0902c73aAa25b43533" + private_key: "0595CB0CD9BF5264A85A603EC8E43C30ADBB5FD2D9E2EF84C374EA4A65BB616C" rpcs: zevm: "http://zetacore0:8545" evm: "http://eth:8545" diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index 7e00f9ba5b..f282cd9f07 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -126,8 +126,12 @@ func localE2ETest(cmd *cobra.Command, _ []string) { zetaTxServer, err := txserver.NewZetaTxServer( conf.RPCs.ZetaCoreRPC, - []string{utils.FungibleAdminName}, - []string{conf.AdditionalAccounts.UserFungibleAdmin.RawPrivateKey.String()}, + []string{utils.EmergencyPolicyName, utils.OperationalPolicyName, utils.AdminPolicyName}, + []string{ + conf.PolicyAccounts.EmergencyPolicyAccount.RawPrivateKey.String(), + conf.PolicyAccounts.OperationalPolicyAccount.RawPrivateKey.String(), + conf.PolicyAccounts.AdminPolicyAccount.RawPrivateKey.String(), + }, conf.ZetaChainID, ) noError(err) diff --git a/cmd/zetae2e/stress.go b/cmd/zetae2e/stress.go index 30b2faf924..51e4762635 100644 --- a/cmd/zetae2e/stress.go +++ b/cmd/zetae2e/stress.go @@ -170,10 +170,10 @@ func StressTest(cmd *cobra.Command, _ []string) { // Get current nonce on zevm for DeployerAddress - Need to keep track of nonce at client level blockNum := must(e2eTest.ZEVMClient.BlockNumber(ctx)) - // #nosec G701 e2eTest - always in range + // #nosec G115 e2eTest - always in range nonce := must(e2eTest.ZEVMClient.NonceAt(ctx, deployerAccount.EVMAddress(), big.NewInt(int64(blockNum)))) - // #nosec G701 e2e - always in range + // #nosec G115 e2e - always in range zevmNonce = big.NewInt(int64(nonce)) // -------------- TEST BEGINS ------------------ diff --git a/cmd/zetatool/filterdeposit/btc.go b/cmd/zetatool/filterdeposit/btc.go index c8d8ddd11a..a667a7d3eb 100644 --- a/cmd/zetatool/filterdeposit/btc.go +++ b/cmd/zetatool/filterdeposit/btc.go @@ -174,7 +174,7 @@ func getHashList(cfg *config.Config, tssAddress string) ([]Deposit, error) { if strings.Compare("0014", scriptpubkey[:4]) == 0 && targetAddr == tssAddress { entry := Deposit{ hash, - // #nosec G701 parsing json requires float64 type from blockstream + // #nosec G115 parsing json requires float64 type from blockstream uint64(vout0["value"].(float64)), } list = append(list, entry) diff --git a/cmd/zetatool/filterdeposit/evm.go b/cmd/zetatool/filterdeposit/evm.go index f2cea39b99..427e0c421c 100644 --- a/cmd/zetatool/filterdeposit/evm.go +++ b/cmd/zetatool/filterdeposit/evm.go @@ -188,9 +188,9 @@ func getTSSDeposits(tssAddress string, startBlock uint64, endBlock uint64, apiKe client := etherscan.New(etherscan.Mainnet, apiKey) deposits := make([]Deposit, 0) - // #nosec G701 these block numbers need to be *int for this particular client package + // #nosec G115 these block numbers need to be *int for this particular client package startInt := int(startBlock) - // #nosec G701 + // #nosec G115 endInt := int(endBlock) txns, err := client.NormalTxByAddress(tssAddress, &startInt, &endInt, 0, 0, true) if err != nil { diff --git a/contrib/localnet/scripts/start-zetacored.sh b/contrib/localnet/scripts/start-zetacored.sh index 64779d2a39..c3e53c6c93 100755 --- a/contrib/localnet/scripts/start-zetacored.sh +++ b/contrib/localnet/scripts/start-zetacored.sh @@ -219,12 +219,20 @@ then # set admin account zetacored add-genesis-account zeta1n0rn6sne54hv7w2uu93fl48ncyqz97d3kty6sh 100000000000000000000000000azeta # Funds the localnet_gov_admin account - - address=$(yq -r '.additional_accounts.user_fungible_admin.bech32_address' /root/config.yml) + + emergency_policy=$(yq -r '.policy_accounts.emergency_policy_account.bech32_address' /root/config.yml) + admin_policy=$(yq -r '.policy_accounts.admin_policy_account.bech32_address' /root/config.yml) + operational_policy=$(yq -r '.policy_accounts.operational_policy_account.bech32_address' /root/config.yml) + + zetacored add-genesis-account "$address" 100000000000000000000000000azeta - cat $HOME/.zetacored/config/genesis.json | jq --arg address "$address" '.app_state["authority"]["policies"]["items"][0]["address"] = $address' > $HOME/.zetacored/config/tmp_genesis.json && mv $HOME/.zetacored/config/tmp_genesis.json $HOME/.zetacored/config/genesis.json - cat $HOME/.zetacored/config/genesis.json | jq --arg address "$address" '.app_state["authority"]["policies"]["items"][1]["address"] = $address' > $HOME/.zetacored/config/tmp_genesis.json && mv $HOME/.zetacored/config/tmp_genesis.json $HOME/.zetacored/config/genesis.json - cat $HOME/.zetacored/config/genesis.json | jq --arg address "$address" '.app_state["authority"]["policies"]["items"][2]["address"] = $address' > $HOME/.zetacored/config/tmp_genesis.json && mv $HOME/.zetacored/config/tmp_genesis.json $HOME/.zetacored/config/genesis.json + zetacored add-genesis-account "$emergency_policy" 100000000000000000000000000azeta + zetacored add-genesis-account "$admin_policy" 100000000000000000000000000azeta + zetacored add-genesis-account "$operational_policy" 100000000000000000000000000azeta + + cat $HOME/.zetacored/config/genesis.json | jq --arg address "$emergency_policy" '.app_state["authority"]["policies"]["items"][0]["address"] = $address' > $HOME/.zetacored/config/tmp_genesis.json && mv $HOME/.zetacored/config/tmp_genesis.json $HOME/.zetacored/config/genesis.json + cat $HOME/.zetacored/config/genesis.json | jq --arg address "$operational_policy" '.app_state["authority"]["policies"]["items"][1]["address"] = $address' > $HOME/.zetacored/config/tmp_genesis.json && mv $HOME/.zetacored/config/tmp_genesis.json $HOME/.zetacored/config/genesis.json + cat $HOME/.zetacored/config/genesis.json | jq --arg address "$admin_policy" '.app_state["authority"]["policies"]["items"][2]["address"] = $address' > $HOME/.zetacored/config/tmp_genesis.json && mv $HOME/.zetacored/config/tmp_genesis.json $HOME/.zetacored/config/genesis.json # give balance to runner accounts to deploy contracts directly on zEVM # default account @@ -242,6 +250,15 @@ then # ethers tester address=$(yq -r '.additional_accounts.user_ether.bech32_address' /root/config.yml) zetacored add-genesis-account "$address" 100000000000000000000000000azeta +# emergency policy account + address=$(yq -r '.policy_accounts.emergency_policy_account.bech32_address' /root/config.yml) + zetacored add-genesis-account "$address" 100000000000000000000000000azeta +# admin policy account + address=$(yq -r '.policy_accounts.admin_policy_account.bech32_address' /root/config.yml) + zetacored add-genesis-account "$address" 100000000000000000000000000azeta +# operational policy account + address=$(yq -r '.policy_accounts.operational_policy_account.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/contrib/rpctest/main.go b/contrib/rpctest/main.go index a9cc1fb405..227a2c9bc0 100644 --- a/contrib/rpctest/main.go +++ b/contrib/rpctest/main.go @@ -58,7 +58,7 @@ func main() { if bn < 0 { panic("Block number must be non-negative") } - // #nosec G701 check as positive + // #nosec G115 check as positive bnUint64 := uint64(bn) if false { diff --git a/e2e/config/config.go b/e2e/config/config.go index 66409ee339..6cde9bac26 100644 --- a/e2e/config/config.go +++ b/e2e/config/config.go @@ -43,6 +43,7 @@ type Config struct { // Default account to use when running tests and running setup DefaultAccount Account `yaml:"default_account"` AdditionalAccounts AdditionalAccounts `yaml:"additional_accounts"` + PolicyAccounts PolicyAccounts `yaml:"policy_accounts"` RPCs RPCs `yaml:"rpcs"` Contracts Contracts `yaml:"contracts"` ZetaChainID string `yaml:"zeta_chain_id"` @@ -57,14 +58,19 @@ type Account struct { // AdditionalAccounts are extra accounts required to run specific tests type AdditionalAccounts struct { - UserERC20 Account `yaml:"user_erc20"` - UserZetaTest Account `yaml:"user_zeta_test"` - UserZEVMMPTest Account `yaml:"user_zevm_mp_test"` - UserBitcoin Account `yaml:"user_bitcoin"` - UserEther Account `yaml:"user_ether"` - UserMisc Account `yaml:"user_misc"` - UserAdmin Account `yaml:"user_admin"` - UserFungibleAdmin Account `yaml:"user_fungible_admin"` + UserERC20 Account `yaml:"user_erc20"` + UserZetaTest Account `yaml:"user_zeta_test"` + UserZEVMMPTest Account `yaml:"user_zevm_mp_test"` + UserBitcoin Account `yaml:"user_bitcoin"` + UserEther Account `yaml:"user_ether"` + UserMisc Account `yaml:"user_misc"` + UserAdmin Account `yaml:"user_admin"` +} + +type PolicyAccounts struct { + EmergencyPolicyAccount Account `yaml:"emergency_policy_account"` + OperationalPolicyAccount Account `yaml:"operational_policy_account"` + AdminPolicyAccount Account `yaml:"admin_policy_account"` } // RPCs contains the configuration for the RPC endpoints @@ -192,7 +198,14 @@ func (a AdditionalAccounts) AsSlice() []Account { a.UserEther, a.UserMisc, a.UserAdmin, - a.UserFungibleAdmin, + } +} + +func (a PolicyAccounts) AsSlice() []Account { + return []Account{ + a.EmergencyPolicyAccount, + a.OperationalPolicyAccount, + a.AdminPolicyAccount, } } @@ -216,9 +229,21 @@ func (c Config) Validate() error { } err := account.Validate() if err != nil { - return fmt.Errorf("validating account %d: %w", i, err) + return fmt.Errorf("validating additional account %d: %w", i, err) } } + + policyAccounts := c.PolicyAccounts.AsSlice() + for i, account := range policyAccounts { + if account.RawEVMAddress == "" { + continue + } + err := account.Validate() + if err != nil { + return fmt.Errorf("validating policy account %d: %w", i, err) + } + } + return nil } @@ -257,7 +282,15 @@ func (c *Config) GenerateKeys() error { if err != nil { return err } - c.AdditionalAccounts.UserFungibleAdmin, err = generateAccount() + c.PolicyAccounts.EmergencyPolicyAccount, err = generateAccount() + if err != nil { + return err + } + c.PolicyAccounts.OperationalPolicyAccount, err = generateAccount() + if err != nil { + return err + } + c.PolicyAccounts.AdminPolicyAccount, err = generateAccount() if err != nil { return err } diff --git a/e2e/e2etests/test_erc20_deposit_refund.go b/e2e/e2etests/test_erc20_deposit_refund.go index 5e8d1173ef..a47100ef3f 100644 --- a/e2e/e2etests/test_erc20_deposit_refund.go +++ b/e2e/e2etests/test_erc20_deposit_refund.go @@ -36,9 +36,13 @@ func TestERC20DepositAndCallRefund(r *runner.E2ERunner, _ []string) { r.Logger.CCTX(*cctx, "deposit") r.Logger.Info("Refunding the cctx via admin") - msg := types.NewMsgRefundAbortedCCTX(r.ZetaTxServer.GetAccountAddress(0), cctx.Index, r.EVMAddress().String()) + msg := types.NewMsgRefundAbortedCCTX( + r.ZetaTxServer.MustGetAccountAddressFromName(utils.OperationalPolicyName), + cctx.Index, + r.EVMAddress().String(), + ) - _, err = r.ZetaTxServer.BroadcastTx(utils.FungibleAdminName, msg) + _, err = r.ZetaTxServer.BroadcastTx(utils.OperationalPolicyName, msg) require.NoError(r, err) // Check that the erc20 in the aborted cctx was refunded on ZetaChain diff --git a/e2e/e2etests/test_eth_deposit_liquidity_cap.go b/e2e/e2etests/test_eth_deposit_liquidity_cap.go index 2f03c7a568..7e1ea55a70 100644 --- a/e2e/e2etests/test_eth_deposit_liquidity_cap.go +++ b/e2e/e2etests/test_eth_deposit_liquidity_cap.go @@ -25,11 +25,11 @@ func TestDepositEtherLiquidityCap(r *runner.E2ERunner, args []string) { amountLessThanCap := liquidityCapArg.BigInt().Div(liquidityCapArg.BigInt(), big.NewInt(10)) // 1/10 of the cap amountMoreThanCap := liquidityCapArg.BigInt().Mul(liquidityCapArg.BigInt(), big.NewInt(10)) // 10 times the cap msg := fungibletypes.NewMsgUpdateZRC20LiquidityCap( - r.ZetaTxServer.GetAccountAddress(0), + r.ZetaTxServer.MustGetAccountAddressFromName(utils.OperationalPolicyName), r.ETHZRC20Addr.Hex(), liquidityCap, ) - res, err := r.ZetaTxServer.BroadcastTx(utils.FungibleAdminName, msg) + res, err := r.ZetaTxServer.BroadcastTx(utils.OperationalPolicyName, msg) require.NoError(r, err) r.Logger.Info("set liquidity cap tx hash: %s", res.TxHash) @@ -69,12 +69,12 @@ func TestDepositEtherLiquidityCap(r *runner.E2ERunner, args []string) { r.Logger.Info("Removing the liquidity cap") msg = fungibletypes.NewMsgUpdateZRC20LiquidityCap( - r.ZetaTxServer.GetAccountAddress(0), + r.ZetaTxServer.MustGetAccountAddressFromName(utils.OperationalPolicyName), r.ETHZRC20Addr.Hex(), math.ZeroUint(), ) - res, err = r.ZetaTxServer.BroadcastTx(utils.FungibleAdminName, msg) + res, err = r.ZetaTxServer.BroadcastTx(utils.OperationalPolicyName, msg) require.NoError(r, err) r.Logger.Info("remove liquidity cap tx hash: %s", res.TxHash) diff --git a/e2e/e2etests/test_migrate_chain_support.go b/e2e/e2etests/test_migrate_chain_support.go index 07f49a5815..f0fa69f6e0 100644 --- a/e2e/e2etests/test_migrate_chain_support.go +++ b/e2e/e2etests/test_migrate_chain_support.go @@ -58,27 +58,28 @@ func TestMigrateChainSupport(r *runner.E2ERunner, _ []string) { // update the chain params to set up the chain chainParams := getNewEVMChainParams(newRunner) - adminAddr, err := newRunner.ZetaTxServer.GetAccountAddressFromName(utils.FungibleAdminName) - require.NoError(r, err) - _, err = newRunner.ZetaTxServer.BroadcastTx(utils.FungibleAdminName, observertypes.NewMsgUpdateChainParams( - adminAddr, + _, err = newRunner.ZetaTxServer.BroadcastTx(utils.OperationalPolicyName, observertypes.NewMsgUpdateChainParams( + r.ZetaTxServer.MustGetAccountAddressFromName(utils.OperationalPolicyName), chainParams, )) require.NoError(r, err) // setup the gas token require.NoError(r, err) - _, err = newRunner.ZetaTxServer.BroadcastTx(utils.FungibleAdminName, fungibletypes.NewMsgDeployFungibleCoinZRC20( - adminAddr, - "", - chainParams.ChainId, - 18, - "Sepolia ETH", - "sETH", - coin.CoinType_Gas, - 100000, - )) + _, err = newRunner.ZetaTxServer.BroadcastTx( + utils.OperationalPolicyName, + fungibletypes.NewMsgDeployFungibleCoinZRC20( + r.ZetaTxServer.MustGetAccountAddressFromName(utils.OperationalPolicyName), + "", + chainParams.ChainId, + 18, + "Sepolia ETH", + "sETH", + coin.CoinType_Gas, + 100000, + ), + ) require.NoError(r, err) // set the gas token in the runner @@ -95,8 +96,8 @@ func TestMigrateChainSupport(r *runner.E2ERunner, _ []string) { newRunner.ETHZRC20 = ethZRC20 // set the chain nonces for the new chain - _, err = r.ZetaTxServer.BroadcastTx(utils.FungibleAdminName, observertypes.NewMsgResetChainNonces( - adminAddr, + _, err = r.ZetaTxServer.BroadcastTx(utils.OperationalPolicyName, observertypes.NewMsgResetChainNonces( + r.ZetaTxServer.MustGetAccountAddressFromName(utils.OperationalPolicyName), chainParams.ChainId, 0, 0, @@ -106,8 +107,8 @@ func TestMigrateChainSupport(r *runner.E2ERunner, _ []string) { // deactivate the previous chain chainParams = observertypes.GetDefaultGoerliLocalnetChainParams() chainParams.IsSupported = false - _, err = newRunner.ZetaTxServer.BroadcastTx(utils.FungibleAdminName, observertypes.NewMsgUpdateChainParams( - adminAddr, + _, err = newRunner.ZetaTxServer.BroadcastTx(utils.OperationalPolicyName, observertypes.NewMsgUpdateChainParams( + r.ZetaTxServer.MustGetAccountAddressFromName(utils.OperationalPolicyName), chainParams, )) require.NoError(r, err) @@ -155,8 +156,8 @@ func TestMigrateChainSupport(r *runner.E2ERunner, _ []string) { // whitelist erc20 zrc20 newRunner.Logger.Info("whitelisting ERC20 on new network") - res, err := newRunner.ZetaTxServer.BroadcastTx(utils.FungibleAdminName, crosschaintypes.NewMsgWhitelistERC20( - adminAddr, + res, err := newRunner.ZetaTxServer.BroadcastTx(utils.OperationalPolicyName, crosschaintypes.NewMsgWhitelistERC20( + r.ZetaTxServer.MustGetAccountAddressFromName(utils.OperationalPolicyName), newRunner.ERC20Addr.Hex(), chains.Sepolia.ChainId, "USDT", diff --git a/e2e/e2etests/test_pause_zrc20.go b/e2e/e2etests/test_pause_zrc20.go index 1495a7ac60..c1924d85cf 100644 --- a/e2e/e2etests/test_pause_zrc20.go +++ b/e2e/e2etests/test_pause_zrc20.go @@ -35,10 +35,10 @@ func TestPauseZRC20(r *runner.E2ERunner, _ []string) { // Pause ETH ZRC20 r.Logger.Info("Pausing ETH") msgPause := fungibletypes.NewMsgPauseZRC20( - r.ZetaTxServer.GetAccountAddress(0), + r.ZetaTxServer.MustGetAccountAddressFromName(utils.EmergencyPolicyName), []string{r.ETHZRC20Addr.Hex()}, ) - res, err := r.ZetaTxServer.BroadcastTx(utils.FungibleAdminName, msgPause) + res, err := r.ZetaTxServer.BroadcastTx(utils.EmergencyPolicyName, msgPause) require.NoError(r, err) r.Logger.Info("pause zrc20 tx hash: %s", res.TxHash) @@ -106,10 +106,10 @@ func TestPauseZRC20(r *runner.E2ERunner, _ []string) { // Unpause ETH ZRC20 r.Logger.Info("Unpausing ETH") msgUnpause := fungibletypes.NewMsgUnpauseZRC20( - r.ZetaTxServer.GetAccountAddress(0), + r.ZetaTxServer.MustGetAccountAddressFromName(utils.OperationalPolicyName), []string{r.ETHZRC20Addr.Hex()}, ) - res, err = r.ZetaTxServer.BroadcastTx(utils.FungibleAdminName, msgUnpause) + res, err = r.ZetaTxServer.BroadcastTx(utils.OperationalPolicyName, msgUnpause) require.NoError(r, err) r.Logger.Info("unpause zrc20 tx hash: %s", res.TxHash) diff --git a/e2e/e2etests/test_rate_limiter.go b/e2e/e2etests/test_rate_limiter.go index 739d7821dc..d1eb666986 100644 --- a/e2e/e2etests/test_rate_limiter.go +++ b/e2e/e2etests/test_rate_limiter.go @@ -71,8 +71,8 @@ func TestRateLimiter(r *runner.E2ERunner, _ []string) { // https://github.com/zeta-chain/node/issues/2090 r.Logger.Print("rate limiter enabled") require.NoError(r, createAndWaitWithdraws(r, withdrawTypeZETA, zetaAmount)) - require.NoError(r, createAndWaitWithdraws(r, withdrawTypeZETA, ethAmount)) - require.NoError(r, createAndWaitWithdraws(r, withdrawTypeZETA, erc20Amount)) + require.NoError(r, createAndWaitWithdraws(r, withdrawTypeETH, ethAmount)) + require.NoError(r, createAndWaitWithdraws(r, withdrawTypeERC20, erc20Amount)) // Disable rate limiter r.Logger.Info("disabling rate limiter") @@ -167,11 +167,11 @@ func waitForWithdrawMined( // setupRateLimiterFlags sets up the rate limiter flags with flags defined in the test func setupRateLimiterFlags(r *runner.E2ERunner, flags crosschaintypes.RateLimiterFlags) error { - adminAddr, err := r.ZetaTxServer.GetAccountAddressFromName(utils.FungibleAdminName) + adminAddr, err := r.ZetaTxServer.GetAccountAddressFromName(utils.OperationalPolicyName) if err != nil { return err } - _, err = r.ZetaTxServer.BroadcastTx(utils.FungibleAdminName, crosschaintypes.NewMsgUpdateRateLimiterFlags( + _, err = r.ZetaTxServer.BroadcastTx(utils.OperationalPolicyName, crosschaintypes.NewMsgUpdateRateLimiterFlags( adminAddr, flags, )) diff --git a/e2e/e2etests/test_update_bytecode_connector.go b/e2e/e2etests/test_update_bytecode_connector.go index 73786f2971..c370cf6a33 100644 --- a/e2e/e2etests/test_update_bytecode_connector.go +++ b/e2e/e2etests/test_update_bytecode_connector.go @@ -46,11 +46,11 @@ func TestUpdateBytecodeConnector(r *runner.E2ERunner, _ []string) { r.Logger.Info("Updating the bytecode of the Connector") msg := fungibletypes.NewMsgUpdateContractBytecode( - r.ZetaTxServer.GetAccountAddress(0), + r.ZetaTxServer.MustGetAccountAddressFromName(utils.AdminPolicyName), r.ConnectorZEVMAddr.Hex(), codeHashRes.CodeHash, ) - res, err := r.ZetaTxServer.BroadcastTx(utils.FungibleAdminName, msg) + res, err := r.ZetaTxServer.BroadcastTx(utils.AdminPolicyName, msg) require.NoError(r, err) r.Logger.Info("Update connector bytecode tx hash: %s", res.TxHash) diff --git a/e2e/e2etests/test_update_bytecode_zrc20.go b/e2e/e2etests/test_update_bytecode_zrc20.go index 8205b88d9c..33f6c39dd6 100644 --- a/e2e/e2etests/test_update_bytecode_zrc20.go +++ b/e2e/e2etests/test_update_bytecode_zrc20.go @@ -30,7 +30,7 @@ func TestUpdateBytecodeZRC20(r *runner.E2ERunner, _ []string) { r.ZEVMAuth, r.ZEVMClient, big.NewInt(5), - // #nosec G701 test - always in range + // #nosec G115 test - always in range uint8(coin.CoinType_Gas), ) require.NoError(r, err) @@ -68,11 +68,11 @@ func TestUpdateBytecodeZRC20(r *runner.E2ERunner, _ []string) { r.Logger.Info("Updating the bytecode of the ZRC20") msg := fungibletypes.NewMsgUpdateContractBytecode( - r.ZetaTxServer.GetAccountAddress(0), + r.ZetaTxServer.MustGetAccountAddressFromName(utils.AdminPolicyName), r.ETHZRC20Addr.Hex(), codeHashRes.CodeHash, ) - res, err := r.ZetaTxServer.BroadcastTx(utils.FungibleAdminName, msg) + res, err := r.ZetaTxServer.BroadcastTx(utils.AdminPolicyName, msg) require.NoError(r, err) r.Logger.Info("Update zrc20 bytecode tx hash: %s", res.TxHash) diff --git a/e2e/runner/accounting.go b/e2e/runner/accounting.go index 4992d46ad8..d5a7e3a9e2 100644 --- a/e2e/runner/accounting.go +++ b/e2e/runner/accounting.go @@ -65,16 +65,16 @@ 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 G701 test - always in range + // #nosec G115 test - always in range if int64(btcBalance*1e8) < (zrc20Supply.Int64() - 10000000) { - // #nosec G701 test - always in range + // #nosec G115 test - always in range return fmt.Errorf( "BTC: TSS Balance (%d) < ZRC20 TotalSupply (%d)", int64(btcBalance*1e8), zrc20Supply.Int64()-10000000, ) } - // #nosec G701 test - always in range + // #nosec G115 test - always in range r.Logger.Info( "BTC: Balance (%d) >= ZRC20 TotalSupply (%d)", int64(btcBalance*1e8), diff --git a/e2e/runner/evm.go b/e2e/runner/evm.go index 88c1e100bf..5fd4bdd565 100644 --- a/e2e/runner/evm.go +++ b/e2e/runner/evm.go @@ -183,7 +183,7 @@ func (r *E2ERunner) ProveEthTransaction(receipt *ethtypes.Receipt) { txHash := receipt.TxHash blockHash := receipt.BlockHash - // #nosec G701 test - always in range + // #nosec G115 test - always in range txIndex := int(receipt.TransactionIndex) block, err := r.EVMClient.BlockByHash(r.Ctx, blockHash) diff --git a/e2e/runner/setup_zeta.go b/e2e/runner/setup_zeta.go index ef3acbd64b..859f83d3e4 100644 --- a/e2e/runner/setup_zeta.go +++ b/e2e/runner/setup_zeta.go @@ -75,7 +75,7 @@ func (r *E2ERunner) SetZEVMContracts() { // deploy system contracts and ZRC20 contracts on ZetaChain uniswapV2FactoryAddr, uniswapV2RouterAddr, zevmConnectorAddr, wzetaAddr, erc20zrc20Addr, err := r.ZetaTxServer.DeploySystemContractsAndZRC20( - e2eutils.FungibleAdminName, + e2eutils.OperationalPolicyName, r.ERC20Addr.Hex(), ) require.NoError(r, err) @@ -209,12 +209,12 @@ func (r *E2ERunner) SetupBTCZRC20() { func (r *E2ERunner) EnableHeaderVerification(chainIDList []int64) error { r.Logger.Print("⚙️ enabling verification flags for block headers") - return r.ZetaTxServer.EnableHeaderVerification(e2eutils.FungibleAdminName, chainIDList) + return r.ZetaTxServer.EnableHeaderVerification(e2eutils.OperationalPolicyName, chainIDList) } // FundEmissionsPool funds the emissions pool on ZetaChain with the same value as used originally on mainnet (20M ZETA) func (r *E2ERunner) FundEmissionsPool() error { r.Logger.Print("⚙️ funding the emissions pool on ZetaChain with 20M ZETA (%s)", txserver.EmissionsPoolAddress) - return r.ZetaTxServer.FundEmissionsPool(e2eutils.FungibleAdminName, EmissionsPoolFunding) + return r.ZetaTxServer.FundEmissionsPool(e2eutils.OperationalPolicyName, EmissionsPoolFunding) } diff --git a/e2e/txserver/zeta_tx_server.go b/e2e/txserver/zeta_tx_server.go index 5c4c5e80af..9b93aee293 100644 --- a/e2e/txserver/zeta_tx_server.go +++ b/e2e/txserver/zeta_tx_server.go @@ -153,6 +153,20 @@ func (zts ZetaTxServer) GetAccountAddressFromName(name string) (string, error) { return addr.String(), nil } +// MustGetAccountAddressFromName returns the account address from the given name.It panics on error +// and should be used in tests only +func (zts ZetaTxServer) MustGetAccountAddressFromName(name string) string { + acc, err := zts.clientCtx.Keyring.Key(name) + if err != nil { + panic(err) + } + addr, err := acc.GetAddress() + if err != nil { + panic(err) + } + return addr.String() +} + // GetAllAccountAddress returns all account addresses func (zts ZetaTxServer) GetAllAccountAddress() []string { return zts.address diff --git a/e2e/utils/zetacore.go b/e2e/utils/zetacore.go index cbba05de75..a10dc8d68b 100644 --- a/e2e/utils/zetacore.go +++ b/e2e/utils/zetacore.go @@ -16,7 +16,9 @@ import ( type CCTXClient = crosschaintypes.QueryClient const ( - FungibleAdminName = "fungibleadmin" + EmergencyPolicyName = "emergency" + AdminPolicyName = "admin" + OperationalPolicyName = "operational" DefaultCctxTimeout = 4 * time.Minute ) diff --git a/go.mod b/go.mod index 43584e6b4e..c730cd2222 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/zeta-chain/zetacore -go 1.20 +go 1.22 require ( github.com/cosmos/cosmos-sdk v0.47.10 @@ -39,7 +39,7 @@ require ( github.com/frumioj/crypto11 v1.2.5-0.20210823151709-946ce662cc0e github.com/pkg/errors v0.9.1 github.com/rakyll/statik v0.1.7 - github.com/zeta-chain/go-tss v0.1.1-0.20240430111318-1785e48eb127 + github.com/zeta-chain/go-tss v0.1.1-0.20240711225655-6ab1e42a0dee github.com/zeta-chain/keystone/keys v0.0.0-20231105174229-903bc9405da2 github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20240418181724-c222fd3ae1f5 google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 @@ -96,20 +96,14 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/gogo/googleapis v1.4.1 // indirect github.com/golang/glog v1.1.2 // indirect - github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 // indirect github.com/google/s2a-go v0.1.7 // indirect github.com/iancoleman/orderedmap v0.3.0 // indirect github.com/ipfs/boxo v0.10.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/libp2p/go-yamux/v4 v4.0.0 // indirect github.com/miekg/pkcs11 v1.1.1 // indirect - github.com/onsi/ginkgo/v2 v2.9.7 // indirect + github.com/onsi/gomega v1.27.7 // indirect github.com/prometheus/tsdb v0.7.1 // indirect - github.com/quic-go/qpack v0.4.0 // indirect - github.com/quic-go/qtls-go1-19 v0.3.3 // indirect - github.com/quic-go/qtls-go1-20 v0.2.3 // indirect - github.com/quic-go/quic-go v0.33.0 // indirect - github.com/quic-go/webtransport-go v0.5.3 // indirect github.com/rjeczalik/notify v0.9.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sergi/go-diff v1.3.1 // indirect @@ -157,7 +151,7 @@ require ( github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect - github.com/cenkalti/backoff/v4 v4.2.0 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chzyer/readline v1.5.1 // indirect github.com/cockroachdb/apd/v2 v2.0.2 // indirect @@ -186,7 +180,6 @@ require ( github.com/elastic/gosigar v0.14.2 // indirect github.com/felixge/httpsnoop v1.0.2 // indirect github.com/flynn/noise v1.0.0 // indirect - github.com/francoispqt/gojay v1.2.13 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect github.com/go-kit/kit v0.12.0 // indirect @@ -195,7 +188,6 @@ require ( github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/go-stack/stack v1.8.1 // indirect - github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -334,7 +326,7 @@ require ( google.golang.org/protobuf v1.32.0 gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect + gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.2.1 // indirect nhooyr.io/websocket v1.8.7 // indirect sigs.k8s.io/yaml v1.4.0 // indirect @@ -356,3 +348,5 @@ replace ( replace github.com/cometbft/cometbft-db => github.com/notional-labs/cometbft-db v0.0.0-20230321185329-6dc7c0ca6345 replace github.com/evmos/ethermint => github.com/zeta-chain/ethermint v0.0.0-20240531172701-61d040058c94 + +replace github.com/libp2p/go-libp2p => github.com/zeta-chain/go-libp2p v0.0.0-20240710192637-567fbaacc2b4 diff --git a/go.sum b/go.sum index ad13862826..7003b25c27 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,5 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= @@ -207,16 +205,11 @@ cosmossdk.io/simapp v0.0.0-20230608160436-666c345ad23d h1:E/8y0oG3u9hBR8l4F9MtC0 cosmossdk.io/simapp v0.0.0-20230608160436-666c345ad23d/go.mod h1:xbjky3L3DJEylaho6gXplkrMvJ5sFgv+qNX+Nn47bzY= cosmossdk.io/tools/rosetta v0.2.1 h1:ddOMatOH+pbxWbrGJKRAawdBkPYLfKXutK9IETnjYxw= cosmossdk.io/tools/rosetta v0.2.1/go.mod h1:Pqdc1FdvkNV3LcNIkYWt2RQY6IP1ge6YWZk8MhhO9Hw= -dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= -dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= -dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= filippo.io/edwards25519 v1.0.0-beta.2/go.mod h1:X+pm78QAUPtFLi1z9PYIlS/bdDnvbCOGKtZ+ACWEf7o= filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= -git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= git.sr.ht/~sircmpwn/getopt v0.0.0-20191230200459-23622cc906b3/go.mod h1:wMEGFFFNuPos7vHmWXfszqImLppbc0wEhh6JBfJIUgw= git.sr.ht/~sircmpwn/go-bare v0.0.0-20210406120253-ab86bc2846d9/go.mod h1:BVJwbDfVjCjoFiKrhkei6NdGcZYpkDkdyCdg1ukytRA= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= @@ -232,6 +225,7 @@ github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSu github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo= github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= @@ -261,7 +255,9 @@ github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKz github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= +github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= @@ -278,6 +274,7 @@ github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/ github.com/Workiva/go-datastructures v1.0.52/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= github.com/Zilliqa/gozilliqa-sdk v1.2.1-0.20201201074141-dd0ecada1be6/go.mod h1:eSYp2T6f0apnuW8TzhV3f6Aff2SE8Dwio++U4ha4yEM= github.com/adlio/schema v1.3.3 h1:oBJn8I02PyTB466pZO1UZEn1TV5XLlifBSyMrmHl/1I= +github.com/adlio/schema v1.3.3/go.mod h1:1EsRssiv9/Ce2CMzq5DoL7RiMshhuigQxrR4DMV9fHg= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= @@ -289,8 +286,8 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= +github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= @@ -343,9 +340,9 @@ github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bool64/dev v0.2.29 h1:x+syGyh+0eWtOzQ1ItvLzOGIWyNWnyjXpHIcpF2HvL4= +github.com/bool64/dev v0.2.29/go.mod h1:iJbh1y/HkunEPhgebWRNcs8wfGq7sjvJ6W5iabL8ACg= github.com/bool64/shared v0.1.5 h1:fp3eUhBsrSjNCQPcSdQqZxxh9bBwrYiZ+zOKFkM0/2E= github.com/bool64/shared v0.1.5/go.mod h1:081yz68YC9jeFB3+Bbmno2RFWvGKv1lPKkMP6MHJlPs= -github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/btcsuite/btcd v0.22.3 h1:kYNaWFvOw6xvqP0vR20RP1Zq1DVMBxEO8QN5d1/EfNg= github.com/btcsuite/btcd v0.22.3/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y= github.com/btcsuite/btcd/btcec/v2 v2.1.2/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= @@ -373,15 +370,15 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3 github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= -github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= -github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= @@ -449,6 +446,7 @@ github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTF github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= +github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -456,7 +454,6 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= @@ -522,6 +519,7 @@ github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsP github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/edwards/v2 v2.0.0 h1:E5KszxGgpjpmW8vN811G6rBAZg0/S/DftdGqN4FW5x4= github.com/decred/dcrd/dcrec/edwards/v2 v2.0.0/go.mod h1:d0H8xGMWbiIQP7gN3v2rByWUcuZPm9YsgmnfoxgbINc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= @@ -555,6 +553,7 @@ github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/ github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= @@ -605,10 +604,12 @@ github.com/ethereum/go-ethereum v1.10.26 h1:i/7d9RBBwiXCEuyduBQzJw/mKmnvzsN14jqB github.com/ethereum/go-ethereum v1.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= +github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= +github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -623,17 +624,15 @@ github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+ github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ= github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= -github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/frumioj/crypto11 v1.2.5-0.20210823151709-946ce662cc0e h1:HRagc2sBsKLDvVVXQMaCEU8ueRFAl3txucwykhQPbGc= github.com/frumioj/crypto11 v1.2.5-0.20210823151709-946ce662cc0e/go.mod h1:/1u7qgWwAI7wja4BdNu5Vd5gqMtmtoiACHlhl46uY1E= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -655,13 +654,14 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= -github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -693,10 +693,13 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= +github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= @@ -715,6 +718,7 @@ github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -743,7 +747,6 @@ github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -804,12 +807,12 @@ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= @@ -819,6 +822,7 @@ github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIG github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us= github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -852,8 +856,6 @@ github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= -github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= -github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -868,6 +870,7 @@ github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qK github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4= +github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= @@ -885,7 +888,6 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.2.1/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= @@ -894,7 +896,6 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= @@ -1008,6 +1009,7 @@ github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8 github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= github.com/ipfs/go-ipfs-util v0.0.2 h1:59Sswnk1MFaiq+VcaknX7aYEyGyGDAA73ilhEK2POp8= +github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ= github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8= github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo= github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g= @@ -1023,16 +1025,17 @@ github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc= +github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg= github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= -github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/protoreflect v1.8.2/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= +github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= @@ -1055,6 +1058,7 @@ github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o= @@ -1109,7 +1113,6 @@ github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NB github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= @@ -1120,6 +1123,7 @@ github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= @@ -1130,8 +1134,6 @@ github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38y github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM= github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro= -github.com/libp2p/go-libp2p v0.27.8 h1:IX5x/4yKwyPQeVS2AXHZ3J4YATM9oHBGH1gBc23jBAI= -github.com/libp2p/go-libp2p v0.27.8/go.mod h1:eCFFtd0s5i/EVKR7+5Ki8bM7qwkNW3TPTTSSW9sz8NE= github.com/libp2p/go-libp2p-asn-util v0.3.0 h1:gMDcMyYiZKkocGXDQ5nsUQyquC9+H+iLEQHwOCZ7s8s= github.com/libp2p/go-libp2p-asn-util v0.3.0/go.mod h1:B1mcOrKUE35Xq/ASTmQ4tN3LNzVVaMNmq2NACuqyB9w= github.com/libp2p/go-libp2p-kad-dht v0.24.2 h1:zd7myKBKCmtZBhI3I0zm8xBkb28v3gmSEtQfBdAdFwc= @@ -1141,6 +1143,7 @@ github.com/libp2p/go-libp2p-kbucket v0.6.3/go.mod h1:RCseT7AH6eJWxxk2ol03xtP9pEH github.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0= github.com/libp2p/go-libp2p-record v0.2.0/go.mod h1:I+3zMkvvg5m2OcSdoL0KPljyJyvNDFGKX7QdlpYUcwk= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= +github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= github.com/libp2p/go-nat v0.1.0 h1:MfVsH6DLcpa04Xr+p8hmVRG4juse0s3J8HyNWYHffXg= @@ -1156,14 +1159,12 @@ github.com/libp2p/go-yamux/v4 v4.0.0/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5 github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lucasjones/reggen v0.0.0-20180717132126-cdb49ff09d77/go.mod h1:5ELEyG+X8f+meRWHuqUOewBOhvHkl7M76pdGEansxW4= -github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= @@ -1210,7 +1211,6 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg= github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ= -github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= @@ -1259,6 +1259,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= @@ -1308,8 +1309,6 @@ github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7 github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= -github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/neilotoole/errgroup v0.1.5/go.mod h1:Q2nLGf+594h0CLBs/Mbg6qOr7GtqDK7C2S41udRnToE= github.com/neilotoole/errgroup v0.1.6/go.mod h1:Q2nLGf+594h0CLBs/Mbg6qOr7GtqDK7C2S41udRnToE= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= @@ -1318,6 +1317,7 @@ github.com/notional-labs/cometbft-db v0.0.0-20230321185329-6dc7c0ca6345 h1:QeXhT github.com/notional-labs/cometbft-db v0.0.0-20230321185329-6dc7c0ca6345/go.mod h1:a5TUP6VLnFBEcmg+xhGwb2lO9BjzkHGxg0c8wyRfjN8= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -1334,6 +1334,7 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108 github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss= github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= @@ -1341,10 +1342,14 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= +github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= +github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= github.com/opencontainers/runc v1.1.3 h1:vIXrkId+0/J2Ymu2m7VjGvbSlAId9XNRPhn2p4b+d8w= +github.com/opencontainers/runc v1.1.3/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= @@ -1356,11 +1361,11 @@ github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= -github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= +github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= github.com/otiai10/copy v1.6.0/go.mod h1:XWfuS3CrI0R6IE0FbgHsEazaXO8G0LpMp9o8tos0x4E= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= @@ -1406,7 +1411,6 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4= github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= @@ -1426,7 +1430,6 @@ github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= -github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -1441,7 +1444,6 @@ github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9 github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= -github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -1456,16 +1458,6 @@ github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= -github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/qtls-go1-19 v0.3.3 h1:wznEHvJwd+2X3PqftRha0SUKmGsnb6dfArMhy9PeJVE= -github.com/quic-go/qtls-go1-19 v0.3.3/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= -github.com/quic-go/qtls-go1-20 v0.2.3 h1:m575dovXn1y2ATOb1XrRFcrv0F+EQmlowTkoraNkDPI= -github.com/quic-go/qtls-go1-20 v0.2.3/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= -github.com/quic-go/quic-go v0.33.0 h1:ItNoTDN/Fm/zBlq769lLJc8ECe9gYaW40veHCCco7y0= -github.com/quic-go/quic-go v0.33.0/go.mod h1:YMuhaAV9/jIu0XclDXwZPAsP/2Kgr5yMYhe9oxhhOFA= -github.com/quic-go/webtransport-go v0.5.3 h1:5XMlzemqB4qmOlgIus5zB45AcZ2kCgCy2EptUrfOPWU= -github.com/quic-go/webtransport-go v0.5.3/go.mod h1:OhmmgJIzTTqXK5xvtuX0oBpLV2GkLWNDA+UeTGJXErU= github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= @@ -1520,34 +1512,13 @@ github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NF github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= -github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= -github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= -github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= -github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= -github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= -github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= -github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= -github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= -github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= -github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= -github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= -github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= -github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= -github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= -github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= -github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= -github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= @@ -1557,8 +1528,6 @@ github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3 github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa/go.mod h1:oJyF+mSPHbB5mVY2iO9KV3pTt/QbIkGaO8gQ2WrDbP4= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= -github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -1627,7 +1596,6 @@ github.com/swaggest/assertjson v1.9.0 h1:dKu0BfJkIxv/xe//mkCrK5yZbs79jL7OVf9Ija7 github.com/swaggest/assertjson v1.9.0/go.mod h1:b+ZKX2VRiUjxfUIal0HDN85W0nHPAYUbYH5WkkSsFsU= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= -github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok= github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= github.com/tendermint/btcd v0.1.1/go.mod h1:DC6/m53jtQzr/NFmMNEu0rxf18/ktVoVtMrnDD5pN+U= @@ -1683,6 +1651,7 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= @@ -1693,14 +1662,13 @@ github.com/urfave/cli v1.22.10 h1:p8Fspmz3iTctJstry1PYS3HVdllxnEzTEsgIgtxTrCk= github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/urfave/cli/v2 v2.10.2 h1:x3p8awjp/2arX+Nl/G2040AZpOCHS/eMJJ1/a+mye4Y= +github.com/urfave/cli/v2 v2.10.2/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= -github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= -github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/vmihailenco/msgpack/v5 v5.1.4/go.mod h1:C5gboKD0TJPqWDTVTtrQNfRbiBwHZGo8UTqP/9/XvLI= github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= @@ -1718,6 +1686,7 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= github.com/ybbus/jsonrpc v2.1.2+incompatible/go.mod h1:XJrh1eMSzdIYFbM08flv0wp5G35eRniyeGut1z+LSiE= github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA= @@ -1734,8 +1703,10 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zeta-chain/ethermint v0.0.0-20240531172701-61d040058c94 h1:M54ljayJvy+WlEVdUmX8pgo1nA+XguB3mLhm3wi2z9o= github.com/zeta-chain/ethermint v0.0.0-20240531172701-61d040058c94/go.mod h1:s1zA6OpXv3Tb5I0M6M6j5fo/AssaZL/pgkc7G0W2kN8= -github.com/zeta-chain/go-tss v0.1.1-0.20240430111318-1785e48eb127 h1:AGQepvsMIVHAHPlplzNcSCyMoGBY1DfO4WHG/QHUSIU= -github.com/zeta-chain/go-tss v0.1.1-0.20240430111318-1785e48eb127/go.mod h1:bVpAoSlRYYCY/R34horVU3cheeHqhSVxygelc++emIU= +github.com/zeta-chain/go-libp2p v0.0.0-20240710192637-567fbaacc2b4 h1:FmO3HfVdZ7LzxBUfg6sVzV7ilKElQU2DZm8PxJ7KcYI= +github.com/zeta-chain/go-libp2p v0.0.0-20240710192637-567fbaacc2b4/go.mod h1:TBv5NY/CqWYIfUstXO1fDWrt4bDoqgCw79yihqBspg8= +github.com/zeta-chain/go-tss v0.1.1-0.20240711225655-6ab1e42a0dee h1:6/Pjh9eTfdKebYDi4jn5n0UcmzVwLjJuF733AA3kRaQ= +github.com/zeta-chain/go-tss v0.1.1-0.20240711225655-6ab1e42a0dee/go.mod h1:yphhJIacmFEAUxAwyRI4HR2pPt6Gk0jl2m7PD+1OwTM= github.com/zeta-chain/keystone/keys v0.0.0-20231105174229-903bc9405da2 h1:gd2uE0X+ZbdFJ8DubxNqLbOVlCB12EgWdzSNRAR82tM= github.com/zeta-chain/keystone/keys v0.0.0-20231105174229-903bc9405da2/go.mod h1:x7Bkwbzt2W2lQfjOirnff0Dj+tykdbTG1FMJPVPZsvE= github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20240418181724-c222fd3ae1f5 h1:ljM7xka3WZvth9k1uYxrG3/FKQQTkR96FZlIjUKOoYw= @@ -1757,13 +1728,13 @@ go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQc go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.nhat.io/aferomock v0.4.0 h1:gs3nJzIqAezglUuaPfautAmZwulwRWLcfSSzdK4YCC0= +go.nhat.io/aferomock v0.4.0/go.mod h1:msi5MDOtJ/AroUa/lDc3jVGOILM4SKP//4yBRImOvkI= go.nhat.io/grpcmock v0.25.0 h1:zk03vvA60w7UrnurZbqL4wxnjmJz1Kuyb7ig2MF+n4c= go.nhat.io/grpcmock v0.25.0/go.mod h1:5U694ASEFBkiZP7aPuz9kbbb/jphVlfpbOnocyht/rE= go.nhat.io/matcher/v2 v2.0.0 h1:W+rbHi0hKuZHtOQH4U5g+KwyKyfVioIxrxjoGRcUETE= go.nhat.io/matcher/v2 v2.0.0/go.mod h1:cL5oYp0M9A4L8jEGqjmUfy+k7AXVDddoVt6aYIL1r5g= go.nhat.io/wait v0.1.0 h1:aQ4YDzaOgFbypiJ9c/eAfOIB1G25VOv7Gd2QS8uz1gw= go.nhat.io/wait v0.1.0/go.mod h1:+ijMghc9/9zXi+HDcs49HNReprvXOZha2Q3jTOtqJrE= -go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -1780,6 +1751,7 @@ go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/ go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= +go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= @@ -1797,6 +1769,7 @@ go.uber.org/fx v1.19.2/go.mod h1:43G1VcqSzbIv77y00p1DRAsyZS8WdzuYdhZXmEUkMyQ= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= @@ -1813,14 +1786,10 @@ go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= -go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= -golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -1866,7 +1835,6 @@ golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb/go.mod h1:FXUEEKJgO7OQYeo8N0 golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1901,8 +1869,6 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1910,7 +1876,6 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -1972,8 +1937,6 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2001,7 +1964,6 @@ golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= -golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2025,7 +1987,6 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2035,7 +1996,6 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2175,7 +2135,6 @@ golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -2262,9 +2221,6 @@ gonum.org/v1/gonum v0.13.0/go.mod h1:/WPYRckkfWrhWefxyYTfrTtQR0KH4iyHNuzxqXAKyAU gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -2319,7 +2275,6 @@ google.golang.org/api v0.152.0 h1:t0r1vPnfMc260S2Ci+en7kfCZaLOPs5KI0sVV/6jZrY= google.golang.org/api v0.152.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= @@ -2331,9 +2286,6 @@ google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJ google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= -google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -2450,8 +2402,6 @@ google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0/go. google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 h1:gphdwh0npgs8elJ4T6J+DQJHPVF7RsuJHCfwztUb4J4= google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -2530,7 +2480,6 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= @@ -2566,7 +2515,7 @@ gorm.io/gorm v1.24.6/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= -grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -2592,5 +2541,3 @@ sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= -sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/pkg/bg/bg.go b/pkg/bg/bg.go new file mode 100644 index 0000000000..85d85964cf --- /dev/null +++ b/pkg/bg/bg.go @@ -0,0 +1,62 @@ +// Package bg provides primitives for the background tasks +package bg + +import ( + "context" + "fmt" + + "github.com/rs/zerolog" +) + +type config struct { + name string + logger zerolog.Logger +} + +type Opt func(*config) + +func WithName(name string) Opt { + return func(cfg *config) { cfg.name = name } +} + +func WithLogger(logger zerolog.Logger) Opt { + return func(cfg *config) { cfg.logger = logger } +} + +// Work emits a new task in the background +func Work(ctx context.Context, f func(context.Context) error, opts ...Opt) { + cfg := config{ + name: "", + logger: zerolog.Nop(), + } + + for _, opt := range opts { + opt(&cfg) + } + + go func() { + defer func() { + if r := recover(); r != nil { + err := fmt.Errorf("recovered from PANIC in background task: %v", r) + logError(err, cfg) + } + }() + + if err := f(ctx); err != nil { + logError(err, cfg) + } + }() +} + +func logError(err error, cfg config) { + if err == nil { + return + } + + name := cfg.name + if name == "" { + name = "unknown" + } + + cfg.logger.Error().Err(err).Str("worker.name", name).Msgf("Background task failed") +} diff --git a/pkg/bg/bg_test.go b/pkg/bg/bg_test.go new file mode 100644 index 0000000000..c55b6287f9 --- /dev/null +++ b/pkg/bg/bg_test.go @@ -0,0 +1,81 @@ +package bg + +import ( + "bytes" + "context" + "fmt" + "testing" + "time" + + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" +) + +func TestWork(t *testing.T) { + ctx := context.Background() + + t.Run("basic case", func(t *testing.T) { + // ARRANGE + signal := make(chan struct{}) + + // ACT + Work(ctx, func(ctx context.Context) error { + // simulate some work + time.Sleep(100 * time.Millisecond) + close(signal) + return nil + }) + + // ASSERT + <-signal + assertChanClosed(t, signal) + }) + + t.Run("with name and logger", func(t *testing.T) { + // ARRANGE + // Given a logger + out := &bytes.Buffer{} + logger := zerolog.New(out) + + // And a call returning an error + call := func(ctx context.Context) error { + time.Sleep(100 * time.Millisecond) + return fmt.Errorf("oopsie") + } + + // ACT + Work(ctx, call, WithName("hello"), WithLogger(logger)) + time.Sleep(200 * time.Millisecond) + + // Check the log output + const expected = `{"level":"error","error":"oopsie","worker.name":"hello","message":"Background task failed"}` + assert.JSONEq(t, expected, out.String()) + }) + + t.Run("panic recovery", func(t *testing.T) { + // ARRANGE + // Given a logger + out := &bytes.Buffer{} + logger := zerolog.New(out) + + // And a call that has panic + call := func(ctx context.Context) error { + panic("press F") + return nil + } + + // ACT + Work(ctx, call, WithLogger(logger)) + time.Sleep(100 * time.Millisecond) + + // Check the log output + const expected = `{"level":"error","error":"recovered from PANIC in background task: press F",` + + `"worker.name":"unknown","message":"Background task failed"}` + assert.JSONEq(t, expected, out.String()) + }) +} + +func assertChanClosed(t *testing.T, ch <-chan struct{}) { + _, ok := <-ch + assert.False(t, ok, "channel is not closed") +} diff --git a/pkg/chains/conversion.go b/pkg/chains/conversion.go index 84b3eaf3aa..949d39ebb7 100644 --- a/pkg/chains/conversion.go +++ b/pkg/chains/conversion.go @@ -10,7 +10,7 @@ import ( // NonceMarkAmount uses special value to mark current nonce in UTXO func NonceMarkAmount(nonce uint64) int64 { - // #nosec G701 always in range + // #nosec G115 always in range return int64(nonce) + BtcNonceMarkOffset() } diff --git a/pkg/coin/coin.go b/pkg/coin/coin.go index a11f5b91bd..4c5c86889b 100644 --- a/pkg/coin/coin.go +++ b/pkg/coin/coin.go @@ -19,7 +19,7 @@ func GetCoinType(coin string) (CoinType, error) { if coinInt < 0 || coinInt > 3 { return CoinType_Cmd, fmt.Errorf("invalid coin type %d", coinInt) } - // #nosec G701 always in range + // #nosec G115 always in range return CoinType(coinInt), nil } diff --git a/pkg/mempool/custom_proposal_handler.go b/pkg/mempool/custom_proposal_handler.go index 1835bb050f..f61fe70cdc 100644 --- a/pkg/mempool/custom_proposal_handler.go +++ b/pkg/mempool/custom_proposal_handler.go @@ -73,7 +73,7 @@ func (h *CustomProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHand return func(ctx sdk.Context, req abci.RequestPrepareProposal) abci.ResponsePrepareProposal { var maxBlockGas uint64 if b := ctx.ConsensusParams().Block; b != nil { - // #nosec G701 range checked, cosmos-sdk forked code + // #nosec G115 range checked, cosmos-sdk forked code maxBlockGas = uint64(b.MaxGas) } @@ -89,7 +89,7 @@ func (h *CustomProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHand // XXX: We pass nil as the memTx because we have no way of decoding the // txBz. We'd need to break (update) the ProposalTxVerifier interface. // As a result, we CANNOT account for block max gas. - // #nosec G701 range checked, cosmos-sdk forked code + // #nosec G115 range checked, cosmos-sdk forked code stop := h.txSelector.SelectTxForProposal(uint64(req.MaxTxBytes), maxBlockGas, nil, txBz) if stop { break @@ -148,7 +148,7 @@ func (h *CustomProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHand panic(err) } } else { - // #nosec G701 range checked, cosmos-sdk forked code + // #nosec G115 range checked, cosmos-sdk forked code stop := h.txSelector.SelectTxForProposal(uint64(req.MaxTxBytes), maxBlockGas, memTx, txBz) if stop { break @@ -218,7 +218,7 @@ func (h *CustomProposalHandler) ProcessProposalHandler() sdk.ProcessProposalHand totalTxGas += gasTx.GetGas() } - // #nosec G701 range checked, cosmos-sdk forked code + // #nosec G115 range checked, cosmos-sdk forked code if totalTxGas > uint64(maxBlockGas) { return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT} } diff --git a/pkg/proofs/ethereum/proof.go b/pkg/proofs/ethereum/proof.go index b09919f971..8f7d2976b9 100644 --- a/pkg/proofs/ethereum/proof.go +++ b/pkg/proofs/ethereum/proof.go @@ -106,7 +106,7 @@ func (m *Proof) Verify(rootHash common.Hash, key int) ([]byte, error) { return nil, errors.New("key not found") } var indexBuf []byte - // #nosec G701 range is valid + // #nosec G115 range is valid indexBuf = rlp.AppendUint64(indexBuf[:0], uint64(key)) return trie.VerifyProof(rootHash, indexBuf, m) } @@ -133,7 +133,7 @@ func (t *Trie) GenerateProof(txIndex int) (*Proof, error) { return nil, errors.New("transaction index out of range") } var indexBuf []byte - // #nosec G701 checked as non-negative + // #nosec G115 checked as non-negative indexBuf = rlp.AppendUint64(indexBuf[:0], uint64(txIndex)) proof := NewProof() err := t.Prove(indexBuf, 0, proof) @@ -157,7 +157,7 @@ func NewTrie(list types.DerivableList) Trie { // order is correct. var indexBuf []byte for i := 1; i < list.Len() && i <= 0x7f; i++ { - // #nosec G701 iterator + // #nosec G115 iterator indexBuf = rlp.AppendUint64(indexBuf[:0], uint64(i)) value := encodeForDerive(list, i, valueBuf) hasher.Update(indexBuf, value) @@ -168,7 +168,7 @@ func NewTrie(list types.DerivableList) Trie { hasher.Update(indexBuf, value) } for i := 0x80; i < list.Len(); i++ { - // #nosec G701 iterator + // #nosec G115 iterator indexBuf = rlp.AppendUint64(indexBuf[:0], uint64(i)) value := encodeForDerive(list, i, valueBuf) hasher.Update(indexBuf, value) diff --git a/pkg/proofs/proof.go b/pkg/proofs/proof.go index 76383ed857..fe3a5b0fd3 100644 --- a/pkg/proofs/proof.go +++ b/pkg/proofs/proof.go @@ -49,7 +49,7 @@ func NewBitcoinProof(txBytes []byte, path []byte, index uint) *Proof { BitcoinProof: &bitcoin.Proof{ TxBytes: txBytes, Path: path, - // #nosec G701 always in range + // #nosec G115 always in range Index: uint32(index), }, }, diff --git a/pkg/retry/retry.go b/pkg/retry/retry.go new file mode 100644 index 0000000000..291ceafe23 --- /dev/null +++ b/pkg/retry/retry.go @@ -0,0 +1,143 @@ +// Package retry provides a generic retry mechanism with exponential backoff. +// +// Example: +// +// ctx := context.Background() +// client := foobar.NewClient() +// +// err := retry.Do(func() error { +// err := client.UpdateConfig(ctx, map[string]any{"key": "value"}) +// +// // will be retied +// if errors.Is(err, foobar.ErrTxConflict) { +// return retry.Retryable(err) +// } +// +// return err +// }) +package retry + +import ( + "context" + "time" + + "github.com/cenkalti/backoff/v4" + "github.com/pkg/errors" +) + +type Backoff = backoff.BackOff + +type Callback func() error + +type TypedCallback[T any] func() (T, error) + +type errRetryable struct { + error +} + +// DefaultBackoff returns a default backoff strategy with 5 exponential retries. +func DefaultBackoff() Backoff { + bo := backoff.NewExponentialBackOff( + backoff.WithInitialInterval(50*time.Millisecond), + backoff.WithMaxInterval(500*time.Millisecond), + backoff.WithMultiplier(1.8), + ) + + return backoff.WithMaxRetries(bo, 5) +} + +// Do executes the callback function with the default backoff config. +// It will retry a callback ONLY if error is retryable. +func Do(cb Callback) error { + return DoWithBackoff(cb, DefaultBackoff()) +} + +// DoWithBackoff executes the callback function with provided backoff config. +// It will retry a callback ONLY if error is retryable. +func DoWithBackoff(cb Callback, bo Backoff) error { + for { + err := cb() + if err == nil { + return nil + } + + var errRetry errRetryable + isRetryable := errors.As(err, &errRetry) + if !isRetryable { + return err + } + + sleepFor := bo.NextBackOff() + if sleepFor == backoff.Stop { + return errors.Wrap(err, "retry limit exceeded") + } + + time.Sleep(sleepFor) + } +} + +// DoTyped is typed version of Do that returns a value along with an error. +// It will retry a callback ONLY if error is retryable. +func DoTyped[T any](cb TypedCallback[T]) (T, error) { + return DoTypedWithBackoff(cb, DefaultBackoff()) +} + +// DoTypedWithBackoff is typed version of DoWithBackoff that returns a value along with an error. +// It will retry a callback ONLY if error is retryable. +func DoTypedWithBackoff[T any](cb TypedCallback[T], bo Backoff) (T, error) { + var ( + result T + err error + ) + + // #nosec G703 error is propagated + _ = DoWithBackoff(func() error { + result, err = cb() + return err + }, bo) + + return result, err +} + +// DoTypedWithRetry is DoTyped but ANY error is retried. +func DoTypedWithRetry[T any](cb TypedCallback[T]) (T, error) { + wrapper := func() (T, error) { + return RetryTyped(cb()) + } + + return DoTypedWithBackoffAndRetry(wrapper, DefaultBackoff()) +} + +// DoTypedWithBackoffAndRetry is DoTypedWithBackoff but ANY error is retried. +func DoTypedWithBackoffAndRetry[T any](cb TypedCallback[T], bo Backoff) (T, error) { + wrapper := func() (T, error) { + return RetryTyped(cb()) + } + + return DoTypedWithBackoff(wrapper, bo) +} + +// Retry wraps error to mark it as retryable. Skips retry for context errors. +func Retry(err error) error { + switch { + case err == nil: + return nil + case errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded): + // do not retry context errors + return err + default: + return errRetryable{error: err} + } +} + +// RetryTyped wraps error to mark it as retryable +// +//goland:noinspection GoNameStartsWithPackageName +//nolint:revive +func RetryTyped[T any](result T, err error) (T, error) { + if err == nil { + return result, nil + } + + return result, Retry(err) +} diff --git a/pkg/retry/retry_test.go b/pkg/retry/retry_test.go new file mode 100644 index 0000000000..7bb983a16d --- /dev/null +++ b/pkg/retry/retry_test.go @@ -0,0 +1,153 @@ +package retry + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestDo(t *testing.T) { + t.Parallel() + + t.Run("no error", func(t *testing.T) { + err := Do(func() error { return nil }) + + assert.NoError(t, err) + }) + + t.Run("non-retryable error", func(t *testing.T) { + var counter int + err := Do(func() error { + counter++ + return errors.New("something went wrong") + }) + + assert.Equal(t, 1, counter) + assert.ErrorContains(t, err, "something went wrong") + }) + + t.Run("retryable error suddenly became non-retryable", func(t *testing.T) { + var counter int + err := Do(func() error { + err := errors.New("something went wrong") + + counter++ + if counter < 3 { + return Retry(err) + } + + return err + }) + + assert.Equal(t, 3, counter) + assert.ErrorContains(t, err, "something went wrong") + }) + + t.Run("retryable code eventually works", func(t *testing.T) { + var counter int + err := Do(func() error { + err := errors.New("something went wrong") + + counter++ + if counter < 3 { + return Retry(err) + } + + return nil + }) + + assert.Equal(t, 3, counter) + assert.NoError(t, err) + }) + + t.Run("retry limit exceeded", func(t *testing.T) { + start := time.Now() + + var counter int + err := Do(func() error { + trackTime(t, start) + err := errors.New("something went wrong") + + counter++ + return Retry(err) + }) + + assert.ErrorContains(t, err, "retry limit exceeded") + }) + + t.Run("context errors are non-retryable", func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) + defer cancel() + + var counter int + err := Do(func() error { + time.Sleep(100 * time.Millisecond) + + if err := ctx.Err(); err != nil { + return err + } + + counter++ + + return nil + }) + + assert.Equal(t, 0, counter) + assert.ErrorIs(t, err, context.DeadlineExceeded) + }) +} + +func TestDoTyped(t *testing.T) { + t.Parallel() + + type myType struct { + Value string + } + + t.Run("no error", func(t *testing.T) { + result, err := DoTyped(func() (myType, error) { + return myType{Value: "abc"}, nil + }) + + assert.NoError(t, err) + assert.Equal(t, "abc", result.Value) + }) + + t.Run("fails", func(t *testing.T) { + var counter int + + result, err := DoTyped(func() (myType, error) { + counter++ + return myType{}, errors.New("something went wrong") + }) + + assert.ErrorContains(t, err, "something went wrong") + assert.Empty(t, result) + assert.Equal(t, 1, counter) + }) + + t.Run("recovers", func(t *testing.T) { + var counter int + + result, err := DoTyped(func() (myType, error) { + counter++ + if counter == 4 { + return myType{Value: "abc"}, nil + } + return myType{}, Retry(errors.New("something went wrong")) + }) + + assert.NoError(t, err) + assert.Equal(t, "abc", result.Value) + assert.Equal(t, 4, counter) + }) +} + +func trackTime(t *testing.T, from time.Time) { + duration := time.Since(from) + + t.Logf("Retrier invokation: t = %dms", duration.Milliseconds()) +} diff --git a/rpc/backend/account_info.go b/rpc/backend/account_info.go index 4711074c89..59d6a57520 100644 --- a/rpc/backend/account_info.go +++ b/rpc/backend/account_info.go @@ -82,7 +82,7 @@ func (b *Backend) GetProof( return nil, fmt.Errorf("not able to query block number greater than MaxInt64") } - // #nosec G701 range checked + // #nosec G115 range checked height = int64(bn) } @@ -212,7 +212,7 @@ func (b *Backend) GetTransactionCount(address common.Address, blockNum rpctypes. return nil, fmt.Errorf("not able to query block number greater than MaxInt64") } height := blockNum.Int64() - // #nosec G701 range checked + // #nosec G115 range checked currentHeight := int64(bn) if height > currentHeight { diff --git a/rpc/backend/blocks.go b/rpc/backend/blocks.go index d28b8345da..a4a9d03063 100644 --- a/rpc/backend/blocks.go +++ b/rpc/backend/blocks.go @@ -197,7 +197,7 @@ func (b *Backend) TendermintBlockByNumber(blockNum rpctypes.BlockNumber) (*tmrpc if n > math.MaxInt64 { return nil, fmt.Errorf("block number %d is greater than max int64", n) } - // #nosec G701 range checked + // #nosec G115 range checked height = int64(n) } resBlock, err := b.clientCtx.Client.Block(b.ctx, &height) @@ -494,15 +494,15 @@ func (b *Backend) RPCBlockFromTendermintBlock( rpcTx, err = rpctypes.NewRPCTransaction( tx, common.BytesToHash(block.Hash()), - // #nosec G701 non negative value + // #nosec G115 non negative value uint64(block.Height), - // #nosec G701 non negative value + // #nosec G115 non negative value uint64(txIndex), baseFee, b.chainID, ) } else { - // #nosec G701 non negative value + // #nosec G115 non negative value rpcTx, err = rpctypes.NewRPCTransactionFromIncompleteMsg(ethMsg, common.BytesToHash(block.Hash()), uint64(block.Height), uint64(txIndex), baseFee, b.chainID, txsAdditional[txIndex]) } if err != nil { @@ -556,7 +556,7 @@ func (b *Backend) RPCBlockFromTendermintBlock( // block gas limit has exceeded, other txs must have failed with same reason. break } - // #nosec G701 non negative value + // #nosec G115 non negative value gasUsed += uint64(txsResult.GetGasUsed()) } diff --git a/rpc/backend/chain_info.go b/rpc/backend/chain_info.go index bb993054d8..c5a06d354a 100644 --- a/rpc/backend/chain_info.go +++ b/rpc/backend/chain_info.go @@ -184,14 +184,14 @@ func (b *Backend) FeeHistory( if blockNumber > math.MaxInt64 { return nil, fmt.Errorf("not able to query block number greater than MaxInt64") } - // #nosec G701 range checked + // #nosec G115 range checked blockEnd = int64(blockNumber) } if userBlockCount > math.MaxInt64 { return nil, fmt.Errorf("not able to query block count greater than MaxInt64") } - // #nosec G701 range checked + // #nosec G115 range checked blocks := int64(userBlockCount) maxBlockCount := int64(b.cfg.JSONRPC.FeeHistoryCap) if blocks > maxBlockCount { @@ -220,7 +220,7 @@ func (b *Backend) FeeHistory( // fetch block for blockID := blockStart; blockID <= blockEnd; blockID++ { - // #nosec G701 range checked + // #nosec G115 range checked index := int32(blockID - blockStart) // tendermint block tendermintblock, err := b.TendermintBlockByNumber(rpctypes.BlockNumber(blockID)) @@ -297,7 +297,7 @@ func (b *Backend) SuggestGasTipCap(baseFee *big.Int) (*big.Int, error) { // MaxDelta = BaseFee * (GasLimit - GasLimit / ElasticityMultiplier) / (GasLimit / ElasticityMultiplier) / Denominator // = BaseFee * (ElasticityMultiplier - 1) / Denominator // ``` - // #nosec G701 range checked + // #nosec G115 range checked maxDelta := baseFee.Int64() * (int64(params.Params.ElasticityMultiplier) - 1) / int64( params.Params.BaseFeeChangeDenominator, ) diff --git a/rpc/backend/tx_info.go b/rpc/backend/tx_info.go index d43dc26177..a65c86743f 100644 --- a/rpc/backend/tx_info.go +++ b/rpc/backend/tx_info.go @@ -56,7 +56,7 @@ func (b *Backend) GetTransactionByHash(txHash common.Hash) (*rpctypes.RPCTransac var ethMsg *evmtypes.MsgEthereumTx // if additional fields are empty we can try to get MsgEthereumTx from sdk.Msg array if additional == nil { - // #nosec G701 always in range + // #nosec G115 always in range if int(res.TxIndex) >= len(resBlock.Block.Txs) { b.logger.Error("tx out of bounds") return nil, fmt.Errorf("tx out of bounds") @@ -85,7 +85,7 @@ func (b *Backend) GetTransactionByHash(txHash common.Hash) (*rpctypes.RPCTransac msgs, _ := b.EthMsgsFromTendermintBlock(resBlock, blockRes) for i := range msgs { if msgs[i].Hash == hexTx { - // #nosec G701 always in range + // #nosec G115 always in range res.EthTxIndex = int32(i) break } @@ -115,9 +115,9 @@ func (b *Backend) GetTransactionByHash(txHash common.Hash) (*rpctypes.RPCTransac return rpctypes.NewTransactionFromMsg( ethMsg, common.BytesToHash(resBlock.BlockID.Hash.Bytes()), - // #nosec G701 always positive + // #nosec G115 always positive uint64(res.Height), - // #nosec G701 always positive + // #nosec G115 always positive uint64(res.EthTxIndex), baseFee, b.chainID, @@ -184,7 +184,7 @@ func (b *Backend) GetTransactionReceipt(hash common.Hash) (map[string]interface{ var ethMsg *evmtypes.MsgEthereumTx // if additional fields are empty we can try to get MsgEthereumTx from sdk.Msg array if additional == nil { - // #nosec G701 always in range + // #nosec G115 always in range if int(res.TxIndex) >= len(resBlock.Block.Txs) { b.logger.Error("tx out of bounds") return nil, fmt.Errorf("tx out of bounds") @@ -221,7 +221,7 @@ func (b *Backend) GetTransactionReceipt(hash common.Hash) (map[string]interface{ return nil, nil } for _, txResult := range blockRes.TxsResults[0:res.TxIndex] { - // #nosec G701 always positive + // #nosec G115 always positive cumulativeGasUsed += uint64(txResult.GasUsed) } cumulativeGasUsed += res.CumulativeGasUsed @@ -251,7 +251,7 @@ func (b *Backend) GetTransactionReceipt(hash common.Hash) (map[string]interface{ } // parse tx logs from events - // #nosec G701 always in range + // #nosec G115 always in range logs, err := TxLogsFromEvents(blockRes.TxsResults[res.TxIndex].Events, int(res.MsgIndex)) if err != nil { b.logger.Debug("failed to parse logs", "hash", hexTx, "error", err.Error()) @@ -262,7 +262,7 @@ func (b *Backend) GetTransactionReceipt(hash common.Hash) (map[string]interface{ msgs, _ := b.EthMsgsFromTendermintBlock(resBlock, blockRes) for i := range msgs { if msgs[i].Hash == hexTx { - // #nosec G701 always in range + // #nosec G115 always in range res.EthTxIndex = int32(i) break } @@ -281,7 +281,7 @@ func (b *Backend) GetTransactionReceipt(hash common.Hash) (map[string]interface{ var txType uint8 if txData == nil { - // #nosec G701 always in range + // #nosec G115 always in range txType = uint8(additional.Type) *to = additional.Recipient } else { @@ -311,6 +311,7 @@ func (b *Backend) GetTransactionReceipt(hash common.Hash) (map[string]interface{ // sender and receiver (contract or EOA) addreses "from": from, "to": to, + // #nosec G115 uint8 -> uint false positive "type": hexutil.Uint(txType), } @@ -411,7 +412,7 @@ func (b *Backend) GetTxByTxIndex( index uint, ) (*ethermint.TxResult, *rpctypes.TxResultAdditionalFields, error) { if b.indexer != nil { - // #nosec G701 always in range + // #nosec G115 always in range txRes, err := b.indexer.GetByBlockAndIndex(height, int32(index)) if err == nil { return txRes, nil, nil @@ -424,7 +425,7 @@ func (b *Backend) GetTxByTxIndex( evmtypes.AttributeKeyTxIndex, index, ) txResult, txAdditional, err := b.queryTendermintTxIndexer(query, func(txs *rpctypes.ParsedTxs) *rpctypes.ParsedTx { - // #nosec G701 always in range + // #nosec G115 always in range return txs.GetTxByTxIndex(int(index)) }) if err != nil { @@ -472,7 +473,7 @@ func (b *Backend) GetTransactionByBlockAndIndex( return nil, nil } - // #nosec G701 always in range + // #nosec G115 always in range i := int(idx) ethMsgs, additionals := b.EthMsgsFromTendermintBlock(block, blockRes) if i >= len(ethMsgs) { @@ -497,9 +498,9 @@ func (b *Backend) GetTransactionByBlockAndIndex( return rpctypes.NewTransactionFromMsg( msg, common.BytesToHash(block.Block.Hash()), - // #nosec G701 always positive + // #nosec G115 always positive uint64(block.Block.Height), - // #nosec G701 always positive + // #nosec G115 always positive uint64(idx), baseFee, b.chainID, diff --git a/rpc/backend/utils.go b/rpc/backend/utils.go index 38a53c3d3c..1be9e81b33 100644 --- a/rpc/backend/utils.go +++ b/rpc/backend/utils.go @@ -188,7 +188,7 @@ func (b *Backend) processBlock( b.logger.Debug("failed to decode transaction in block", "height", blockHeight, "error", err.Error()) continue } - // #nosec G701 always positive + // #nosec G115 always positive txGasUsed := uint64(eachTendermintTxResult.GasUsed) for _, msg := range tx.GetMsgs() { ethMsg, ok := msg.(*evmtypes.MsgEthereumTx) @@ -216,7 +216,7 @@ func (b *Backend) processBlock( sumGasUsed := sorter[0].gasUsed for i, p := range rewardPercentiles { - // #nosec G701 always positive + // #nosec G115 always positive thresholdGasUsed := uint64(blockGasUsed * p / 100) for sumGasUsed < thresholdGasUsed && txIndex < ethTxCount-1 { txIndex++ diff --git a/rpc/namespaces/ethereum/eth/api.go b/rpc/namespaces/ethereum/eth/api.go index 982f5655ae..2a558109af 100644 --- a/rpc/namespaces/ethereum/eth/api.go +++ b/rpc/namespaces/ethereum/eth/api.go @@ -466,7 +466,7 @@ func (e *PublicAPI) GetTransactionLogs(txHash common.Hash) ([]*ethtypes.Log, err } // parse tx logs from events - // #nosec G701 always in range + // #nosec G115 always in range return backend.TxLogsFromEvents(resBlockResult.TxsResults[res.TxIndex].Events, int(res.MsgIndex)) } diff --git a/rpc/types/block.go b/rpc/types/block.go index 3e9f1b012b..4515e67d8c 100644 --- a/rpc/types/block.go +++ b/rpc/types/block.go @@ -108,7 +108,7 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error { if blckNum > math.MaxInt64 { return fmt.Errorf("block number larger than int64") } - // #nosec G701 range checked + // #nosec G115 range checked *bn = BlockNumber(blckNum) return nil diff --git a/rpc/types/events.go b/rpc/types/events.go index f43270fae3..4650f460a6 100644 --- a/rpc/types/events.go +++ b/rpc/types/events.go @@ -171,7 +171,7 @@ func ParseTxResult(result *abci.ResponseDeliverTx, tx sdk.Tx) (*ParsedTxs, error // some old versions miss some events, fill it with tx result // txs with type CosmosEVMTxType will always emit GasUsed in events so no need to override for those if len(p.Txs) == 1 && p.Txs[0].Type != CosmosEVMTxType { - // #nosec G701 always positive + // #nosec G115 always positive p.Txs[0].GasUsed = uint64(result.GasUsed) } @@ -228,7 +228,7 @@ func ParseTxIndexerResult( return ðermint.TxResult{ Height: txResult.Height, TxIndex: txResult.Index, - // #nosec G701 always in range + // #nosec G115 always in range MsgIndex: uint32(parsedTx.MsgIndex), EthTxIndex: parsedTx.EthTxIndex, Failed: parsedTx.Failed, @@ -249,7 +249,7 @@ func ParseTxIndexerResult( return ðermint.TxResult{ Height: txResult.Height, TxIndex: txResult.Index, - // #nosec G701 always in range + // #nosec G115 always in range MsgIndex: uint32(parsedTx.MsgIndex), EthTxIndex: parsedTx.EthTxIndex, Failed: parsedTx.Failed, @@ -277,9 +277,9 @@ func ParseTxBlockResult( if parsedTx.Type == CosmosEVMTxType { return ðermint.TxResult{ Height: height, - // #nosec G701 always in range + // #nosec G115 always in range TxIndex: uint32(txIndex), - // #nosec G701 always in range + // #nosec G115 always in range MsgIndex: uint32(parsedTx.MsgIndex), EthTxIndex: parsedTx.EthTxIndex, Failed: parsedTx.Failed, @@ -299,9 +299,9 @@ func ParseTxBlockResult( } return ðermint.TxResult{ Height: height, - // #nosec G701 always in range + // #nosec G115 always in range TxIndex: uint32(txIndex), - // #nosec G701 always in range + // #nosec G115 always in range MsgIndex: uint32(parsedTx.MsgIndex), EthTxIndex: parsedTx.EthTxIndex, Failed: parsedTx.Failed, @@ -387,7 +387,7 @@ func fillTxAttribute(tx *ParsedTx, key, value string) error { if err != nil { return err } - // #nosec G701 always in range + // #nosec G115 always in range tx.EthTxIndex = int32(txIndex) case evmtypes.AttributeKeyTxGasUsed: gasUsed, err := strconv.ParseUint(value, 10, 64) diff --git a/rpc/types/utils.go b/rpc/types/utils.go index 7ba3c998da..cd00d55746 100644 --- a/rpc/types/utils.go +++ b/rpc/types/utils.go @@ -79,7 +79,7 @@ func EthHeaderFromTendermint(header tmtypes.Header, bloom ethtypes.Bloom, baseFe Number: big.NewInt(header.Height), GasLimit: 0, GasUsed: 0, - // #nosec G701 always positive + // #nosec G115 always positive Time: uint64(header.Time.UTC().Unix()), Extra: []byte{}, MixDigest: common.Hash{}, @@ -96,7 +96,7 @@ func BlockMaxGasFromConsensusParams(goCtx context.Context, clientCtx client.Cont } resConsParams, err := tmrpcClient.ConsensusParams(goCtx, &blockHeight) if err != nil { - // #nosec G701 always in range + // #nosec G115 always in range return int64(^uint32(0)), err } @@ -105,7 +105,7 @@ func BlockMaxGasFromConsensusParams(goCtx context.Context, clientCtx client.Cont // Sets gas limit to max uint32 to not error with javascript dev tooling // This -1 value indicating no block gas limit is set to max uint64 with geth hexutils // which errors certain javascript dev tooling which only supports up to 53 bits - // #nosec G701 always in range + // #nosec G115 always in range gasLimit = int64(^uint32(0)) } @@ -196,6 +196,7 @@ func NewRPCTransaction( } v, r, s := tx.RawSignatureValues() result := &RPCTransaction{ + // #nosec G115 uint8 -> uint64 false positive Type: hexutil.Uint64(tx.Type()), From: from, Gas: hexutil.Uint64(tx.Gas()), diff --git a/scripts/cosmos-gosec.sh b/scripts/cosmos-gosec.sh deleted file mode 100644 index 0863a94cce..0000000000 --- a/scripts/cosmos-gosec.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -# Install gosec -go install github.com/cosmos/gosec/v2/cmd/gosec@latest - -# Run gosec -gosec -include=G701,G703,G704 ./... \ No newline at end of file diff --git a/scripts/gosec.sh b/scripts/gosec.sh new file mode 100644 index 0000000000..2b831a35e9 --- /dev/null +++ b/scripts/gosec.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +docker run -it --rm -w /node -v "$(pwd):/node" ghcr.io/zeta-chain/gosec:2.21.0-zeta -exclude-generated -exclude-dir testutil ./... \ No newline at end of file diff --git a/testutil/nullify/nullify.go b/testutil/nullify/nullify.go index 5812c1b3c5..94c3070ea7 100644 --- a/testutil/nullify/nullify.go +++ b/testutil/nullify/nullify.go @@ -24,7 +24,7 @@ func Fill(x interface{}) interface{} { case reflect.Slice: for i := 0; i < v.Len(); i++ { obj := v.Index(i) - //#nosec G103 + //#nosec G103 G115 objPt := reflect.NewAt(obj.Type(), unsafe.Pointer(obj.UnsafeAddr())).Interface() objPt = Fill(objPt) obj.Set(reflect.ValueOf(objPt)) @@ -53,7 +53,7 @@ func Fill(x interface{}) interface{} { s := reflect.ValueOf(mathUint).Elem() f.Set(s) default: - //#nosec G103 + //#nosec G103 G115 objPt := reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())).Interface() s := Fill(objPt) f.Set(reflect.ValueOf(s)) diff --git a/x/crosschain/client/cli/cli_whitelist_erc20.go b/x/crosschain/client/cli/cli_whitelist_erc20.go index 6941ece36d..79647bc0db 100644 --- a/x/crosschain/client/cli/cli_whitelist_erc20.go +++ b/x/crosschain/client/cli/cli_whitelist_erc20.go @@ -50,7 +50,7 @@ func CmdWhitelistERC20() *cobra.Command { chainID, name, symbol, - // #nosec G701 always in range + // #nosec G115 always in range uint32(decimals), gasLimit, ) diff --git a/x/crosschain/client/cli/query_cctx.go b/x/crosschain/client/cli/query_cctx.go index 064d660830..06d162c491 100644 --- a/x/crosschain/client/cli/query_cctx.go +++ b/x/crosschain/client/cli/query_cctx.go @@ -65,7 +65,7 @@ func CmdPendingCctx() *cobra.Command { params := &types.QueryListPendingCctxRequest{ ChainId: chainID, - // #nosec G701 bit size verified + // #nosec G115 bit size verified Limit: uint32(limit), } diff --git a/x/crosschain/client/querytests/cctx.go b/x/crosschain/client/querytests/cctx.go index c9c9b37bac..0fe177e6ca 100644 --- a/x/crosschain/client/querytests/cctx.go +++ b/x/crosschain/client/querytests/cctx.go @@ -35,7 +35,7 @@ func (s *CliTestSuite) TestListCCTX() { s.Run("ByOffset", func() { step := 2 for i := 0; i < len(objs); i += step { - // #nosec G701 always in range + // #nosec G115 always in range args := request(nil, uint64(i), uint64(step), false) out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListSend(), args) s.Require().NoError(err) @@ -50,7 +50,7 @@ func (s *CliTestSuite) TestListCCTX() { step := 2 var next []byte for i := 0; i < len(objs); i += step { - // #nosec G701 always in range + // #nosec G115 always in range args := request(next, 0, uint64(step), false) out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListSend(), args) s.Require().NoError(err) @@ -69,7 +69,7 @@ func (s *CliTestSuite) TestListCCTX() { var resp types.QueryAllCctxResponse s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) s.Require().NoError(err) - // #nosec G701 always in range + // #nosec G115 always in range s.Require().Equal(len(objs), int(resp.Pagination.Total)) s.Require().Equal(objs, resp.CrossChainTx) }) diff --git a/x/crosschain/client/querytests/gas_price.go b/x/crosschain/client/querytests/gas_price.go index de12a9d34b..7a0c6422c8 100644 --- a/x/crosschain/client/querytests/gas_price.go +++ b/x/crosschain/client/querytests/gas_price.go @@ -80,7 +80,7 @@ func (s *CliTestSuite) TestListGasPrice() { s.Run("ByOffset", func() { step := 2 for i := 0; i < len(objs); i += step { - // #nosec G701 always in range + // #nosec G115 always in range args := request(nil, uint64(i), uint64(step), false) out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListGasPrice(), args) s.Require().NoError(err) @@ -95,7 +95,7 @@ func (s *CliTestSuite) TestListGasPrice() { step := 2 var next []byte for i := 0; i < len(objs); i += step { - // #nosec G701 always in range + // #nosec G115 always in range args := request(next, 0, uint64(step), false) out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListGasPrice(), args) s.Require().NoError(err) @@ -108,14 +108,14 @@ func (s *CliTestSuite) TestListGasPrice() { } }) s.Run("Total", func() { - // #nosec G701 always in range + // #nosec G115 always in range args := request(nil, 0, uint64(len(objs)), true) out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListGasPrice(), args) s.Require().NoError(err) var resp types.QueryAllGasPriceResponse s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) s.Require().NoError(err) - // #nosec G701 always in range + // #nosec G115 always in range s.Require().Equal(len(objs), int(resp.Pagination.Total)) s.Require().Equal(objs, resp.GasPrice) }) diff --git a/x/crosschain/client/querytests/inbound_hash.go b/x/crosschain/client/querytests/inbound_hash.go index 5c88f99141..9f6d48ef53 100644 --- a/x/crosschain/client/querytests/inbound_hash.go +++ b/x/crosschain/client/querytests/inbound_hash.go @@ -90,7 +90,7 @@ func (s *CliTestSuite) TestListInboundHashToCctx() { s.Run("ByOffset", func() { step := 2 for i := 0; i < len(objs); i += step { - // #nosec G701 always in range + // #nosec G115 always in range args := request(nil, uint64(i), uint64(step), false) out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListInboundHashToCctx(), args) s.Require().NoError(err) @@ -106,7 +106,7 @@ func (s *CliTestSuite) TestListInboundHashToCctx() { step := 2 var next []byte for i := 0; i < len(objs); i += step { - // #nosec G701 always in range + // #nosec G115 always in range args := request(next, 0, uint64(step), false) out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListInboundHashToCctx(), args) s.Require().NoError(err) @@ -120,7 +120,7 @@ func (s *CliTestSuite) TestListInboundHashToCctx() { } }) s.Run("Total", func() { - // #nosec G701 always in range + // #nosec G115 always in range args := request(nil, 0, uint64(len(objs)), true) out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListInboundHashToCctx(), args) s.Require().NoError(err) @@ -128,7 +128,7 @@ func (s *CliTestSuite) TestListInboundHashToCctx() { s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) s.Require().NoError(err) // saving CCTX also adds a new mapping - // #nosec G701 always in range + // #nosec G115 always in range s.Require().Equal(len(objs)+cctxCount, int(resp.Pagination.Total)) s.Require().ElementsMatch(nullify.Fill(objs), nullify.Fill(resp.InboundHashToCctx), diff --git a/x/crosschain/client/querytests/inbound_tracker.go b/x/crosschain/client/querytests/inbound_tracker.go index d3c1b1a0de..162b552865 100644 --- a/x/crosschain/client/querytests/inbound_tracker.go +++ b/x/crosschain/client/querytests/inbound_tracker.go @@ -50,7 +50,7 @@ func (s *CliTestSuite) TestListInboundTrackersByChain() { s.Run("ByOffset", func() { step := 2 for i := 0; i < len(objs); i += step { - // #nosec G701 always positive + // #nosec G115 always positive args := request(nil, uint64(i), uint64(step), false, 5) out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListInboundTrackerByChain(), args) s.Require().NoError(err) @@ -66,7 +66,7 @@ func (s *CliTestSuite) TestListInboundTrackersByChain() { step := 2 var next []byte for i := 0; i < len(objs); i += step { - // #nosec G701 always positive + // #nosec G115 always positive args := request(next, 0, uint64(step), false, 5) out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListInboundTrackerByChain(), args) s.Require().NoError(err) diff --git a/x/crosschain/client/querytests/last_block_height.go b/x/crosschain/client/querytests/last_block_height.go index 82ba19ff1f..b7bf12f015 100644 --- a/x/crosschain/client/querytests/last_block_height.go +++ b/x/crosschain/client/querytests/last_block_height.go @@ -80,7 +80,7 @@ func (s *CliTestSuite) TestListLastBlockHeight() { s.Run("ByOffset", func() { step := 2 for i := 0; i < len(objs); i += step { - // #nosec G701 always in range + // #nosec G115 always in range args := request(nil, uint64(i), uint64(step), false) out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListLastBlockHeight(), args) s.Require().NoError(err) @@ -95,7 +95,7 @@ func (s *CliTestSuite) TestListLastBlockHeight() { step := 2 var next []byte for i := 0; i < len(objs); i += step { - // #nosec G701 always in range + // #nosec G115 always in range args := request(next, 0, uint64(step), false) out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListLastBlockHeight(), args) s.Require().NoError(err) @@ -108,14 +108,14 @@ func (s *CliTestSuite) TestListLastBlockHeight() { } }) s.Run("Total", func() { - // #nosec G701 always in range + // #nosec G115 always in range args := request(nil, 0, uint64(len(objs)), true) out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListLastBlockHeight(), args) s.Require().NoError(err) var resp types.QueryAllLastBlockHeightResponse s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) s.Require().NoError(err) - // #nosec G701 always in range + // #nosec G115 always in range s.Require().Equal(len(objs), int(resp.Pagination.Total)) s.Require().Equal(objs, resp.LastBlockHeight) }) diff --git a/x/crosschain/client/querytests/outbound_tracker.go b/x/crosschain/client/querytests/outbound_tracker.go index 0be4b74178..6a6ded0b6d 100644 --- a/x/crosschain/client/querytests/outbound_tracker.go +++ b/x/crosschain/client/querytests/outbound_tracker.go @@ -33,7 +33,7 @@ func (s *CliTestSuite) TestListOutboundTracker() { s.Run("ByOffset", func() { step := 2 for i := 0; i < len(objs); i += step { - // #nosec G701 always in range + // #nosec G115 always in range args := request(nil, uint64(i), uint64(step), false) out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListOutboundTracker(), args) s.Require().NoError(err) @@ -49,7 +49,7 @@ func (s *CliTestSuite) TestListOutboundTracker() { step := 2 var next []byte for i := 0; i < len(objs); i += step { - // #nosec G701 always in range + // #nosec G115 always in range args := request(next, 0, uint64(step), false) out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListOutboundTracker(), args) s.Require().NoError(err) @@ -64,14 +64,14 @@ func (s *CliTestSuite) TestListOutboundTracker() { } }) s.Run("Total", func() { - // #nosec G701 always in range + // #nosec G115 always in range args := request(nil, 0, uint64(len(objs)), true) out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListOutboundTracker(), args) s.Require().NoError(err) var resp types.QueryAllOutboundTrackerResponse s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) s.Require().NoError(err) - // #nosec G701 always in range + // #nosec G115 always in range s.Require().Equal(len(objs), int(resp.Pagination.Total)) s.Require().ElementsMatch(nullify.Fill(objs), nullify.Fill(resp.OutboundTracker), diff --git a/x/crosschain/keeper/cctx.go b/x/crosschain/keeper/cctx.go index 8c660fb99b..b89b517653 100644 --- a/x/crosschain/keeper/cctx.go +++ b/x/crosschain/keeper/cctx.go @@ -26,7 +26,7 @@ func (k Keeper) SetCctxAndNonceToCctxAndInboundHashToCctx(ctx sdk.Context, cctx cctx.CctxStatus.Status == types.CctxStatus_PendingRevert { k.GetObserverKeeper().SetNonceToCctx(ctx, observerTypes.NonceToCctx{ ChainId: cctx.GetCurrentOutboundParam().ReceiverChainId, - // #nosec G701 always in range + // #nosec G115 always in range Nonce: int64(cctx.GetCurrentOutboundParam().TssNonce), CctxIndex: cctx.Index, Tss: tss.TssPubkey, diff --git a/x/crosschain/keeper/cctx_orchestrator_validate_outbound.go b/x/crosschain/keeper/cctx_orchestrator_validate_outbound.go index a1a25e9988..1391bbaf9d 100644 --- a/x/crosschain/keeper/cctx_orchestrator_validate_outbound.go +++ b/x/crosschain/keeper/cctx_orchestrator_validate_outbound.go @@ -294,7 +294,7 @@ func (k Keeper) validateFailedOutboundObserversForZEVM(ctx sdk.Context, cctx *ty hash := tmbytes.HexBytes(tmtypes.Tx(ctx.TxBytes()).Hash()) ethTxHash := ethcommon.BytesToHash(hash) cctx.GetCurrentOutboundParam().Hash = ethTxHash.String() - // #nosec G701 always positive + // #nosec G115 always positive cctx.GetCurrentOutboundParam().ObservedExternalHeight = uint64(ctx.BlockHeight()) } cctx.GetCurrentOutboundParam().TxFinalizationStatus = types.TxFinalizationStatus_Executed diff --git a/x/crosschain/keeper/cctx_utils.go b/x/crosschain/keeper/cctx_utils.go index 8996ad0b5a..7474cb74b5 100644 --- a/x/crosschain/keeper/cctx_utils.go +++ b/x/crosschain/keeper/cctx_utils.go @@ -50,7 +50,7 @@ func (k Keeper) SetObserverOutboundInfo(ctx sdk.Context, receiveChainID int64, c ) } - // #nosec G701 always in range + // #nosec G115 always in range if p.NonceHigh != int64(nonce.Nonce) { return cosmoserrors.Wrap( types.ErrNonceMismatch, diff --git a/x/crosschain/keeper/evm_deposit.go b/x/crosschain/keeper/evm_deposit.go index 0b60a3c9e6..b1e55e3f6f 100644 --- a/x/crosschain/keeper/evm_deposit.go +++ b/x/crosschain/keeper/evm_deposit.go @@ -37,7 +37,7 @@ func (k Keeper) HandleEVMDeposit(ctx sdk.Context, cctx *types.CrossChainTx) (boo hash := tmbytes.HexBytes(tmtypes.Tx(ctx.TxBytes()).Hash()) ethTxHash = ethcommon.BytesToHash(hash) cctx.GetCurrentOutboundParam().Hash = ethTxHash.String() - // #nosec G701 always positive + // #nosec G115 always positive cctx.GetCurrentOutboundParam().ObservedExternalHeight = uint64(ctx.BlockHeight()) } diff --git a/x/crosschain/keeper/grpc_query_cctx.go b/x/crosschain/keeper/grpc_query_cctx.go index edfeaabc3d..15c4420246 100644 --- a/x/crosschain/keeper/grpc_query_cctx.go +++ b/x/crosschain/keeper/grpc_query_cctx.go @@ -88,7 +88,7 @@ func (k Keeper) CctxByNonce( if !found { return nil, status.Error(codes.Internal, "tss not found") } - // #nosec G701 always in range + // #nosec G115 always in range cctx, err := getCctxByChainIDAndNonce(k, ctx, tss.TssPubkey, req.ChainID, int64(req.Nonce)) if err != nil { return nil, err @@ -126,7 +126,7 @@ func (k Keeper) ListPendingCctx( cctxs := make([]*types.CrossChainTx, 0) maxCCTXsReached := func() bool { - // #nosec G701 len always positive + // #nosec G115 len always positive return uint32(len(cctxs)) >= limit } @@ -155,7 +155,7 @@ func (k Keeper) ListPendingCctx( } // add the pending nonces to the total pending - // #nosec G701 always in range + // #nosec G115 always in range totalPending += uint64(pendingNonces.NonceHigh - pendingNonces.NonceLow) // now query the pending nonces that we know are pending diff --git a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go index d2eb8d85ea..f2406c7a13 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go @@ -51,13 +51,13 @@ func (k Keeper) RateLimiterInput( // the `limit` of pending result is reached or not maxCCTXsReached := func(cctxs []*types.CrossChainTx) bool { - // #nosec G701 len always positive + // #nosec G115 len always positive return uint32(len(cctxs)) > limit } // if a cctx falls within the rate limiter window isCCTXInWindow := func(cctx *types.CrossChainTx) bool { - // #nosec G701 checked positive + // #nosec G115 checked positive return cctx.InboundParams.ObservedExternalHeight >= uint64(leftWindowBoundary) } @@ -69,7 +69,7 @@ func (k Keeper) RateLimiterInput( // it is a past cctx if its nonce < `nonceLow`, isPastCctx := func(cctx *types.CrossChainTx, nonceLow int64) bool { - // #nosec G701 always positive + // #nosec G115 always positive return cctx.GetCurrentOutboundParam().TssNonce < uint64(nonceLow) } @@ -97,7 +97,7 @@ func (k Keeper) RateLimiterInput( if err != nil { return nil, err } - // #nosec G701 len always in range + // #nosec G115 len always in range cctxHeight := int64(cctx.InboundParams.ObservedExternalHeight) if lowestPendingCctxHeight == 0 || cctxHeight < lowestPendingCctxHeight { lowestPendingCctxHeight = cctxHeight @@ -259,13 +259,13 @@ func (k Keeper) ListPendingCctxWithinRateLimit( // the criteria to stop adding cctxs to the rpc response maxCCTXsReached := func(cctxs []*types.CrossChainTx) bool { - // #nosec G701 len always positive + // #nosec G115 len always positive return uint32(len(cctxs)) >= limit } // if a cctx falls within the rate limiter window isCCTXInWindow := func(cctx *types.CrossChainTx) bool { - // #nosec G701 checked positive + // #nosec G115 checked positive return cctx.InboundParams.ObservedExternalHeight >= uint64(leftWindowBoundary) } @@ -291,7 +291,7 @@ func (k Keeper) ListPendingCctxWithinRateLimit( if err != nil { return nil, err } - // #nosec G701 len always in range + // #nosec G115 len always in range cctxHeight := int64(cctx.InboundParams.ObservedExternalHeight) if lowestPendingCctxHeight == 0 || cctxHeight < lowestPendingCctxHeight { lowestPendingCctxHeight = cctxHeight @@ -371,7 +371,7 @@ func (k Keeper) ListPendingCctxWithinRateLimit( for _, chain := range foreignChains { pendingNonces := pendingNoncesMap[chain.ChainId] - // #nosec G701 always in range + // #nosec G115 always in range totalPending += uint64(pendingNonces.NonceHigh - pendingNonces.NonceLow) // query the pending cctxs in range [NonceLow, NonceHigh) diff --git a/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go b/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go index 409031e121..8dedb591e2 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go @@ -71,7 +71,7 @@ func setCctxsInKeeper( k.SetCrossChainTx(ctx, *cctx) zk.ObserverKeeper.SetNonceToCctx(ctx, observertypes.NonceToCctx{ ChainId: cctx.GetCurrentOutboundParam().ReceiverChainId, - // #nosec G701 always in range for tests + // #nosec G115 always in range for tests Nonce: int64(cctx.GetCurrentOutboundParam().TssNonce), CctxIndex: cctx.Index, Tss: tss.TssPubkey, diff --git a/x/crosschain/keeper/grpc_query_zeta_conversion_rate.go b/x/crosschain/keeper/grpc_query_zeta_conversion_rate.go index 7abf0f5401..b1c1ca1dcf 100644 --- a/x/crosschain/keeper/grpc_query_zeta_conversion_rate.go +++ b/x/crosschain/keeper/grpc_query_zeta_conversion_rate.go @@ -45,7 +45,7 @@ func (k Keeper) ConvertGasToZeta( return &types.QueryConvertGasToZetaResponse{ OutboundGasInZeta: outTxGasFeeInZeta.String(), ProtocolFeeInZeta: types.GetProtocolFee().String(), - // #nosec G701 always positive + // #nosec G115 always positive ZetaBlockHeight: uint64(ctx.BlockHeight()), }, nil } diff --git a/x/crosschain/keeper/grpc_query_zeta_conversion_rate_test.go b/x/crosschain/keeper/grpc_query_zeta_conversion_rate_test.go index 275440cfed..4afc8378f7 100644 --- a/x/crosschain/keeper/grpc_query_zeta_conversion_rate_test.go +++ b/x/crosschain/keeper/grpc_query_zeta_conversion_rate_test.go @@ -105,7 +105,7 @@ func TestKeeper_ConvertGasToZeta(t *testing.T) { require.Equal(t, &types.QueryConvertGasToZetaResponse{ OutboundGasInZeta: "5", ProtocolFeeInZeta: types.GetProtocolFee().String(), - // #nosec G701 always positive + // #nosec G115 always positive ZetaBlockHeight: uint64(ctx.BlockHeight()), }, res) }) diff --git a/x/crosschain/keeper/msg_server_vote_gas_price.go b/x/crosschain/keeper/msg_server_vote_gas_price.go index a5fbaf4c0c..040c85fd75 100644 --- a/x/crosschain/keeper/msg_server_vote_gas_price.go +++ b/x/crosschain/keeper/msg_server_vote_gas_price.go @@ -63,7 +63,7 @@ func (k msgServer) VoteGasPrice( } // recompute the median gas price mi := medianOfArray(gasPrice.Prices) - // #nosec G701 always positive + // #nosec G115 always positive gasPrice.MedianIndex = uint64(mi) } k.SetGasPrice(ctx, gasPrice) diff --git a/x/crosschain/keeper/msg_server_vote_inbound_tx.go b/x/crosschain/keeper/msg_server_vote_inbound_tx.go index ecf87ef9fd..e597509ab1 100644 --- a/x/crosschain/keeper/msg_server_vote_inbound_tx.go +++ b/x/crosschain/keeper/msg_server_vote_inbound_tx.go @@ -122,7 +122,7 @@ func (k Keeper) SaveObservedInboundInformation(ctx sdk.Context, cctx *types.Cros cctx.GetInboundParams().ObservedHash, cctx.GetInboundParams().SenderChainId, eventIndex) - // #nosec G701 always positive + // #nosec G115 always positive cctx.InboundParams.FinalizedZetaHeight = uint64(ctx.BlockHeight()) cctx.InboundParams.TxFinalizationStatus = types.TxFinalizationStatus_Executed k.RemoveInboundTrackerIfExists(ctx, cctx.InboundParams.SenderChainId, cctx.InboundParams.ObservedHash) diff --git a/x/crosschain/keeper/msg_server_vote_outbound_tx.go b/x/crosschain/keeper/msg_server_vote_outbound_tx.go index 3445d32cfb..8cb4a2ce6d 100644 --- a/x/crosschain/keeper/msg_server_vote_outbound_tx.go +++ b/x/crosschain/keeper/msg_server_vote_outbound_tx.go @@ -195,7 +195,7 @@ func (k Keeper) SaveOutbound(ctx sdk.Context, cctx *types.CrossChainTx, ballotIn outTxTssNonce := cctx.GetCurrentOutboundParam().TssNonce cctx.GetCurrentOutboundParam().BallotIndex = ballotIndex - // #nosec G701 always in range + // #nosec G115 always in range k.GetObserverKeeper().RemoveFromPendingNonces(ctx, tssPubkey, receiverChain, int64(outTxTssNonce)) k.RemoveOutboundTrackerFromStore(ctx, receiverChain, outTxTssNonce) ctx.Logger(). diff --git a/x/crosschain/keeper/msg_server_whitelist_erc20.go b/x/crosschain/keeper/msg_server_whitelist_erc20.go index e3ace26baa..8666bcbe82 100644 --- a/x/crosschain/keeper/msg_server_whitelist_erc20.go +++ b/x/crosschain/keeper/msg_server_whitelist_erc20.go @@ -76,7 +76,7 @@ func (k msgServer) WhitelistERC20( tmpCtx, msg.Name, msg.Symbol, - // #nosec G701 always in range + // #nosec G115 always in range uint8(msg.Decimals), chain.ChainId, coin.CoinType_ERC20, @@ -176,7 +176,7 @@ func (k msgServer) WhitelistERC20( Name: msg.Name, Symbol: msg.Symbol, CoinType: coin.CoinType_ERC20, - // #nosec G701 always positive + // #nosec G115 always positive GasLimit: uint64(msg.GasLimit), } k.fungibleKeeper.SetForeignCoins(ctx, foreignCoin) diff --git a/x/crosschain/migrations/v2/migrate.go b/x/crosschain/migrations/v2/migrate.go index 4fad2501bb..4baa2e8baf 100644 --- a/x/crosschain/migrations/v2/migrate.go +++ b/x/crosschain/migrations/v2/migrate.go @@ -70,7 +70,7 @@ func MigrateStore( totalObserverCountCurrentBlock := allObservers.LenUint() observerKeeper.SetLastObserverCount(ctx, &observerTypes.LastObserverCount{ - // #nosec G701 always positive + // #nosec G115 always positive Count: totalObserverCountCurrentBlock, LastChangeHeight: ctx.BlockHeight(), }) diff --git a/x/crosschain/migrations/v5/migrate.go b/x/crosschain/migrations/v5/migrate.go index bb650991f0..73f194cdba 100644 --- a/x/crosschain/migrations/v5/migrate.go +++ b/x/crosschain/migrations/v5/migrate.go @@ -62,9 +62,9 @@ func ResetTestnetNonce( if !found { continue } - // #nosec G701 always in range for testnet chains + // #nosec G115 always in range for testnet chains pn.NonceLow = int64(chainNonce.nonceLow) - // #nosec G701 always in range for testnet chains + // #nosec G115 always in range for testnet chains pn.NonceHigh = int64(chainNonce.nonceHigh) observerKeeper.SetPendingNonces(ctx, pn) } diff --git a/x/fungible/client/cli/tx_deploy_fungible_coin_zrc_4.go b/x/fungible/client/cli/tx_deploy_fungible_coin_zrc_4.go index 9274d2c537..6c49a5824d 100644 --- a/x/fungible/client/cli/tx_deploy_fungible_coin_zrc_4.go +++ b/x/fungible/client/cli/tx_deploy_fungible_coin_zrc_4.go @@ -46,7 +46,7 @@ func CmdDeployFungibleCoinZRC4() *cobra.Command { clientCtx.GetFromAddress().String(), argERC20, argForeignChain, - // #nosec G701 parsed in range + // #nosec G115 parsed in range uint32(argDecimals), argName, argSymbol, diff --git a/x/fungible/keeper/evm.go b/x/fungible/keeper/evm.go index 5a9d950b14..8deaaf0e1b 100644 --- a/x/fungible/keeper/evm.go +++ b/x/fungible/keeper/evm.go @@ -130,7 +130,7 @@ func (k Keeper) DeployZRC20Contract( symbol, // symbol decimals, // decimals big.NewInt(chain.ChainId), // chainID - // #nosec G701 always in range + // #nosec G115 always in range uint8(coinType), // coinType: 0: Zeta 1: gas 2 ERC20 gasLimit, //gas limit for transfer; 21k for gas asset; around 70k for ERC20 common.HexToAddress(system.SystemContract), @@ -147,6 +147,7 @@ func (k Keeper) DeployZRC20Contract( coin.CoinType = coinType coin.Name = name coin.Symbol = symbol + // #nosec G115 uint8 -> uint32 false positive coin.Decimals = uint32(decimals) coin.Asset = erc20Contract coin.Zrc20ContractAddress = contractAddr.Hex() diff --git a/x/fungible/keeper/gas_coin_and_pool.go b/x/fungible/keeper/gas_coin_and_pool.go index 8e01e0dc49..58fbcff1d0 100644 --- a/x/fungible/keeper/gas_coin_and_pool.go +++ b/x/fungible/keeper/gas_coin_and_pool.go @@ -76,7 +76,7 @@ func (k Keeper) SetupChainGasCoinAndPool( return ethcommon.Address{}, err } amount := big.NewInt(10) - // #nosec G701 always in range + // #nosec G115 always in range amount.Exp(amount, big.NewInt(int64(decimals-1)), nil) amountAZeta := big.NewInt(1e17) diff --git a/x/fungible/keeper/msg_server_deploy_fungible_coin_zrc20.go b/x/fungible/keeper/msg_server_deploy_fungible_coin_zrc20.go index 46ecb550d0..9461080fbc 100644 --- a/x/fungible/keeper/msg_server_deploy_fungible_coin_zrc20.go +++ b/x/fungible/keeper/msg_server_deploy_fungible_coin_zrc20.go @@ -50,7 +50,7 @@ func (k msgServer) DeployFungibleCoinZRC20( } if msg.CoinType == coin.CoinType_Gas { - // #nosec G701 always in range + // #nosec G115 always in range address, err = k.SetupChainGasCoinAndPool( ctx, msg.ForeignChainId, @@ -63,7 +63,7 @@ func (k msgServer) DeployFungibleCoinZRC20( return nil, cosmoserrors.Wrapf(err, "failed to setupChainGasCoinAndPool") } } else { - // #nosec G701 always in range + // #nosec G115 always in range address, err = k.DeployZRC20Contract(ctx, msg.Name, msg.Symbol, uint8(msg.Decimals), msg.ForeignChainId, msg.CoinType, msg.ERC20, big.NewInt(msg.GasLimit)) if err != nil { return nil, err @@ -77,7 +77,7 @@ func (k msgServer) DeployFungibleCoinZRC20( Contract: address.String(), Name: msg.Name, Symbol: msg.Symbol, - // #nosec G701 always in range + // #nosec G115 always in range Decimals: int64(msg.Decimals), CoinType: msg.CoinType, Erc20: msg.ERC20, diff --git a/x/observer/abci.go b/x/observer/abci.go index 1cc55eeb5d..d57fbf5894 100644 --- a/x/observer/abci.go +++ b/x/observer/abci.go @@ -22,7 +22,7 @@ func BeginBlocker(ctx sdk.Context, k keeper.Keeper) { return } totalObserverCountCurrentBlock := allObservers.LenUint() - // #nosec G701 always in range + // #nosec G115 always in range if totalObserverCountCurrentBlock == lastBlockObserverCount.Count { return } @@ -31,11 +31,11 @@ func BeginBlocker(ctx sdk.Context, k keeper.Keeper) { for _, observer := range allObservers.ObserverList { ctx.Logger().Error("Observer : ", observer) } - // #nosec G701 always in range + // #nosec G115 always in range k.DisableInboundOnly(ctx) k.SetKeygen(ctx, types.Keygen{BlockNumber: math.MaxInt64}) - // #nosec G701 always positive + // #nosec G115 always positive k.SetLastObserverCount( ctx, &types.LastObserverCount{Count: totalObserverCountCurrentBlock, LastChangeHeight: ctx.BlockHeight()}, diff --git a/x/observer/client/cli/tx_update_gas_price_increase_flags.go b/x/observer/client/cli/tx_update_gas_price_increase_flags.go index 5569104bcf..98d156435a 100644 --- a/x/observer/client/cli/tx_update_gas_price_increase_flags.go +++ b/x/observer/client/cli/tx_update_gas_price_increase_flags.go @@ -46,11 +46,11 @@ func CmdUpdateGasPriceIncreaseFlags() *cobra.Command { gasPriceIncreaseFlags := types.GasPriceIncreaseFlags{ EpochLength: epochLength, RetryInterval: time.Duration(retryInterval), - // #nosec G701 bitsize set to 32 + // #nosec G115 bitsize set to 32 GasPriceIncreasePercent: uint32(gasPriceIncreasePercent), - // #nosec G701 bitsize set to 32 + // #nosec G115 bitsize set to 32 GasPriceIncreaseMax: uint32(gasPriceIncreaseMax), - // #nosec G701 bitsize set to 32 + // #nosec G115 bitsize set to 32 MaxPendingCctxs: uint32(maxPendingCctxs)} msg := types.NewMsgUpdateGasPriceIncreaseFlags(clientCtx.GetFromAddress().String(), gasPriceIncreaseFlags) err = msg.ValidateBasic() diff --git a/x/observer/client/cli/tx_update_observer.go b/x/observer/client/cli/tx_update_observer.go index c04cfa2664..47b1bd5259 100644 --- a/x/observer/client/cli/tx_update_observer.go +++ b/x/observer/client/cli/tx_update_observer.go @@ -27,7 +27,7 @@ func CmdUpdateObserver() *cobra.Command { if err != nil { return err } - // #nosec G701 parsed in range + // #nosec G115 parsed in range updateReason, err := parseUpdateReason(int32(updateReasonInt)) if err != nil { return err diff --git a/x/observer/client/querytests/chain_nonces.go b/x/observer/client/querytests/chain_nonces.go index 1cee363051..c9c08039d9 100644 --- a/x/observer/client/querytests/chain_nonces.go +++ b/x/observer/client/querytests/chain_nonces.go @@ -80,7 +80,7 @@ func (s *CliTestSuite) TestListChainNonces() { s.Run("ByOffset", func() { step := 2 for i := 0; i < len(objs); i += step { - // #nosec G701 always in range + // #nosec G115 always in range args := request(nil, uint64(i), uint64(step), false) out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListChainNonces(), args) s.Require().NoError(err) @@ -95,7 +95,7 @@ func (s *CliTestSuite) TestListChainNonces() { step := 2 var next []byte for i := 0; i < len(objs); i += step { - // #nosec G701 always in range + // #nosec G115 always in range args := request(next, 0, uint64(step), false) out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListChainNonces(), args) s.Require().NoError(err) @@ -108,14 +108,14 @@ func (s *CliTestSuite) TestListChainNonces() { } }) s.Run("Total", func() { - // #nosec G701 always in range + // #nosec G115 always in range args := request(nil, 0, uint64(len(objs)), true) out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListChainNonces(), args) s.Require().NoError(err) var resp types.QueryAllChainNoncesResponse s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) s.Require().NoError(err) - // #nosec G701 always in range + // #nosec G115 always in range s.Require().Equal(len(objs), int(resp.Pagination.Total)) s.Require().Equal(objs, resp.ChainNonces) }) diff --git a/x/observer/keeper/msg_server_reset_chain_nonces.go b/x/observer/keeper/msg_server_reset_chain_nonces.go index 8cd61c97d6..2e1ba57607 100644 --- a/x/observer/keeper/msg_server_reset_chain_nonces.go +++ b/x/observer/keeper/msg_server_reset_chain_nonces.go @@ -36,9 +36,9 @@ func (k msgServer) ResetChainNonces( chainNonce := types.ChainNonces{ Index: chain.ChainName.String(), ChainId: chain.ChainId, - // #nosec G701 always positive + // #nosec G115 always positive Nonce: uint64(msg.ChainNonceHigh), - // #nosec G701 always positive + // #nosec G115 always positive FinalizedHeight: uint64(ctx.BlockHeight()), } k.SetChainNonces(ctx, chainNonce) diff --git a/x/observer/keeper/pending_nonces.go b/x/observer/keeper/pending_nonces.go index 6eb8dd2ae1..2d166bf0a6 100644 --- a/x/observer/keeper/pending_nonces.go +++ b/x/observer/keeper/pending_nonces.go @@ -91,7 +91,7 @@ func (k Keeper) SetTssAndUpdateNonce(ctx sdk.Context, tss types.TSS) { Index: chain.ChainName.String(), ChainId: chain.ChainId, Nonce: 0, - // #nosec G701 always positive + // #nosec G115 always positive FinalizedHeight: uint64(ctx.BlockHeight()), } k.SetChainNonces(ctx, chainNonce) diff --git a/zetaclient/chains/base/logger.go b/zetaclient/chains/base/logger.go index eeffcfab3b..94579df5ae 100644 --- a/zetaclient/chains/base/logger.go +++ b/zetaclient/chains/base/logger.go @@ -58,20 +58,22 @@ func InitLogger(cfg config.Config) (Logger, error) { return DefaultLogger(), err } + level := zerolog.Level(cfg.LogLevel) + // create loggers based on configured level and format var std zerolog.Logger var compliance zerolog.Logger switch cfg.LogFormat { case "json": - std = zerolog.New(os.Stdout).Level(zerolog.Level(cfg.LogLevel)).With().Timestamp().Logger() - compliance = zerolog.New(file).Level(zerolog.Level(cfg.LogLevel)).With().Timestamp().Logger() + std = zerolog.New(os.Stdout).Level(level).With().Timestamp().Logger() + compliance = zerolog.New(file).Level(level).With().Timestamp().Logger() case "text": std = zerolog.New(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}). Level(zerolog.Level(cfg.LogLevel)). With(). Timestamp(). Logger() - compliance = zerolog.New(file).Level(zerolog.Level(cfg.LogLevel)).With().Timestamp().Logger() + compliance = zerolog.New(file).Level(level).With().Timestamp().Logger() default: std = zerolog.New(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}) compliance = zerolog.New(file).With().Timestamp().Logger() diff --git a/zetaclient/chains/base/observer.go b/zetaclient/chains/base/observer.go index edfef83629..e4cd8eb65f 100644 --- a/zetaclient/chains/base/observer.go +++ b/zetaclient/chains/base/observer.go @@ -18,7 +18,6 @@ import ( "github.com/zeta-chain/zetacore/pkg/chains" observertypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" - "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/zetaclient/metrics" clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" ) @@ -45,9 +44,6 @@ type Observer struct { // chainParams contains the dynamic chain parameters of the observed chain chainParams observertypes.ChainParams - // appContext contains context data for zetaclient & zetacore (e.g. supported chains) - appContext *context.AppContext - // zetacoreClient is the client to interact with ZetaChain zetacoreClient interfaces.ZetacoreClient @@ -87,7 +83,6 @@ type Observer struct { func NewObserver( chain chains.Chain, chainParams observertypes.ChainParams, - appContext *context.AppContext, zetacoreClient interfaces.ZetacoreClient, tss interfaces.TSSSigner, blockCacheSize int, @@ -98,7 +93,6 @@ func NewObserver( ob := Observer{ chain: chain, chainParams: chainParams, - appContext: appContext, zetacoreClient: zetacoreClient, tss: tss, lastBlock: 0, @@ -164,11 +158,6 @@ func (ob *Observer) WithChainParams(params observertypes.ChainParams) *Observer return ob } -// AppContext returns the zetacore context for the observer. -func (ob *Observer) AppContext() *context.AppContext { - return ob.appContext -} - // ZetacoreClient returns the zetacore client for the observer. func (ob *Observer) ZetacoreClient() interfaces.ZetacoreClient { return ob.zetacoreClient @@ -286,7 +275,7 @@ func (ob *Observer) StopChannel() chan struct{} { func (ob *Observer) OpenDB(dbPath string, dbName string) error { // create db path if not exist if _, err := os.Stat(dbPath); os.IsNotExist(err) { - err := os.MkdirAll(dbPath, os.ModePerm) + err := os.MkdirAll(dbPath, 0o750) if err != nil { return errors.Wrapf(err, "error creating db path: %s", dbPath) } diff --git a/zetaclient/chains/base/observer_test.go b/zetaclient/chains/base/observer_test.go index e6d5a088a9..923c4481a1 100644 --- a/zetaclient/chains/base/observer_test.go +++ b/zetaclient/chains/base/observer_test.go @@ -26,8 +26,7 @@ func createObserver(t *testing.T) *base.Observer { // constructor parameters chain := chains.Ethereum chainParams := *sample.ChainParams(chain.ChainId) - appContext := context.New(config.NewConfig(), zerolog.Nop()) - zetacoreClient := mocks.NewMockZetacoreClient() + zetacoreClient := mocks.NewZetacoreClient(t) tss := mocks.NewTSSMainnet() // create observer @@ -35,7 +34,6 @@ func createObserver(t *testing.T) *base.Observer { ob, err := base.NewObserver( chain, chainParams, - appContext, zetacoreClient, tss, base.DefaultBlockCacheSize, @@ -52,8 +50,8 @@ func TestNewObserver(t *testing.T) { // constructor parameters chain := chains.Ethereum chainParams := *sample.ChainParams(chain.ChainId) - appContext := context.New(config.NewConfig(), zerolog.Nop()) - zetacoreClient := mocks.NewMockZetacoreClient() + appContext := context.New(config.New(false), zerolog.Nop()) + zetacoreClient := mocks.NewZetacoreClient(t) tss := mocks.NewTSSMainnet() blockCacheSize := base.DefaultBlockCacheSize headersCacheSize := base.DefaultHeaderCacheSize @@ -114,7 +112,6 @@ func TestNewObserver(t *testing.T) { ob, err := base.NewObserver( tt.chain, tt.chainParams, - tt.appContext, tt.zetacoreClient, tt.tss, tt.blockCacheSize, @@ -166,7 +163,7 @@ func TestObserverGetterAndSetter(t *testing.T) { ob := createObserver(t) // update zetacore client - newZetacoreClient := mocks.NewMockZetacoreClient() + newZetacoreClient := mocks.NewZetacoreClient(t) ob = ob.WithZetacoreClient(newZetacoreClient) require.Equal(t, newZetacoreClient, ob.ZetacoreClient()) }) diff --git a/zetaclient/chains/base/signer.go b/zetaclient/chains/base/signer.go index 2ac38e048d..2545c3639a 100644 --- a/zetaclient/chains/base/signer.go +++ b/zetaclient/chains/base/signer.go @@ -5,7 +5,6 @@ import ( "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" - "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/zetaclient/metrics" ) @@ -15,8 +14,6 @@ type Signer struct { // chain contains static information about the external chain chain chains.Chain - appContext *context.AppContext - // tss is the TSS signer tss interfaces.TSSSigner @@ -32,18 +29,11 @@ type Signer struct { } // NewSigner creates a new base signer -func NewSigner( - chain chains.Chain, - appContext *context.AppContext, - tss interfaces.TSSSigner, - ts *metrics.TelemetryServer, - logger Logger, -) *Signer { +func NewSigner(chain chains.Chain, tss interfaces.TSSSigner, ts *metrics.TelemetryServer, logger Logger) *Signer { return &Signer{ - chain: chain, - appContext: appContext, - tss: tss, - ts: ts, + chain: chain, + tss: tss, + ts: ts, logger: Logger{ Std: logger.Std.With().Int64("chain", chain.ChainId).Str("module", "signer").Logger(), Compliance: logger.Compliance, @@ -62,11 +52,6 @@ func (s *Signer) WithChain(chain chains.Chain) *Signer { return s } -// AppContext returns the zetacore context for the signer -func (s *Signer) AppContext() *context.AppContext { - return s.appContext -} - // Tss returns the tss signer for the signer func (s *Signer) TSS() interfaces.TSSSigner { return s.tss diff --git a/zetaclient/chains/base/signer_test.go b/zetaclient/chains/base/signer_test.go index a0e2696b92..e35ed01792 100644 --- a/zetaclient/chains/base/signer_test.go +++ b/zetaclient/chains/base/signer_test.go @@ -3,13 +3,10 @@ package base_test import ( "testing" - "github.com/rs/zerolog" "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/zetaclient/chains/base" - "github.com/zeta-chain/zetacore/zetaclient/config" - "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/zetaclient/metrics" "github.com/zeta-chain/zetacore/zetaclient/testutils/mocks" ) @@ -18,12 +15,11 @@ import ( func createSigner(_ *testing.T) *base.Signer { // constructor parameters chain := chains.Ethereum - appContext := context.New(config.NewConfig(), zerolog.Nop()) tss := mocks.NewTSSMainnet() logger := base.DefaultLogger() // create signer - return base.NewSigner(chain, appContext, tss, nil, logger) + return base.NewSigner(chain, tss, nil, logger) } func TestNewSigner(t *testing.T) { diff --git a/zetaclient/chains/bitcoin/fee.go b/zetaclient/chains/bitcoin/fee.go index 27b3037145..aec41407d9 100644 --- a/zetaclient/chains/bitcoin/fee.go +++ b/zetaclient/chains/bitcoin/fee.go @@ -52,7 +52,7 @@ var ( // FeeRateToSatPerByte converts a fee rate in BTC/KB to sat/byte. func FeeRateToSatPerByte(rate float64) *big.Int { - // #nosec G701 always in range + // #nosec G115 always in range satPerKB := new(big.Int).SetInt64(int64(rate * btcutil.SatoshiPerBitcoin)) return new(big.Int).Div(satPerKB, big.NewInt(bytesPerKB)) } @@ -61,7 +61,7 @@ func FeeRateToSatPerByte(rate float64) *big.Int { func WiredTxSize(numInputs uint64, numOutputs uint64) uint64 { // Version 4 bytes + LockTime 4 bytes + Serialized varint size for the // number of transaction inputs and outputs. - // #nosec G701 always positive + // #nosec G115 always positive return uint64(8 + wire.VarIntSerializeSize(numInputs) + wire.VarIntSerializeSize(numOutputs)) } @@ -70,7 +70,7 @@ func EstimateOutboundSize(numInputs uint64, payees []btcutil.Address) (uint64, e if numInputs == 0 { return 0, nil } - // #nosec G701 always positive + // #nosec G115 always positive numOutputs := 2 + uint64(len(payees)) bytesWiredTx := WiredTxSize(numInputs, numOutputs) bytesInput := numInputs * bytesPerInput @@ -189,7 +189,7 @@ func CalcBlockAvgFeeRate(blockVb *btcjson.GetBlockVerboseTxResult, netParams *ch btcEarned += out.Value } } - // #nosec G701 checked above + // #nosec G115 checked above subsidy := blockchain.CalcBlockSubsidy(int32(blockVb.Height), netParams) if btcEarned < subsidy { return 0, fmt.Errorf("miner earned %d, less than subsidy %d", btcEarned, subsidy) @@ -234,7 +234,7 @@ func CalcDepositorFee( logger.Error().Err(err).Msgf("cannot calculate fee rate for block %d", blockVb.Height) } - // #nosec G701 always in range + // #nosec G115 always in range feeRate = int64(float64(feeRate) * clientcommon.BTCOutboundGasPriceMultiplier) return DepositorFee(feeRate) diff --git a/zetaclient/chains/bitcoin/fee_test.go b/zetaclient/chains/bitcoin/fee_test.go index 160bdf6093..3c754d17db 100644 --- a/zetaclient/chains/bitcoin/fee_test.go +++ b/zetaclient/chains/bitcoin/fee_test.go @@ -194,7 +194,7 @@ func TestOutboundSize2In3Out(t *testing.T) { addTxInputsOutputsAndSignTx(t, tx, privateKey, payerScript, utxosTxids, [][]byte{payeeScript}) // Estimate the tx size in vByte - // #nosec G701 always positive + // #nosec G115 always positive vError := uint64(1) // 1 vByte error tolerance vBytes := uint64(blockchain.GetTransactionWeight(btcutil.NewTx(tx)) / blockchain.WitnessScaleFactor) vBytesEstimated, err := EstimateOutboundSize(uint64(len(utxosTxids)), []btcutil.Address{payee}) @@ -218,7 +218,7 @@ func TestOutboundSize21In3Out(t *testing.T) { addTxInputsOutputsAndSignTx(t, tx, privateKey, payerScript, exampleTxids, [][]byte{payeeScript}) // Estimate the tx size in vByte - // #nosec G701 always positive + // #nosec G115 always positive vError := uint64(21 / 4) // 5 vBytes error tolerance vBytes := uint64(blockchain.GetTransactionWeight(btcutil.NewTx(tx)) / blockchain.WitnessScaleFactor) vBytesEstimated, err := EstimateOutboundSize(uint64(len(exampleTxids)), []btcutil.Address{payee}) @@ -242,7 +242,7 @@ func TestOutboundSizeXIn3Out(t *testing.T) { addTxInputsOutputsAndSignTx(t, tx, privateKey, payerScript, exampleTxids[:x], [][]byte{payeeScript}) // Estimate the tx size - // #nosec G701 always positive + // #nosec G115 always positive vError := uint64( 0.25 + float64(x)/4, ) // 1st witness incurs 0.25 more vByte error than others (which incurs 1/4 vByte per witness) @@ -335,7 +335,7 @@ func TestOutputSizeP2TR(t *testing.T) { addTxInputsOutputsAndSignTx(t, tx, privateKey, payerScript, exampleTxids[:2], payeeScripts) // Estimate the tx size in vByte - // #nosec G701 always positive + // #nosec G115 always positive vBytes := uint64(blockchain.GetTransactionWeight(btcutil.NewTx(tx)) / blockchain.WitnessScaleFactor) vBytesEstimated, err := EstimateOutboundSize(2, payees) require.NoError(t, err) @@ -355,7 +355,7 @@ func TestOutputSizeP2WSH(t *testing.T) { addTxInputsOutputsAndSignTx(t, tx, privateKey, payerScript, exampleTxids[:2], payeeScripts) // Estimate the tx size in vByte - // #nosec G701 always positive + // #nosec G115 always positive vBytes := uint64(blockchain.GetTransactionWeight(btcutil.NewTx(tx)) / blockchain.WitnessScaleFactor) vBytesEstimated, err := EstimateOutboundSize(2, payees) require.NoError(t, err) @@ -375,7 +375,7 @@ func TestOutputSizeP2SH(t *testing.T) { addTxInputsOutputsAndSignTx(t, tx, privateKey, payerScript, exampleTxids[:2], payeeScripts) // Estimate the tx size in vByte - // #nosec G701 always positive + // #nosec G115 always positive vBytes := uint64(blockchain.GetTransactionWeight(btcutil.NewTx(tx)) / blockchain.WitnessScaleFactor) vBytesEstimated, err := EstimateOutboundSize(2, payees) require.NoError(t, err) @@ -395,7 +395,7 @@ func TestOutputSizeP2PKH(t *testing.T) { addTxInputsOutputsAndSignTx(t, tx, privateKey, payerScript, exampleTxids[:2], payeeScripts) // Estimate the tx size in vByte - // #nosec G701 always positive + // #nosec G115 always positive vBytes := uint64(blockchain.GetTransactionWeight(btcutil.NewTx(tx)) / blockchain.WitnessScaleFactor) vBytesEstimated, err := EstimateOutboundSize(2, payees) require.NoError(t, err) @@ -421,7 +421,7 @@ func TestOutboundSizeBreakdown(t *testing.T) { } // calculate the average outbound size - // #nosec G701 always in range + // #nosec G115 always in range txSizeAverage := uint64((float64(txSizeTotal))/float64(len(payees)) + 0.5) // get deposit fee diff --git a/zetaclient/chains/bitcoin/observer/inbound.go b/zetaclient/chains/bitcoin/observer/inbound.go index 1d528a7c62..ce0d74062a 100644 --- a/zetaclient/chains/bitcoin/observer/inbound.go +++ b/zetaclient/chains/bitcoin/observer/inbound.go @@ -1,6 +1,7 @@ package observer import ( + "context" "encoding/hex" "fmt" "math/big" @@ -20,6 +21,7 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" "github.com/zeta-chain/zetacore/zetaclient/compliance" "github.com/zeta-chain/zetacore/zetaclient/config" + zctx "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/zetaclient/types" "github.com/zeta-chain/zetacore/zetaclient/zetacore" ) @@ -27,11 +29,16 @@ import ( // WatchInbound watches Bitcoin chain for inbounds on a ticker // It starts a ticker and run ObserveInbound // TODO(revamp): move all ticker related methods in the same file -func (ob *Observer) WatchInbound() { +func (ob *Observer) WatchInbound(ctx context.Context) error { + app, err := zctx.FromContext(ctx) + if err != nil { + return err + } + ticker, err := types.NewDynamicTicker("Bitcoin_WatchInbound", ob.GetChainParams().InboundTicker) if err != nil { ob.logger.Inbound.Error().Err(err).Msg("error creating ticker") - return + return err } defer ticker.Stop() @@ -42,26 +49,33 @@ func (ob *Observer) WatchInbound() { for { select { case <-ticker.C(): - if !ob.AppContext().IsInboundObservationEnabled(ob.GetChainParams()) { + if !app.IsInboundObservationEnabled(ob.GetChainParams()) { sampledLogger.Info(). Msgf("WatchInbound: inbound observation is disabled for chain %d", ob.Chain().ChainId) continue } - err := ob.ObserveInbound() + err := ob.ObserveInbound(ctx) if err != nil { ob.logger.Inbound.Error().Err(err).Msg("WatchInbound error observing in tx") } ticker.UpdateInterval(ob.GetChainParams().InboundTicker, ob.logger.Inbound) case <-ob.StopChannel(): ob.logger.Inbound.Info().Msgf("WatchInbound stopped for chain %d", ob.Chain().ChainId) - return + return nil } } } // ObserveInbound observes the Bitcoin chain for inbounds and post votes to zetacore // TODO(revamp): simplify this function into smaller functions -func (ob *Observer) ObserveInbound() error { +func (ob *Observer) ObserveInbound(ctx context.Context) error { + app, err := zctx.FromContext(ctx) + if err != nil { + return err + } + + zetaCoreClient := ob.ZetacoreClient() + // get and update latest block height cnt, err := ob.btcClient.GetBlockCount() if err != nil { @@ -70,7 +84,7 @@ func (ob *Observer) ObserveInbound() error { if cnt < 0 { return fmt.Errorf("observeInboundBTC: block number is negative: %d", cnt) } - // #nosec G701 checked positive + // #nosec G115 checked positive lastBlock := uint64(cnt) if lastBlock < ob.LastBlock() { return fmt.Errorf( @@ -94,7 +108,7 @@ func (ob *Observer) ObserveInbound() error { // query incoming gas asset to TSS address blockNumber := lastScanned + 1 - // #nosec G701 always in range + // #nosec G115 always in range res, err := ob.GetBlockByNumberCached(int64(blockNumber)) if err != nil { ob.logger.Inbound.Error().Err(err).Msgf("observeInboundBTC: error getting bitcoin block %d", blockNumber) @@ -108,10 +122,10 @@ func (ob *Observer) ObserveInbound() error { // https://github.com/zeta-chain/node/issues/1847 // TODO: move this logic in its own routine // https://github.com/zeta-chain/node/issues/2204 - blockHeaderVerification, found := ob.AppContext().GetBlockHeaderEnabledChains(ob.Chain().ChainId) + blockHeaderVerification, found := app.GetBlockHeaderEnabledChains(ob.Chain().ChainId) if found && blockHeaderVerification.Enabled { - // #nosec G701 always in range - err = ob.postBlockHeader(int64(blockNumber)) + // #nosec G115 always in range + err = ob.postBlockHeader(ctx, int64(blockNumber)) if err != nil { ob.logger.Inbound.Warn().Err(err).Msgf("observeInboundBTC: error posting block header %d", blockNumber) } @@ -124,7 +138,7 @@ func (ob *Observer) ObserveInbound() error { // filter incoming txs to TSS address tssAddress := ob.TSS().BTCAddress() - // #nosec G701 always positive + // #nosec G115 always positive inbounds, err := FilterAndParseIncomingTx( ob.btcClient, res.Block.Tx, @@ -145,7 +159,8 @@ func (ob *Observer) ObserveInbound() error { for _, inbound := range inbounds { msg := ob.GetInboundVoteMessageFromBtcEvent(inbound) if msg != nil { - zetaHash, ballot, err := ob.ZetacoreClient().PostVoteInbound( + zetaHash, ballot, err := zetaCoreClient.PostVoteInbound( + ctx, zetacore.PostVoteInboundGasLimit, zetacore.PostVoteInboundExecutionGasLimit, msg, @@ -175,21 +190,26 @@ func (ob *Observer) ObserveInbound() error { // WatchInboundTracker watches zetacore for bitcoin inbound trackers // TODO(revamp): move all ticker related methods in the same file -func (ob *Observer) WatchInboundTracker() { +func (ob *Observer) WatchInboundTracker(ctx context.Context) error { + app, err := zctx.FromContext(ctx) + if err != nil { + return err + } + ticker, err := types.NewDynamicTicker("Bitcoin_WatchInboundTracker", ob.GetChainParams().InboundTicker) if err != nil { ob.logger.Inbound.Err(err).Msg("error creating ticker") - return + return err } defer ticker.Stop() for { select { case <-ticker.C(): - if !ob.AppContext().IsInboundObservationEnabled(ob.GetChainParams()) { + if !app.IsInboundObservationEnabled(ob.GetChainParams()) { continue } - err := ob.ProcessInboundTrackers() + err := ob.ProcessInboundTrackers(ctx) if err != nil { ob.logger.Inbound.Error(). Err(err). @@ -198,15 +218,15 @@ func (ob *Observer) WatchInboundTracker() { ticker.UpdateInterval(ob.GetChainParams().InboundTicker, ob.logger.Inbound) case <-ob.StopChannel(): ob.logger.Inbound.Info().Msgf("WatchInboundTracker stopped for chain %d", ob.Chain().ChainId) - return + return nil } } } // ProcessInboundTrackers processes inbound trackers // TODO(revamp): move inbound tracker logic in a specific file -func (ob *Observer) ProcessInboundTrackers() error { - trackers, err := ob.ZetacoreClient().GetInboundTrackersForChain(ob.Chain().ChainId) +func (ob *Observer) ProcessInboundTrackers(ctx context.Context) error { + trackers, err := ob.ZetacoreClient().GetInboundTrackersForChain(ctx, ob.Chain().ChainId) if err != nil { return err } @@ -214,7 +234,7 @@ func (ob *Observer) ProcessInboundTrackers() error { for _, tracker := range trackers { ob.logger.Inbound.Info(). Msgf("checking tracker with hash :%s and coin-type :%s ", tracker.TxHash, tracker.CoinType) - ballotIdentifier, err := ob.CheckReceiptForBtcTxHash(tracker.TxHash, true) + ballotIdentifier, err := ob.CheckReceiptForBtcTxHash(ctx, tracker.TxHash, true) if err != nil { return err } @@ -226,7 +246,7 @@ func (ob *Observer) ProcessInboundTrackers() error { } // CheckReceiptForBtcTxHash checks the receipt for a btc tx hash -func (ob *Observer) CheckReceiptForBtcTxHash(txHash string, vote bool) (string, error) { +func (ob *Observer) CheckReceiptForBtcTxHash(ctx context.Context, txHash string, vote bool) (string, error) { hash, err := chainhash.NewHashFromStr(txHash) if err != nil { return "", err @@ -252,12 +272,12 @@ func (ob *Observer) CheckReceiptForBtcTxHash(txHash string, vote bool) (string, } depositorFee := bitcoin.CalcDepositorFee(blockVb, ob.Chain().ChainId, ob.netParams, ob.logger.Inbound) - tss, err := ob.ZetacoreClient().GetBtcTssAddress(ob.Chain().ChainId) + tss, err := ob.ZetacoreClient().GetBTCTSSAddress(ctx, ob.Chain().ChainId) if err != nil { return "", err } - // #nosec G701 always positive + // #nosec G115 always positive event, err := GetBtcEvent( ob.btcClient, *tx, @@ -285,6 +305,7 @@ func (ob *Observer) CheckReceiptForBtcTxHash(txHash string, vote bool) (string, } zetaHash, ballot, err := ob.ZetacoreClient().PostVoteInbound( + ctx, zetacore.PostVoteInboundGasLimit, zetacore.PostVoteInboundExecutionGasLimit, msg, diff --git a/zetaclient/chains/bitcoin/observer/inbound_test.go b/zetaclient/chains/bitcoin/observer/inbound_test.go index 63042e8b85..ec121bd9af 100644 --- a/zetaclient/chains/bitcoin/observer/inbound_test.go +++ b/zetaclient/chains/bitcoin/observer/inbound_test.go @@ -161,7 +161,7 @@ func TestCalcDepositorFee828440(t *testing.T) { path.Join(TestDataDir, testutils.TestDataPathBTC, "block_trimmed_8332_828440.json"), ) avgGasRate := float64(32.0) - // #nosec G701 test - always in range + // #nosec G115 test - always in range gasRate := int64(avgGasRate * clientcommon.BTCOutboundGasPriceMultiplier) dynamicFee828440 := bitcoin.DepositorFee(gasRate) diff --git a/zetaclient/chains/bitcoin/observer/observer.go b/zetaclient/chains/bitcoin/observer/observer.go index a04188da80..c9d8350781 100644 --- a/zetaclient/chains/bitcoin/observer/observer.go +++ b/zetaclient/chains/bitcoin/observer/observer.go @@ -3,6 +3,7 @@ package observer import ( "bytes" + "context" "encoding/hex" "fmt" "math" @@ -18,13 +19,14 @@ import ( "github.com/pkg/errors" "github.com/rs/zerolog" + "github.com/zeta-chain/zetacore/pkg/bg" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/proofs" observertypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/chains/base" "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin" + "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/rpc" "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" - "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/zetaclient/metrics" clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" ) @@ -112,7 +114,6 @@ func NewObserver( chain chains.Chain, btcClient interfaces.BTCRPCClient, chainParams observertypes.ChainParams, - appContext *context.AppContext, zetacoreClient interfaces.ZetacoreClient, tss interfaces.TSSSigner, dbpath string, @@ -123,7 +124,6 @@ func NewObserver( baseObserver, err := base.NewObserver( chain, chainParams, - appContext, zetacoreClient, tss, btcBlocksPerDay, @@ -193,32 +193,32 @@ func (ob *Observer) GetChainParams() observertypes.ChainParams { } // Start starts the Go routine processes to observe the Bitcoin chain -func (ob *Observer) Start() { +func (ob *Observer) Start(ctx context.Context) { ob.Logger().Chain.Info().Msgf("observer is starting for chain %d", ob.Chain().ChainId) // watch bitcoin chain for incoming txs and post votes to zetacore - go ob.WatchInbound() + bg.Work(ctx, ob.WatchInbound, bg.WithName("WatchInbound"), bg.WithLogger(ob.Logger().Inbound)) // watch bitcoin chain for outgoing txs status - go ob.WatchOutbound() + bg.Work(ctx, ob.WatchOutbound, bg.WithName("WatchOutbound"), bg.WithLogger(ob.Logger().Outbound)) // watch bitcoin chain for UTXOs owned by the TSS address - go ob.WatchUTXOs() + bg.Work(ctx, ob.WatchUTXOs, bg.WithName("WatchUTXOs"), bg.WithLogger(ob.Logger().Outbound)) // watch bitcoin chain for gas rate and post to zetacore - go ob.WatchGasPrice() + bg.Work(ctx, ob.WatchGasPrice, bg.WithName("WatchGasPrice"), bg.WithLogger(ob.Logger().GasPrice)) // watch zetacore for bitcoin inbound trackers - go ob.WatchInboundTracker() + bg.Work(ctx, ob.WatchInboundTracker, bg.WithName("WatchInboundTracker"), bg.WithLogger(ob.Logger().Inbound)) // watch the RPC status of the bitcoin chain - go ob.WatchRPCStatus() + bg.Work(ctx, ob.WatchRPCStatus, bg.WithName("WatchRPCStatus"), bg.WithLogger(ob.Logger().Chain)) } // WatchRPCStatus watches the RPC status of the Bitcoin chain // TODO(revamp): move ticker related functions to a specific file // TODO(revamp): move inner logic in a separate function -func (ob *Observer) WatchRPCStatus() { +func (ob *Observer) WatchRPCStatus(_ context.Context) error { ob.logger.Chain.Info().Msgf("RPCStatus is starting") ticker := time.NewTicker(60 * time.Second) @@ -274,7 +274,7 @@ func (ob *Observer) WatchRPCStatus() { Msgf("[OK] RPC status check: latest block number %d, timestamp %s (%.fs ago), tss addr %s, #utxos: %d", bn, blockTime, elapsedSeconds, tssAddr, len(res)) case <-ob.StopChannel(): - return + return nil } } } @@ -296,16 +296,16 @@ func (ob *Observer) ConfirmationsThreshold(amount *big.Int) int64 { return BigValueConfirmationCount } - // #nosec G701 always in range + // #nosec G115 always in range return int64(ob.GetChainParams().ConfirmationCount) } // WatchGasPrice watches Bitcoin chain for gas rate and post to zetacore // TODO(revamp): move ticker related functions to a specific file // TODO(revamp): move inner logic in a separate function -func (ob *Observer) WatchGasPrice() { +func (ob *Observer) WatchGasPrice(ctx context.Context) error { // report gas price right away as the ticker takes time to kick in - err := ob.PostGasPrice() + err := ob.PostGasPrice(ctx) if err != nil { ob.logger.GasPrice.Error().Err(err).Msgf("PostGasPrice error for chain %d", ob.Chain().ChainId) } @@ -314,7 +314,7 @@ func (ob *Observer) WatchGasPrice() { ticker, err := clienttypes.NewDynamicTicker("Bitcoin_WatchGasPrice", ob.GetChainParams().GasPriceTicker) if err != nil { ob.logger.GasPrice.Error().Err(err).Msg("error creating ticker") - return + return err } ob.logger.GasPrice.Info().Msgf("WatchGasPrice started for chain %d with interval %d", ob.Chain().ChainId, ob.GetChainParams().GasPriceTicker) @@ -326,59 +326,59 @@ func (ob *Observer) WatchGasPrice() { if !ob.GetChainParams().IsSupported { continue } - err := ob.PostGasPrice() + err := ob.PostGasPrice(ctx) if err != nil { ob.logger.GasPrice.Error().Err(err).Msgf("PostGasPrice error for chain %d", ob.Chain().ChainId) } ticker.UpdateInterval(ob.GetChainParams().GasPriceTicker, ob.logger.GasPrice) case <-ob.StopChannel(): ob.logger.GasPrice.Info().Msgf("WatchGasPrice stopped for chain %d", ob.Chain().ChainId) - return + return nil } } } // PostGasPrice posts gas price to zetacore // TODO(revamp): move to gas price file -func (ob *Observer) PostGasPrice() error { - // hardcode gas price here since this RPC is not available on regtest - if chains.IsBitcoinRegnet(ob.Chain().ChainId) { - blockNumber, err := ob.btcClient.GetBlockCount() +func (ob *Observer) PostGasPrice(ctx context.Context) error { + var ( + err error + feeRateEstimated uint64 + ) + + // special handle regnet and testnet gas rate + // regnet: RPC 'EstimateSmartFee' is not available + // testnet: RPC 'EstimateSmartFee' returns unreasonable high gas rate + if ob.Chain().NetworkType != chains.NetworkType_mainnet { + feeRateEstimated, err = ob.specialHandleFeeRate() if err != nil { - return err + return errors.Wrap(err, "unable to execute specialHandleFeeRate") } - - // #nosec G701 always in range - _, err = ob.ZetacoreClient().PostGasPrice(ob.Chain(), 1, "100", uint64(blockNumber)) + } else { + // EstimateSmartFee returns the fees per kilobyte (BTC/kb) targeting given block confirmation + feeResult, err := ob.btcClient.EstimateSmartFee(1, &btcjson.EstimateModeEconomical) if err != nil { - ob.logger.GasPrice.Err(err).Msg("PostGasPrice:") - return err + return errors.Wrap(err, "unable to estimate smart fee") } - return nil - } - - // EstimateSmartFee returns the fees per kilobyte (BTC/kb) targeting given block confirmation - feeResult, err := ob.btcClient.EstimateSmartFee(1, &btcjson.EstimateModeEconomical) - if err != nil { - return err - } - if feeResult.Errors != nil || feeResult.FeeRate == nil { - return fmt.Errorf("error getting gas price: %s", feeResult.Errors) - } - if *feeResult.FeeRate > math.MaxInt64 { - return fmt.Errorf("gas price is too large: %f", *feeResult.FeeRate) + if feeResult.Errors != nil || feeResult.FeeRate == nil { + return fmt.Errorf("error getting gas price: %s", feeResult.Errors) + } + if *feeResult.FeeRate > math.MaxInt64 { + return fmt.Errorf("gas price is too large: %f", *feeResult.FeeRate) + } + feeRateEstimated = bitcoin.FeeRateToSatPerByte(*feeResult.FeeRate).Uint64() } - feeRatePerByte := bitcoin.FeeRateToSatPerByte(*feeResult.FeeRate) + // query the current block number blockNumber, err := ob.btcClient.GetBlockCount() if err != nil { return err } - // #nosec G701 always positive - _, err = ob.ZetacoreClient().PostGasPrice(ob.Chain(), feeRatePerByte.Uint64(), "100", uint64(blockNumber)) + // #nosec G115 always positive + _, err = ob.ZetacoreClient().PostVoteGasPrice(ctx, ob.Chain(), feeRateEstimated, "100", uint64(blockNumber)) if err != nil { - ob.logger.GasPrice.Err(err).Msg("PostGasPrice:") + ob.logger.GasPrice.Err(err).Msg("err PostGasPrice") return err } @@ -400,7 +400,7 @@ func GetSenderAddressByVin(rpcClient interfaces.BTCRPCClient, vin btcjson.Vin, n return "", errors.Wrapf(err, "error getting raw transaction %s", vin.Txid) } - // #nosec G701 - always in range + // #nosec G115 - always in range if len(tx.MsgTx().TxOut) <= int(vin.Vout) { return "", fmt.Errorf("vout index %d out of range for tx %s", vin.Vout, vin.Txid) } @@ -430,11 +430,11 @@ func GetSenderAddressByVin(rpcClient interfaces.BTCRPCClient, vin btcjson.Vin, n // WatchUTXOs watches bitcoin chain for UTXOs owned by the TSS address // TODO(revamp): move ticker related functions to a specific file -func (ob *Observer) WatchUTXOs() { +func (ob *Observer) WatchUTXOs(ctx context.Context) error { ticker, err := clienttypes.NewDynamicTicker("Bitcoin_WatchUTXOs", ob.GetChainParams().WatchUtxoTicker) if err != nil { ob.logger.UTXOs.Error().Err(err).Msg("error creating ticker") - return + return err } defer ticker.Stop() @@ -444,21 +444,21 @@ func (ob *Observer) WatchUTXOs() { if !ob.GetChainParams().IsSupported { continue } - err := ob.FetchUTXOs() + err := ob.FetchUTXOs(ctx) if err != nil { ob.logger.UTXOs.Error().Err(err).Msg("error fetching btc utxos") } ticker.UpdateInterval(ob.GetChainParams().WatchUtxoTicker, ob.logger.UTXOs) case <-ob.StopChannel(): ob.logger.UTXOs.Info().Msgf("WatchUTXOs stopped for chain %d", ob.Chain().ChainId) - return + return nil } } } // FetchUTXOs fetches TSS-owned UTXOs from the Bitcoin node // TODO(revamp): move to UTXO file -func (ob *Observer) FetchUTXOs() error { +func (ob *Observer) FetchUTXOs(ctx context.Context) error { defer func() { if err := recover(); err != nil { ob.logger.UTXOs.Error().Msgf("BTC FetchUTXOs: caught panic error: %v", err) @@ -466,7 +466,7 @@ func (ob *Observer) FetchUTXOs() error { }() // This is useful when a zetaclient's pending nonce lagged behind for whatever reason. - ob.refreshPendingNonce() + ob.refreshPendingNonce(ctx) // get the current block height. bh, err := ob.btcClient.GetBlockCount() @@ -618,7 +618,7 @@ func (ob *Observer) LoadLastBlockScanned() error { if err != nil { return errors.Wrapf(err, "error GetBlockCount for chain %d", ob.Chain().ChainId) } - // #nosec G701 always positive + // #nosec G115 always positive ob.WithLastBlockScanned(uint64(blockNumber)) } @@ -644,6 +644,23 @@ func (ob *Observer) LoadBroadcastedTxMap() error { return nil } +// specialHandleFeeRate handles the fee rate for regnet and testnet +func (ob *Observer) specialHandleFeeRate() (uint64, error) { + switch ob.Chain().NetworkType { + case chains.NetworkType_privnet: + // hardcode gas price for regnet + return 1, nil + case chains.NetworkType_testnet: + feeRateEstimated, err := rpc.GetRecentFeeRate(ob.btcClient, ob.netParams) + if err != nil { + return 0, errors.Wrapf(err, "error GetRecentFeeRate") + } + return feeRateEstimated, nil + default: + return 0, fmt.Errorf(" unsupported bitcoin network type %d", ob.Chain().NetworkType) + } +} + // isTssTransaction checks if a given transaction was sent by TSS itself. // An unconfirmed transaction is safe to spend only if it was sent by TSS and verified by ourselves. func (ob *Observer) isTssTransaction(txid string) bool { @@ -653,12 +670,12 @@ func (ob *Observer) isTssTransaction(txid string) bool { // postBlockHeader posts block header to zetacore // TODO(revamp): move to block header file -func (ob *Observer) postBlockHeader(tip int64) error { +func (ob *Observer) postBlockHeader(ctx context.Context, tip int64) error { ob.logger.Inbound.Info().Msgf("postBlockHeader: tip %d", tip) bn := tip - res, err := ob.ZetacoreClient().GetBlockHeaderChainState(ob.Chain().ChainId) - if err == nil && res.ChainState != nil && res.ChainState.EarliestHeight > 0 { - bn = res.ChainState.LatestHeight + 1 + chainState, err := ob.ZetacoreClient().GetBlockHeaderChainState(ctx, ob.Chain().ChainId) + if err == nil && chainState != nil && chainState.EarliestHeight > 0 { + bn = chainState.LatestHeight + 1 } if bn > tip { return fmt.Errorf("postBlockHeader: must post block confirmed block header: %d > %d", bn, tip) @@ -676,6 +693,7 @@ func (ob *Observer) postBlockHeader(tip int64) error { } blockHash := res2.Header.BlockHash() _, err = ob.ZetacoreClient().PostVoteBlockHeader( + ctx, ob.Chain().ChainId, blockHash[:], res2.Block.Height, diff --git a/zetaclient/chains/bitcoin/observer/observer_test.go b/zetaclient/chains/bitcoin/observer/observer_test.go index 8fb8838ae3..438324b091 100644 --- a/zetaclient/chains/bitcoin/observer/observer_test.go +++ b/zetaclient/chains/bitcoin/observer/observer_test.go @@ -21,7 +21,6 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/chains/base" "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/observer" "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" - "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/zetaclient/metrics" "github.com/zeta-chain/zetacore/zetaclient/testutils/mocks" clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" @@ -92,7 +91,6 @@ func MockBTCObserver( params, nil, nil, - nil, dbpath, base.Logger{}, nil, @@ -113,7 +111,6 @@ func Test_NewObserver(t *testing.T) { chain chains.Chain btcClient interfaces.BTCRPCClient chainParams observertypes.ChainParams - appContext *context.AppContext coreClient interfaces.ZetacoreClient tss interfaces.TSSSigner dbpath string @@ -127,7 +124,6 @@ func Test_NewObserver(t *testing.T) { chain: chain, btcClient: mocks.NewMockBTCRPCClient().WithBlockCount(100), chainParams: params, - appContext: nil, coreClient: nil, tss: mocks.NewTSSMainnet(), dbpath: sample.CreateTempDir(t), @@ -140,7 +136,6 @@ func Test_NewObserver(t *testing.T) { chain: chains.Chain{ChainId: 111}, // invalid chain id btcClient: mocks.NewMockBTCRPCClient().WithBlockCount(100), chainParams: params, - appContext: nil, coreClient: nil, tss: mocks.NewTSSMainnet(), dbpath: sample.CreateTempDir(t), @@ -153,7 +148,6 @@ func Test_NewObserver(t *testing.T) { name: "should fail on invalid dbpath", chain: chain, chainParams: params, - appContext: nil, coreClient: nil, btcClient: mocks.NewMockBTCRPCClient().WithBlockCount(100), tss: mocks.NewTSSMainnet(), @@ -173,7 +167,6 @@ func Test_NewObserver(t *testing.T) { tt.chain, tt.btcClient, tt.chainParams, - tt.appContext, tt.coreClient, tt.tss, tt.dbpath, @@ -254,7 +247,7 @@ func Test_LoadDB(t *testing.T) { // create observer dbpath := sample.CreateTempDir(t) - ob, err := observer.NewObserver(chain, btcClient, params, nil, nil, tss, dbpath, base.Logger{}, nil) + ob, err := observer.NewObserver(chain, btcClient, params, nil, tss, dbpath, base.Logger{}, nil) require.NoError(t, err) t.Run("should load db successfully", func(t *testing.T) { diff --git a/zetaclient/chains/bitcoin/observer/outbound.go b/zetaclient/chains/bitcoin/observer/outbound.go index 681604d48b..7bae9de963 100644 --- a/zetaclient/chains/bitcoin/observer/outbound.go +++ b/zetaclient/chains/bitcoin/observer/outbound.go @@ -1,9 +1,11 @@ package observer import ( + "context" "encoding/hex" "fmt" + "cosmossdk.io/math" "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/pkg/errors" @@ -16,7 +18,9 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/rpc" "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" "github.com/zeta-chain/zetacore/zetaclient/compliance" + zctx "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/zetaclient/types" + "github.com/zeta-chain/zetacore/zetaclient/zetacore" ) // GetTxID returns a unique id for outbound tx @@ -28,12 +32,17 @@ func (ob *Observer) GetTxID(nonce uint64) string { // WatchOutbound watches Bitcoin chain for outgoing txs status // TODO(revamp): move ticker functions to a specific file // TODO(revamp): move into a separate package -func (ob *Observer) WatchOutbound() { +func (ob *Observer) WatchOutbound(ctx context.Context) error { + app, err := zctx.FromContext(ctx) + if err != nil { + return errors.Wrap(err, "unable to get app from context") + } + ticker, err := types.NewDynamicTicker("Bitcoin_WatchOutbound", ob.GetChainParams().OutboundTicker) if err != nil { - ob.logger.Outbound.Error().Err(err).Msg("error creating ticker ") - return + return errors.Wrap(err, "unable to create dynamic ticker") } + defer ticker.Stop() chainID := ob.Chain().ChainId @@ -43,12 +52,12 @@ func (ob *Observer) WatchOutbound() { for { select { case <-ticker.C(): - if !ob.AppContext().IsOutboundObservationEnabled(ob.GetChainParams()) { + if !app.IsOutboundObservationEnabled(ob.GetChainParams()) { sampledLogger.Info(). Msgf("WatchOutbound: outbound observation is disabled for chain %d", chainID) continue } - trackers, err := ob.ZetacoreClient().GetAllOutboundTrackerByChain(chainID, interfaces.Ascending) + trackers, err := ob.ZetacoreClient().GetAllOutboundTrackerByChain(ctx, chainID, interfaces.Ascending) if err != nil { ob.logger.Outbound.Error(). Err(err). @@ -58,7 +67,7 @@ func (ob *Observer) WatchOutbound() { for _, tracker := range trackers { // get original cctx parameters outboundID := ob.GetTxID(tracker.Nonce) - cctx, err := ob.ZetacoreClient().GetCctxByNonce(chainID, tracker.Nonce) + cctx, err := ob.ZetacoreClient().GetCctxByNonce(ctx, chainID, tracker.Nonce) if err != nil { ob.logger.Outbound.Info(). Err(err). @@ -83,7 +92,7 @@ func (ob *Observer) WatchOutbound() { txCount := 0 var txResult *btcjson.GetTransactionResult for _, txHash := range tracker.HashList { - result, inMempool := ob.checkIncludedTx(cctx, txHash.TxHash) + result, inMempool := ob.checkIncludedTx(ctx, cctx, txHash.TxHash) if result != nil && !inMempool { // included txCount++ txResult = result @@ -106,16 +115,29 @@ func (ob *Observer) WatchOutbound() { ticker.UpdateInterval(ob.GetChainParams().OutboundTicker, ob.logger.Outbound) case <-ob.StopChannel(): ob.logger.Outbound.Info().Msgf("WatchOutbound stopped for chain %d", chainID) - return + return nil } } } // IsOutboundProcessed returns isIncluded(or inMempool), isConfirmed, Error // TODO(revamp): rename as it vote the outbound and doesn't only check if outbound is processed -func (ob *Observer) IsOutboundProcessed(cctx *crosschaintypes.CrossChainTx, logger zerolog.Logger) (bool, bool, error) { +func (ob *Observer) IsOutboundProcessed( + ctx context.Context, + cctx *crosschaintypes.CrossChainTx, + logger zerolog.Logger, +) (bool, bool, error) { + const ( + // not used with Bitcoin + outboundGasUsed = 0 + outboundGasPrice = 0 + outboundGasLimit = 0 + + gasLimit = zetacore.PostVoteOutboundGasLimit + gasRetryLimit = 0 + ) + params := *cctx.GetCurrentOutboundParam() - sendHash := cctx.Index nonce := cctx.GetCurrentOutboundParam().TssNonce // get broadcasted outbound and tx result @@ -141,7 +163,7 @@ func (ob *Observer) IsOutboundProcessed(cctx *crosschaintypes.CrossChainTx, logg } // Try including this outbound broadcasted by myself - txResult, inMempool := ob.checkIncludedTx(cctx, txnHash) + txResult, inMempool := ob.checkIncludedTx(ctx, cctx, txnHash) if txResult == nil { // check failed, try again next time return false, false, nil } else if inMempool { // still in mempool (should avoid unnecessary Tss keysign) @@ -176,26 +198,42 @@ func (ob *Observer) IsOutboundProcessed(cctx *crosschaintypes.CrossChainTx, logg } logger.Debug().Msgf("Bitcoin outbound confirmed: txid %s, amount %s\n", res.TxID, amountInSat.String()) - zetaHash, ballot, err := ob.ZetacoreClient().PostVoteOutbound( - sendHash, + + signer := ob.ZetacoreClient().GetKeys().GetOperatorAddress() + + msg := crosschaintypes.NewMsgVoteOutbound( + signer.String(), + cctx.Index, res.TxID, - // #nosec G701 always positive + + // #nosec G115 always positive uint64(blockHeight), - 0, // gas used not used with Bitcoin - nil, // gas price not used with Bitcoin - 0, // gas limit not used with Bitcoin - amountInSat, + + // not used with Bitcoin + outboundGasUsed, + math.NewInt(outboundGasPrice), + outboundGasLimit, + + math.NewUintFromBigInt(amountInSat), chains.ReceiveStatus_success, - ob.Chain(), + ob.Chain().ChainId, nonce, coin.CoinType_Gas, ) + + zetaHash, ballot, err := ob.ZetacoreClient().PostVoteOutbound(ctx, gasLimit, gasRetryLimit, msg) + + logFields := map[string]any{ + "outbound.external_tx_hash": res.TxID, + "outbound.nonce": nonce, + "outbound.zeta_tx_hash": zetaHash, + "outbound.ballot": ballot, + } + if err != nil { - logger.Error(). - Err(err). - Msgf("IsOutboundProcessed: error confirming bitcoin outbound %s, nonce %d ballot %s", res.TxID, nonce, ballot) + logger.Error().Err(err).Fields(logFields).Msg("IsOutboundProcessed: error confirming bitcoin outbound") } else if zetaHash != "" { - logger.Info().Msgf("IsOutboundProcessed: confirmed Bitcoin outbound %s, zeta tx hash %s nonce %d ballot %s", res.TxID, zetaHash, nonce, ballot) + logger.Info().Fields(logFields).Msgf("IsOutboundProcessed: confirmed Bitcoin outbound") } return true, true, nil @@ -218,6 +256,7 @@ func (ob *Observer) IsOutboundProcessed(cctx *crosschaintypes.CrossChainTx, logg // // TODO(revamp): move to utxo file func (ob *Observer) SelectUTXOs( + ctx context.Context, amount float64, utxosToSpend uint16, nonce uint64, @@ -231,7 +270,7 @@ func (ob *Observer) SelectUTXOs( defer ob.Mu().Unlock() } else { // for nonce > 0; we proceed only when we see the nonce-mark utxo - preTxid, err := ob.getOutboundIDByNonce(nonce-1, test) + preTxid, err := ob.getOutboundIDByNonce(ctx, nonce-1, test) if err != nil { return nil, 0, 0, 0, err } @@ -303,9 +342,9 @@ func (ob *Observer) SelectUTXOs( // There could be many (unpredictable) reasons for a pending nonce lagging behind, for example: // 1. The zetaclient gets restarted. // 2. The tracker is missing in zetacore. -func (ob *Observer) refreshPendingNonce() { +func (ob *Observer) refreshPendingNonce(ctx context.Context) { // get pending nonces from zetacore - p, err := ob.ZetacoreClient().GetPendingNoncesByChain(ob.Chain().ChainId) + p, err := ob.ZetacoreClient().GetPendingNoncesByChain(ctx, ob.Chain().ChainId) if err != nil { ob.logger.Chain.Error().Err(err).Msg("refreshPendingNonce: error getting pending nonces") } @@ -315,11 +354,11 @@ func (ob *Observer) refreshPendingNonce() { pendingNonce := ob.pendingNonce ob.Mu().Unlock() - // #nosec G701 always non-negative + // #nosec G115 always non-negative nonceLow := uint64(p.NonceLow) if nonceLow > pendingNonce { // get the last included outbound hash - txid, err := ob.getOutboundIDByNonce(nonceLow-1, false) + txid, err := ob.getOutboundIDByNonce(ctx, nonceLow-1, false) if err != nil { ob.logger.Chain.Error().Err(err).Msg("refreshPendingNonce: error getting last outbound txid") } @@ -335,7 +374,7 @@ func (ob *Observer) refreshPendingNonce() { // getOutboundIDByNonce gets the outbound ID from the nonce of the outbound transaction // test is true for unit test only -func (ob *Observer) getOutboundIDByNonce(nonce uint64, test bool) (string, error) { +func (ob *Observer) getOutboundIDByNonce(ctx context.Context, nonce uint64, test bool) (string, error) { // There are 2 types of txids an observer can trust // 1. The ones had been verified and saved by observer self. // 2. The ones had been finalized in zetacore based on majority vote. @@ -343,7 +382,7 @@ func (ob *Observer) getOutboundIDByNonce(nonce uint64, test bool) (string, error return res.TxID, nil } if !test { // if not unit test, get cctx from zetacore - send, err := ob.ZetacoreClient().GetCctxByNonce(ob.Chain().ChainId, nonce) + send, err := ob.ZetacoreClient().GetCctxByNonce(ctx, ob.Chain().ChainId, nonce) if err != nil { return "", errors.Wrapf(err, "getOutboundIDByNonce: error getting cctx for nonce %d", nonce) } @@ -390,6 +429,7 @@ func (ob *Observer) findNonceMarkUTXO(nonce uint64, txid string) (int, error) { // checkIncludedTx checks if a txHash is included and returns (txResult, inMempool) // Note: if txResult is nil, then inMempool flag should be ignored. func (ob *Observer) checkIncludedTx( + ctx context.Context, cctx *crosschaintypes.CrossChainTx, txHash string, ) (*btcjson.GetTransactionResult, bool) { @@ -407,7 +447,7 @@ func (ob *Observer) checkIncludedTx( } if getTxResult.Confirmations >= 0 { // check included tx only - err = ob.checkTssOutboundResult(cctx, hash, getTxResult) + err = ob.checkTssOutboundResult(ctx, cctx, hash, getTxResult) if err != nil { ob.logger.Outbound.Error(). Err(err). @@ -472,6 +512,7 @@ func (ob *Observer) removeIncludedTx(nonce uint64) { // // Returns: true if outbound passes basic checks. func (ob *Observer) checkTssOutboundResult( + ctx context.Context, cctx *crosschaintypes.CrossChainTx, hash *chainhash.Hash, res *btcjson.GetTransactionResult, @@ -482,7 +523,7 @@ func (ob *Observer) checkTssOutboundResult( if err != nil { return errors.Wrapf(err, "checkTssOutboundResult: error GetRawTxResultByHash %s", hash.String()) } - err = ob.checkTSSVin(rawResult.Vin, nonce) + err = ob.checkTSSVin(ctx, rawResult.Vin, nonce) if err != nil { return errors.Wrapf(err, "checkTssOutboundResult: invalid TSS Vin in outbound %s nonce %d", hash, nonce) } @@ -510,7 +551,7 @@ func (ob *Observer) checkTssOutboundResult( // checkTSSVin checks vin is valid if: // - The first input is the nonce-mark // - All inputs are from TSS address -func (ob *Observer) checkTSSVin(vins []btcjson.Vin, nonce uint64) error { +func (ob *Observer) checkTSSVin(ctx context.Context, vins []btcjson.Vin, nonce uint64) error { // vins: [nonce-mark, UTXO1, UTXO2, ...] if nonce > 0 && len(vins) <= 1 { return fmt.Errorf("checkTSSVin: len(vins) <= 1") @@ -526,7 +567,7 @@ func (ob *Observer) checkTSSVin(vins []btcjson.Vin, nonce uint64) error { } // 1st vin: nonce-mark MUST come from prior TSS outbound if nonce > 0 && i == 0 { - preTxid, err := ob.getOutboundIDByNonce(nonce-1, false) + preTxid, err := ob.getOutboundIDByNonce(ctx, nonce-1, false) if err != nil { return fmt.Errorf("checkTSSVin: error findTxIDByNonce %d", nonce-1) } @@ -591,7 +632,7 @@ func (ob *Observer) checkTSSVout(params *crosschaintypes.OutboundParams, vouts [ params.Receiver, ) } - // #nosec G701 always positive + // #nosec G115 always positive if uint64(amount) != params.Amount.Uint64() { return fmt.Errorf("checkTSSVout: output amount %d not match params amount %d", amount, params.Amount) } diff --git a/zetaclient/chains/bitcoin/observer/outbound_test.go b/zetaclient/chains/bitcoin/observer/outbound_test.go index 0aaeb7b600..cb43590ff5 100644 --- a/zetaclient/chains/bitcoin/observer/outbound_test.go +++ b/zetaclient/chains/bitcoin/observer/outbound_test.go @@ -1,6 +1,7 @@ package observer import ( + "context" "math" "sort" "testing" @@ -27,7 +28,7 @@ func MockBTCObserverMainnet(t *testing.T) *Observer { tss := mocks.NewTSSMainnet() // create Bitcoin observer - ob, err := NewObserver(chain, btcClient, params, nil, nil, tss, testutils.SQLiteMemory, base.Logger{}, nil) + ob, err := NewObserver(chain, btcClient, params, nil, tss, testutils.SQLiteMemory, base.Logger{}, nil) require.NoError(t, err) return ob @@ -239,13 +240,15 @@ func TestCheckTSSVoutCancelled(t *testing.T) { } func TestSelectUTXOs(t *testing.T) { + ctx := context.Background() + ob := createObserverWithUTXOs(t) dummyTxID := "6e6f71d281146c1fc5c755b35908ee449f26786c84e2ae18f98b268de40b7ec4" // Case1: nonce = 0, bootstrap // input: utxoCap = 5, amount = 0.01, nonce = 0 // output: [0.01], 0.01 - result, amount, _, _, err := ob.SelectUTXOs(0.01, 5, 0, math.MaxUint16, true) + result, amount, _, _, err := ob.SelectUTXOs(ctx, 0.01, 5, 0, math.MaxUint16, true) require.NoError(t, err) require.Equal(t, 0.01, amount) require.Equal(t, ob.utxos[0:1], result) @@ -253,7 +256,7 @@ func TestSelectUTXOs(t *testing.T) { // Case2: nonce = 1, must FAIL and wait for previous transaction to be mined // input: utxoCap = 5, amount = 0.5, nonce = 1 // output: error - result, amount, _, _, err = ob.SelectUTXOs(0.5, 5, 1, math.MaxUint16, true) + result, amount, _, _, err = ob.SelectUTXOs(ctx, 0.5, 5, 1, math.MaxUint16, true) require.Error(t, err) require.Nil(t, result) require.Zero(t, amount) @@ -263,7 +266,7 @@ func TestSelectUTXOs(t *testing.T) { // Case3: nonce = 1, should pass now // input: utxoCap = 5, amount = 0.5, nonce = 1 // output: [0.00002, 0.01, 0.12, 0.18, 0.24], 0.55002 - result, amount, _, _, err = ob.SelectUTXOs(0.5, 5, 1, math.MaxUint16, true) + result, amount, _, _, err = ob.SelectUTXOs(ctx, 0.5, 5, 1, math.MaxUint16, true) require.NoError(t, err) require.Equal(t, 0.55002, amount) require.Equal(t, ob.utxos[0:5], result) @@ -272,7 +275,7 @@ func TestSelectUTXOs(t *testing.T) { // Case4: // input: utxoCap = 5, amount = 1.0, nonce = 2 // output: [0.00002001, 0.01, 0.12, 0.18, 0.24, 0.5], 1.05002001 - result, amount, _, _, err = ob.SelectUTXOs(1.0, 5, 2, math.MaxUint16, true) + result, amount, _, _, err = ob.SelectUTXOs(ctx, 1.0, 5, 2, math.MaxUint16, true) require.NoError(t, err) require.InEpsilon(t, 1.05002001, amount, 1e-8) require.Equal(t, ob.utxos[0:6], result) @@ -281,7 +284,7 @@ func TestSelectUTXOs(t *testing.T) { // Case5: should include nonce-mark utxo on the LEFT // input: utxoCap = 5, amount = 8.05, nonce = 3 // output: [0.00002002, 0.24, 0.5, 1.26, 2.97, 3.28], 8.25002002 - result, amount, _, _, err = ob.SelectUTXOs(8.05, 5, 3, math.MaxUint16, true) + result, amount, _, _, err = ob.SelectUTXOs(ctx, 8.05, 5, 3, math.MaxUint16, true) require.NoError(t, err) require.InEpsilon(t, 8.25002002, amount, 1e-8) expected := append([]btcjson.ListUnspentResult{ob.utxos[0]}, ob.utxos[4:9]...) @@ -291,7 +294,7 @@ func TestSelectUTXOs(t *testing.T) { // Case6: should include nonce-mark utxo on the RIGHT // input: utxoCap = 5, amount = 0.503, nonce = 24105432 // output: [0.24107432, 0.01, 0.12, 0.18, 0.24], 0.55002002 - result, amount, _, _, err = ob.SelectUTXOs(0.503, 5, 24105432, math.MaxUint16, true) + result, amount, _, _, err = ob.SelectUTXOs(ctx, 0.503, 5, 24105432, math.MaxUint16, true) require.NoError(t, err) require.InEpsilon(t, 0.79107431, amount, 1e-8) expected = append([]btcjson.ListUnspentResult{ob.utxos[4]}, ob.utxos[0:4]...) @@ -301,7 +304,7 @@ func TestSelectUTXOs(t *testing.T) { // Case7: should include nonce-mark utxo in the MIDDLE // input: utxoCap = 5, amount = 1.0, nonce = 24105433 // output: [0.24107432, 0.12, 0.18, 0.24, 0.5], 1.28107432 - result, amount, _, _, err = ob.SelectUTXOs(1.0, 5, 24105433, math.MaxUint16, true) + result, amount, _, _, err = ob.SelectUTXOs(ctx, 1.0, 5, 24105433, math.MaxUint16, true) require.NoError(t, err) require.InEpsilon(t, 1.28107432, amount, 1e-8) expected = append([]btcjson.ListUnspentResult{ob.utxos[4]}, ob.utxos[1:4]...) @@ -311,7 +314,7 @@ func TestSelectUTXOs(t *testing.T) { // Case8: should work with maximum amount // input: utxoCap = 5, amount = 16.03 // output: [0.24107432, 1.26, 2.97, 3.28, 5.16, 8.72], 21.63107432 - result, amount, _, _, err = ob.SelectUTXOs(16.03, 5, 24105433, math.MaxUint16, true) + result, amount, _, _, err = ob.SelectUTXOs(ctx, 16.03, 5, 24105433, math.MaxUint16, true) require.NoError(t, err) require.InEpsilon(t, 21.63107432, amount, 1e-8) expected = append([]btcjson.ListUnspentResult{ob.utxos[4]}, ob.utxos[6:11]...) @@ -320,7 +323,7 @@ func TestSelectUTXOs(t *testing.T) { // Case9: must FAIL due to insufficient funds // input: utxoCap = 5, amount = 21.64 // output: error - result, amount, _, _, err = ob.SelectUTXOs(21.64, 5, 24105433, math.MaxUint16, true) + result, amount, _, _, err = ob.SelectUTXOs(ctx, 21.64, 5, 24105433, math.MaxUint16, true) require.Error(t, err) require.Nil(t, result) require.Zero(t, amount) @@ -332,6 +335,8 @@ func TestSelectUTXOs(t *testing.T) { } func TestUTXOConsolidation(t *testing.T) { + ctx := context.Background() + dummyTxID := "6e6f71d281146c1fc5c755b35908ee449f26786c84e2ae18f98b268de40b7ec4" t.Run("should not consolidate", func(t *testing.T) { @@ -340,7 +345,7 @@ func TestUTXOConsolidation(t *testing.T) { // input: utxoCap = 10, amount = 0.01, nonce = 1, rank = 10 // output: [0.00002, 0.01], 0.01002 - result, amount, clsdtUtxo, clsdtValue, err := ob.SelectUTXOs(0.01, 10, 1, 10, true) + result, amount, clsdtUtxo, clsdtValue, err := ob.SelectUTXOs(ctx, 0.01, 10, 1, 10, true) require.NoError(t, err) require.Equal(t, 0.01002, amount) require.Equal(t, ob.utxos[0:2], result) @@ -354,7 +359,7 @@ func TestUTXOConsolidation(t *testing.T) { // input: utxoCap = 9, amount = 0.01, nonce = 1, rank = 9 // output: [0.00002, 0.01, 0.12], 0.13002 - result, amount, clsdtUtxo, clsdtValue, err := ob.SelectUTXOs(0.01, 9, 1, 9, true) + result, amount, clsdtUtxo, clsdtValue, err := ob.SelectUTXOs(ctx, 0.01, 9, 1, 9, true) require.NoError(t, err) require.Equal(t, 0.13002, amount) require.Equal(t, ob.utxos[0:3], result) @@ -368,7 +373,7 @@ func TestUTXOConsolidation(t *testing.T) { // input: utxoCap = 5, amount = 0.01, nonce = 0, rank = 5 // output: [0.00002, 0.014, 1.26, 0.5, 0.2], 2.01002 - result, amount, clsdtUtxo, clsdtValue, err := ob.SelectUTXOs(0.01, 5, 1, 5, true) + result, amount, clsdtUtxo, clsdtValue, err := ob.SelectUTXOs(ctx, 0.01, 5, 1, 5, true) require.NoError(t, err) require.Equal(t, 2.01002, amount) expected := make([]btcjson.ListUnspentResult, 2) @@ -387,7 +392,7 @@ func TestUTXOConsolidation(t *testing.T) { // input: utxoCap = 12, amount = 0.01, nonce = 0, rank = 1 // output: [0.00002, 0.01, 8.72, 5.16, 3.28, 2.97, 1.26, 0.5, 0.24, 0.18, 0.12], 22.44002 - result, amount, clsdtUtxo, clsdtValue, err := ob.SelectUTXOs(0.01, 12, 1, 1, true) + result, amount, clsdtUtxo, clsdtValue, err := ob.SelectUTXOs(ctx, 0.01, 12, 1, 1, true) require.NoError(t, err) require.Equal(t, 22.44002, amount) expected := make([]btcjson.ListUnspentResult, 2) @@ -411,7 +416,7 @@ func TestUTXOConsolidation(t *testing.T) { // input: utxoCap = 5, amount = 0.13, nonce = 24105432, rank = 5 // output: [0.24107431, 0.01, 0.12, 1.26, 0.5, 0.24], 2.37107431 - result, amount, clsdtUtxo, clsdtValue, err := ob.SelectUTXOs(0.13, 5, 24105432, 5, true) + result, amount, clsdtUtxo, clsdtValue, err := ob.SelectUTXOs(ctx, 0.13, 5, 24105432, 5, true) require.NoError(t, err) require.InEpsilon(t, 2.37107431, amount, 1e-8) expected := append([]btcjson.ListUnspentResult{ob.utxos[4]}, ob.utxos[0:2]...) @@ -434,7 +439,7 @@ func TestUTXOConsolidation(t *testing.T) { // input: utxoCap = 12, amount = 0.13, nonce = 24105432, rank = 1 // output: [0.24107431, 0.01, 0.12, 8.72, 5.16, 3.28, 2.97, 1.26, 0.5, 0.24, 0.18], 22.68107431 - result, amount, clsdtUtxo, clsdtValue, err := ob.SelectUTXOs(0.13, 12, 24105432, 1, true) + result, amount, clsdtUtxo, clsdtValue, err := ob.SelectUTXOs(ctx, 0.13, 12, 24105432, 1, true) require.NoError(t, err) require.InEpsilon(t, 22.68107431, amount, 1e-8) expected := append([]btcjson.ListUnspentResult{ob.utxos[4]}, ob.utxos[0:2]...) diff --git a/zetaclient/chains/bitcoin/rpc/rpc.go b/zetaclient/chains/bitcoin/rpc/rpc.go index 32144d77f2..079b6fa298 100644 --- a/zetaclient/chains/bitcoin/rpc/rpc.go +++ b/zetaclient/chains/bitcoin/rpc/rpc.go @@ -4,14 +4,24 @@ import ( "fmt" "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/rpcclient" "github.com/pkg/errors" + "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin" "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" "github.com/zeta-chain/zetacore/zetaclient/config" ) +const ( + // feeRateCountBackBlocks is the default number of blocks to look back for fee rate estimation + feeRateCountBackBlocks = 2 + + // defaultTestnetFeeRate is the default fee rate for testnet, 10 sat/byte + defaultTestnetFeeRate = 10 +) + // NewRPCClient creates a new RPC client by the given config. func NewRPCClient(btcConfig config.BTCConfig) (*rpcclient.Client, error) { connCfg := &rpcclient.ConnConfig{ @@ -107,3 +117,43 @@ func GetRawTxResult( // res.Confirmations < 0 (meaning not included) return btcjson.TxRawResult{}, fmt.Errorf("GetRawTxResult: tx %s not included yet", hash) } + +// GetRecentFeeRate gets the highest fee rate from recent blocks +// Note: this method is only used for testnet +func GetRecentFeeRate(rpcClient interfaces.BTCRPCClient, netParams *chaincfg.Params) (uint64, error) { + blockNumber, err := rpcClient.GetBlockCount() + if err != nil { + return 0, err + } + + // get the highest fee rate among recent 'countBack' blocks to avoid underestimation + highestRate := int64(0) + for i := int64(0); i < feeRateCountBackBlocks; i++ { + // get the block + hash, err := rpcClient.GetBlockHash(blockNumber - i) + if err != nil { + return 0, err + } + block, err := rpcClient.GetBlockVerboseTx(hash) + if err != nil { + return 0, err + } + + // computes the average fee rate of the block and take the higher rate + avgFeeRate, err := bitcoin.CalcBlockAvgFeeRate(block, netParams) + if err != nil { + return 0, err + } + if avgFeeRate > highestRate { + highestRate = avgFeeRate + } + } + + // use 10 sat/byte as default estimation if recent fee rate drops to 0 + if highestRate == 0 { + highestRate = defaultTestnetFeeRate + } + + // #nosec G115 always in range + return uint64(highestRate), nil +} diff --git a/zetaclient/chains/bitcoin/rpc/rpc_live_test.go b/zetaclient/chains/bitcoin/rpc/rpc_live_test.go index 97a373f94d..7cc0abc11d 100644 --- a/zetaclient/chains/bitcoin/rpc/rpc_live_test.go +++ b/zetaclient/chains/bitcoin/rpc/rpc_live_test.go @@ -55,11 +55,11 @@ func (suite *BitcoinObserverTestSuite) SetupTest() { btcClient := mocks.NewMockBTCRPCClient() // create observer - ob, err := observer.NewObserver(chain, btcClient, params, nil, nil, tss, testutils.SQLiteMemory, + ob, err := observer.NewObserver(chain, btcClient, params, nil, tss, testutils.SQLiteMemory, base.DefaultLogger(), nil) suite.Require().NoError(err) suite.Require().NotNil(ob) - suite.rpcClient, err = getRPCClient(18332) + suite.rpcClient, err = createRPCClient(18332) suite.Require().NoError(err) skBytes, err := hex.DecodeString(skHex) suite.Require().NoError(err) @@ -91,13 +91,14 @@ func (suite *BitcoinObserverTestSuite) SetupTest() { func (suite *BitcoinObserverTestSuite) TearDownSuite() { } -func getRPCClient(chainID int64) (*rpcclient.Client, error) { +// createRPCClient creates a new Bitcoin RPC client for given chainID +func createRPCClient(chainID int64) (*rpcclient.Client, error) { var connCfg *rpcclient.ConnConfig rpcMainnet := os.Getenv("BTC_RPC_MAINNET") rpcTestnet := os.Getenv("BTC_RPC_TESTNET") // mainnet - if chainID == 8332 { + if chainID == chains.BitcoinMainnet.ChainId { connCfg = &rpcclient.ConnConfig{ Host: rpcMainnet, // mainnet endpoint goes here User: "user", @@ -108,7 +109,7 @@ func getRPCClient(chainID int64) (*rpcclient.Client, error) { } } // testnet3 - if chainID == 18332 { + if chainID == chains.BitcoinTestnet.ChainId { connCfg = &rpcclient.ConnConfig{ Host: rpcTestnet, // testnet endpoint goes here User: "user", @@ -218,6 +219,7 @@ func TestBitcoinObserverLive(t *testing.T) { // LiveTestBitcoinFeeRate(t) // LiveTestAvgFeeRateMainnetMempoolSpace(t) // LiveTestAvgFeeRateTestnetMempoolSpace(t) + // LiveTestGetRecentFeeRate(t) // LiveTestGetSenderByVin(t) } @@ -243,7 +245,7 @@ func LiveTestNewRPCClient(t *testing.T) { // LiveTestGetBlockHeightByHash queries Bitcoin block height by hash func LiveTestGetBlockHeightByHash(t *testing.T) { // setup Bitcoin client - client, err := getRPCClient(8332) + client, err := createRPCClient(chains.BitcoinMainnet.ChainId) require.NoError(t, err) // the block hashes to test @@ -265,7 +267,7 @@ func LiveTestGetBlockHeightByHash(t *testing.T) { // and compares Conservative and Economical fee rates for different block targets (1 and 2) func LiveTestBitcoinFeeRate(t *testing.T) { // setup Bitcoin client - client, err := getRPCClient(8332) + client, err := createRPCClient(chains.BitcoinMainnet.ChainId) require.NoError(t, err) bn, err := client.GetBlockCount() if err != nil { @@ -390,7 +392,7 @@ func compareAvgFeeRate(t *testing.T, client *rpcclient.Client, startBlock int, e // LiveTestAvgFeeRateMainnetMempoolSpace compares calculated fee rate with mempool.space fee rate for mainnet func LiveTestAvgFeeRateMainnetMempoolSpace(t *testing.T) { // setup Bitcoin client - client, err := getRPCClient(8332) + client, err := createRPCClient(chains.BitcoinMainnet.ChainId) require.NoError(t, err) // test against mempool.space API for 10000 blocks @@ -404,7 +406,7 @@ func LiveTestAvgFeeRateMainnetMempoolSpace(t *testing.T) { // LiveTestAvgFeeRateTestnetMempoolSpace compares calculated fee rate with mempool.space fee rate for testnet func LiveTestAvgFeeRateTestnetMempoolSpace(t *testing.T) { // setup Bitcoin client - client, err := getRPCClient(18332) + client, err := createRPCClient(chains.BitcoinTestnet.ChainId) require.NoError(t, err) // test against mempool.space API for 10000 blocks @@ -415,11 +417,23 @@ func LiveTestAvgFeeRateTestnetMempoolSpace(t *testing.T) { compareAvgFeeRate(t, client, startBlock, endBlock, true) } +// LiveTestGetRecentFeeRate gets the highest fee rate from recent blocks +func LiveTestGetRecentFeeRate(t *testing.T) { + // setup Bitcoin testnet client + client, err := createRPCClient(chains.BitcoinTestnet.ChainId) + require.NoError(t, err) + + // get fee rate from recent blocks + feeRate, err := rpc.GetRecentFeeRate(client, &chaincfg.TestNet3Params) + require.NoError(t, err) + require.Greater(t, feeRate, uint64(0)) +} + // LiveTestGetSenderByVin gets sender address for each vin and compares with mempool.space sender address func LiveTestGetSenderByVin(t *testing.T) { // setup Bitcoin client - chainID := int64(8332) - client, err := getRPCClient(chainID) + chainID := chains.BitcoinMainnet.ChainId + client, err := createRPCClient(chainID) require.NoError(t, err) // net params diff --git a/zetaclient/chains/bitcoin/signer/signer.go b/zetaclient/chains/bitcoin/signer/signer.go index 259f55bc53..93b2fac800 100644 --- a/zetaclient/chains/bitcoin/signer/signer.go +++ b/zetaclient/chains/bitcoin/signer/signer.go @@ -3,6 +3,7 @@ package signer import ( "bytes" + "context" "encoding/hex" "fmt" "math/big" @@ -26,7 +27,7 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" "github.com/zeta-chain/zetacore/zetaclient/compliance" "github.com/zeta-chain/zetacore/zetaclient/config" - "github.com/zeta-chain/zetacore/zetaclient/context" + zctx "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/zetaclient/metrics" "github.com/zeta-chain/zetacore/zetaclient/outboundprocessor" ) @@ -58,13 +59,12 @@ type Signer struct { // NewSigner creates a new Bitcoin signer func NewSigner( chain chains.Chain, - appContext *context.AppContext, tss interfaces.TSSSigner, ts *metrics.TelemetryServer, logger base.Logger, cfg config.BTCConfig) (*Signer, error) { // create base signer - baseSigner := base.NewSigner(chain, appContext, tss, ts, logger) + baseSigner := base.NewSigner(chain, tss, ts, logger) // create the bitcoin rpc client using the provided config connCfg := &rpcclient.ConnConfig{ @@ -171,6 +171,7 @@ func (signer *Signer) AddWithdrawTxOutputs( // SignWithdrawTx receives utxos sorted by value, amount in BTC, feeRate in BTC per Kb // TODO(revamp): simplify the function func (signer *Signer) SignWithdrawTx( + ctx context.Context, to btcutil.Address, amount float64, gasPrice *big.Int, @@ -185,7 +186,7 @@ func (signer *Signer) SignWithdrawTx( nonceMark := chains.NonceMarkAmount(nonce) // refresh unspent UTXOs and continue with keysign regardless of error - err := observer.FetchUTXOs() + err := observer.FetchUTXOs(ctx) if err != nil { signer.Logger(). Std.Error(). @@ -195,6 +196,7 @@ func (signer *Signer) SignWithdrawTx( // select N UTXOs to cover the total expense prevOuts, total, consolidatedUtxo, consolidatedValue, err := observer.SelectUTXOs( + ctx, amount+estimateFee+float64(nonceMark)*1e-8, maxNoOfInputsPerTx, nonce, @@ -218,7 +220,7 @@ func (signer *Signer) SignWithdrawTx( } // size checking - // #nosec G701 always positive + // #nosec G115 always positive txSize, err := bitcoin.EstimateOutboundSize(uint64(len(prevOuts)), []btcutil.Address{to}) if err != nil { return nil, err @@ -239,7 +241,7 @@ func (signer *Signer) SignWithdrawTx( } // fee calculation - // #nosec G701 always in range (checked above) + // #nosec G115 always in range (checked above) fees := new(big.Int).Mul(big.NewInt(int64(txSize)), gasPrice) signer.Logger(). Std.Info(). @@ -270,7 +272,7 @@ func (signer *Signer) SignWithdrawTx( } } - sig65Bs, err := signer.TSS().SignBatch(witnessHashes, height, nonce, chain.ChainId) + sig65Bs, err := signer.TSS().SignBatch(ctx, witnessHashes, height, nonce, chain.ChainId) if err != nil { return nil, fmt.Errorf("SignBatch error: %v", err) } @@ -317,6 +319,7 @@ func (signer *Signer) Broadcast(signedTx *wire.MsgTx) error { // TryProcessOutbound signs and broadcasts a BTC transaction from a new outbound // TODO(revamp): simplify the function func (signer *Signer) TryProcessOutbound( + ctx context.Context, cctx *types.CrossChainTx, outboundProcessor *outboundprocessor.Processor, outboundID string, @@ -324,6 +327,12 @@ func (signer *Signer) TryProcessOutbound( zetacoreClient interfaces.ZetacoreClient, height uint64, ) { + app, err := zctx.FromContext(ctx) + if err != nil { + signer.Logger().Std.Error().Msgf("BTC TryProcessOutbound: %s, cannot get app context", cctx.Index) + return + } + defer func() { outboundProcessor.EndTryProcess(outboundID) if err := recover(); err != nil { @@ -350,7 +359,7 @@ func (signer *Signer) TryProcessOutbound( logger.Error().Msgf("chain observer is not a bitcoin observer") return } - flags := signer.AppContext().GetCrossChainFlags() + flags := app.GetCrossChainFlags() if !flags.IsOutboundEnabled { logger.Info().Msgf("outbound is disabled") return @@ -403,6 +412,7 @@ func (signer *Signer) TryProcessOutbound( // sign withdraw tx tx, err := signer.SignWithdrawTx( + ctx, to, amount, gasprice, @@ -421,7 +431,7 @@ func (signer *Signer) TryProcessOutbound( Msgf("Key-sign success: %d => %s, nonce %d", cctx.InboundParams.SenderChainId, chain.ChainName, outboundTssNonce) // FIXME: add prometheus metrics - _, err = zetacoreClient.GetObserverList() + _, err = zetacoreClient.GetObserverList(ctx) if err != nil { logger.Warn(). Err(err). @@ -447,6 +457,7 @@ func (signer *Signer) TryProcessOutbound( logger.Info(). Msgf("Broadcast success: nonce %d to chain %s outboundHash %s", outboundTssNonce, chain.String(), outboundHash) zetaHash, err := zetacoreClient.AddOutboundTracker( + ctx, chain.ChainId, outboundTssNonce, outboundHash, diff --git a/zetaclient/chains/bitcoin/signer/signer_keysign_test.go b/zetaclient/chains/bitcoin/signer/signer_keysign_test.go index 2506f57059..0d1ec42431 100644 --- a/zetaclient/chains/bitcoin/signer/signer_keysign_test.go +++ b/zetaclient/chains/bitcoin/signer/signer_keysign_test.go @@ -2,6 +2,7 @@ package signer import ( "bytes" + "context" "encoding/hex" "fmt" "math/big" @@ -141,12 +142,14 @@ func getTSSTX( subscript []byte, hashType txscript.SigHashType, ) (string, error) { + ctx := context.Background() + witnessHash, err := txscript.CalcWitnessSigHash(subscript, sigHashes, txscript.SigHashAll, tx, idx, amt) if err != nil { return "", err } - sig65B, err := tss.Sign(witnessHash, 10, 10, 0, "") + sig65B, err := tss.Sign(ctx, witnessHash, 10, 10, 0, "") R := big.NewInt(0).SetBytes(sig65B[:32]) S := big.NewInt(0).SetBytes(sig65B[32:64]) sig := btcec.Signature{ diff --git a/zetaclient/chains/bitcoin/signer/signer_test.go b/zetaclient/chains/bitcoin/signer/signer_test.go index e351d4a65c..39728899be 100644 --- a/zetaclient/chains/bitcoin/signer/signer_test.go +++ b/zetaclient/chains/bitcoin/signer/signer_test.go @@ -48,7 +48,6 @@ func (s *BTCSignerSuite) SetUpTest(c *C) { } s.btcSigner, err = NewSigner( chains.Chain{}, - nil, tss, nil, base.DefaultLogger(), @@ -231,7 +230,6 @@ func TestAddWithdrawTxOutputs(t *testing.T) { // Create test signer and receiver address signer, err := NewSigner( chains.Chain{}, - nil, mocks.NewTSSMainnet(), nil, base.DefaultLogger(), @@ -394,7 +392,6 @@ func TestNewBTCSigner(t *testing.T) { } btcSigner, err := NewSigner( chains.Chain{}, - nil, tss, nil, base.DefaultLogger(), diff --git a/zetaclient/chains/bitcoin/tx_script.go b/zetaclient/chains/bitcoin/tx_script.go index 2259e8c555..b5f0bed226 100644 --- a/zetaclient/chains/bitcoin/tx_script.go +++ b/zetaclient/chains/bitcoin/tx_script.go @@ -1,5 +1,6 @@ package bitcoin +// #nosec G507 ripemd160 required for bitcoin address encoding import ( "bytes" "encoding/hex" diff --git a/zetaclient/chains/bitcoin/utils.go b/zetaclient/chains/bitcoin/utils.go index 8c37e01fad..fa4798ea63 100644 --- a/zetaclient/chains/bitcoin/utils.go +++ b/zetaclient/chains/bitcoin/utils.go @@ -46,9 +46,9 @@ func GetSatoshis(btc float64) (int64, error) { // round rounds a float64 to the nearest integer func round(f float64) int64 { if f < 0 { - // #nosec G701 always in range + // #nosec G115 always in range return int64(f - 0.5) } - // #nosec G701 always in range + // #nosec G115 always in range return int64(f + 0.5) } diff --git a/zetaclient/chains/evm/observer/inbound.go b/zetaclient/chains/evm/observer/inbound.go index 889d2215ac..4f51e3354c 100644 --- a/zetaclient/chains/evm/observer/inbound.go +++ b/zetaclient/chains/evm/observer/inbound.go @@ -27,6 +27,7 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/chains/evm" "github.com/zeta-chain/zetacore/zetaclient/compliance" "github.com/zeta-chain/zetacore/zetaclient/config" + zctx "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/zetaclient/metrics" clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" "github.com/zeta-chain/zetacore/zetaclient/zetacore" @@ -34,14 +35,19 @@ import ( // WatchInbound watches evm chain for incoming txs and post votes to zetacore // TODO(revamp): move ticker function to a separate file -func (ob *Observer) WatchInbound() { +func (ob *Observer) WatchInbound(ctx context.Context) error { + app, err := zctx.FromContext(ctx) + if err != nil { + return err + } + ticker, err := clienttypes.NewDynamicTicker( fmt.Sprintf("EVM_WatchInbound_%d", ob.Chain().ChainId), ob.GetChainParams().InboundTicker, ) if err != nil { ob.Logger().Inbound.Error().Err(err).Msg("error creating ticker") - return + return err } defer ticker.Stop() @@ -51,19 +57,19 @@ func (ob *Observer) WatchInbound() { for { select { case <-ticker.C(): - if !ob.AppContext().IsInboundObservationEnabled(ob.GetChainParams()) { + if !app.IsInboundObservationEnabled(ob.GetChainParams()) { sampledLogger.Info(). Msgf("WatchInbound: inbound observation is disabled for chain %d", ob.Chain().ChainId) continue } - err := ob.ObserveInbound(sampledLogger) + err := ob.ObserveInbound(ctx, sampledLogger) if err != nil { ob.Logger().Inbound.Err(err).Msg("WatchInbound: observeInbound error") } ticker.UpdateInterval(ob.GetChainParams().InboundTicker, ob.Logger().Inbound) case <-ob.StopChannel(): ob.Logger().Inbound.Info().Msgf("WatchInbound stopped for chain %d", ob.Chain().ChainId) - return + return nil } } } @@ -71,14 +77,19 @@ func (ob *Observer) WatchInbound() { // WatchInboundTracker gets a list of Inbound tracker suggestions from zeta-core at each tick and tries to check if the in-tx was confirmed. // If it was, it tries to broadcast the confirmation vote. If this zeta client has previously broadcast the vote, the tx would be rejected // TODO(revamp): move inbound tracker function to a separate file -func (ob *Observer) WatchInboundTracker() { +func (ob *Observer) WatchInboundTracker(ctx context.Context) error { + app, err := zctx.FromContext(ctx) + if err != nil { + return err + } + ticker, err := clienttypes.NewDynamicTicker( fmt.Sprintf("EVM_WatchInboundTracker_%d", ob.Chain().ChainId), ob.GetChainParams().InboundTicker, ) if err != nil { ob.Logger().Inbound.Err(err).Msg("error creating ticker") - return + return err } defer ticker.Stop() @@ -86,25 +97,25 @@ func (ob *Observer) WatchInboundTracker() { for { select { case <-ticker.C(): - if !ob.AppContext().IsInboundObservationEnabled(ob.GetChainParams()) { + if !app.IsInboundObservationEnabled(ob.GetChainParams()) { continue } - err := ob.ProcessInboundTrackers() + err := ob.ProcessInboundTrackers(ctx) if err != nil { ob.Logger().Inbound.Err(err).Msg("ProcessInboundTrackers error") } ticker.UpdateInterval(ob.GetChainParams().InboundTicker, ob.Logger().Inbound) case <-ob.StopChannel(): ob.Logger().Inbound.Info().Msgf("WatchInboundTracker stopped for chain %d", ob.Chain().ChainId) - return + return nil } } } // ProcessInboundTrackers processes inbound trackers from zetacore // TODO(revamp): move inbound tracker function to a separate file -func (ob *Observer) ProcessInboundTrackers() error { - trackers, err := ob.ZetacoreClient().GetInboundTrackersForChain(ob.Chain().ChainId) +func (ob *Observer) ProcessInboundTrackers(ctx context.Context) error { + trackers, err := ob.ZetacoreClient().GetInboundTrackersForChain(ctx, ob.Chain().ChainId) if err != nil { return err } @@ -120,7 +131,7 @@ func (ob *Observer) ProcessInboundTrackers() error { ) } - receipt, err := ob.evmClient.TransactionReceipt(context.Background(), ethcommon.HexToHash(tracker.TxHash)) + receipt, err := ob.evmClient.TransactionReceipt(ctx, ethcommon.HexToHash(tracker.TxHash)) if err != nil { return errors.Wrapf( err, @@ -134,11 +145,11 @@ func (ob *Observer) ProcessInboundTrackers() error { // check and vote on inbound tx switch tracker.CoinType { case coin.CoinType_Zeta: - _, err = ob.CheckAndVoteInboundTokenZeta(tx, receipt, true) + _, err = ob.CheckAndVoteInboundTokenZeta(ctx, tx, receipt, true) case coin.CoinType_ERC20: - _, err = ob.CheckAndVoteInboundTokenERC20(tx, receipt, true) + _, err = ob.CheckAndVoteInboundTokenERC20(ctx, tx, receipt, true) case coin.CoinType_Gas: - _, err = ob.CheckAndVoteInboundTokenGas(tx, receipt, true) + _, err = ob.CheckAndVoteInboundTokenGas(ctx, tx, receipt, true) default: return fmt.Errorf( "unknown coin type %s for inbound %s chain %d", @@ -155,9 +166,9 @@ func (ob *Observer) ProcessInboundTrackers() error { } // ObserveInbound observes the evm chain for inbounds and posts votes to zetacore -func (ob *Observer) ObserveInbound(sampledLogger zerolog.Logger) error { +func (ob *Observer) ObserveInbound(ctx context.Context, sampledLogger zerolog.Logger) error { // get and update latest block height - blockNumber, err := ob.evmClient.BlockNumber(context.Background()) + blockNumber, err := ob.evmClient.BlockNumber(ctx) if err != nil { return err } @@ -192,13 +203,19 @@ func (ob *Observer) ObserveInbound(sampledLogger zerolog.Logger) error { startBlock, toBlock := ob.calcBlockRangeToScan(confirmedBlockNum, lastScanned, config.MaxBlocksPerPeriod) // task 1: query evm chain for zeta sent logs (read at most 100 blocks in one go) - lastScannedZetaSent := ob.ObserveZetaSent(startBlock, toBlock) + lastScannedZetaSent, err := ob.ObserveZetaSent(ctx, startBlock, toBlock) + if err != nil { + return errors.Wrap(err, "unable to observe ZetaSent") + } // task 2: query evm chain for deposited logs (read at most 100 blocks in one go) - lastScannedDeposited := ob.ObserveERC20Deposited(startBlock, toBlock) + lastScannedDeposited := ob.ObserveERC20Deposited(ctx, startBlock, toBlock) // task 3: query the incoming tx to TSS address (read at most 100 blocks in one go) - lastScannedTssRecvd := ob.ObserverTSSReceive(startBlock, toBlock) + lastScannedTssRecvd, err := ob.ObserverTSSReceive(ctx, startBlock, toBlock) + if err != nil { + return errors.Wrap(err, "unable to observe TSSReceive") + } // note: using lowest height for all 3 events is not perfect, but it's simple and good enough lastScannedLowest := lastScannedZetaSent @@ -225,22 +242,29 @@ func (ob *Observer) ObserveInbound(sampledLogger zerolog.Logger) error { // ObserveZetaSent queries the ZetaSent event from the connector contract and posts to zetacore // returns the last block successfully scanned -func (ob *Observer) ObserveZetaSent(startBlock, toBlock uint64) uint64 { +func (ob *Observer) ObserveZetaSent(ctx context.Context, startBlock, toBlock uint64) (uint64, error) { + app, err := zctx.FromContext(ctx) + if err != nil { + return 0, err + } + // filter ZetaSent logs addrConnector, connector, err := ob.GetConnectorContract() if err != nil { ob.Logger().Chain.Warn().Err(err).Msgf("ObserveZetaSent: GetConnectorContract error:") - return startBlock - 1 // lastScanned + // lastScanned + return startBlock - 1, err } iter, err := connector.FilterZetaSent(&bind.FilterOpts{ Start: startBlock, End: &toBlock, - Context: context.TODO(), + Context: ctx, }, []ethcommon.Address{}, []*big.Int{}) if err != nil { ob.Logger().Chain.Warn().Err(err).Msgf( "ObserveZetaSent: FilterZetaSent error from block %d to %d for chain %d", startBlock, toBlock, ob.Chain().ChainId) - return startBlock - 1 // lastScanned + // lastScanned + return startBlock - 1, err } // collect and sort events by block number, then tx index, then log index (ascending) @@ -286,25 +310,27 @@ func (ob *Observer) ObserveZetaSent(startBlock, toBlock uint64) uint64 { } guard[event.Raw.TxHash.Hex()] = true - msg := ob.BuildInboundVoteMsgForZetaSentEvent(event) + msg := ob.BuildInboundVoteMsgForZetaSentEvent(app, event) if msg != nil { _, err = ob.PostVoteInbound( + ctx, msg, coin.CoinType_Zeta, zetacore.PostVoteInboundMessagePassingExecutionGasLimit, ) if err != nil { - return beingScanned - 1 // we have to re-scan from this block next time + // we have to re-scan from this block next time + return beingScanned - 1, err } } } // successful processed all events in [startBlock, toBlock] - return toBlock + return toBlock, nil } // ObserveERC20Deposited queries the ERC20CustodyDeposited event from the ERC20Custody contract and posts to zetacore // returns the last block successfully scanned -func (ob *Observer) ObserveERC20Deposited(startBlock, toBlock uint64) uint64 { +func (ob *Observer) ObserveERC20Deposited(ctx context.Context, startBlock, toBlock uint64) uint64 { // filter ERC20CustodyDeposited logs addrCustody, erc20custodyContract, err := ob.GetERC20CustodyContract() if err != nil { @@ -315,7 +341,7 @@ func (ob *Observer) ObserveERC20Deposited(startBlock, toBlock uint64) uint64 { iter, err := erc20custodyContract.FilterDeposited(&bind.FilterOpts{ Start: startBlock, End: &toBlock, - Context: context.TODO(), + Context: ctx, }, []ethcommon.Address{}) if err != nil { ob.Logger().Inbound.Warn().Err(err).Msgf( @@ -376,7 +402,7 @@ func (ob *Observer) ObserveERC20Deposited(startBlock, toBlock uint64) uint64 { msg := ob.BuildInboundVoteMsgForDepositedEvent(event, sender) if msg != nil { - _, err = ob.PostVoteInbound(msg, coin.CoinType_ERC20, zetacore.PostVoteInboundExecutionGasLimit) + _, err = ob.PostVoteInbound(ctx, msg, coin.CoinType_ERC20, zetacore.PostVoteInboundExecutionGasLimit) if err != nil { return beingScanned - 1 // we have to re-scan from this block next time } @@ -388,42 +414,65 @@ func (ob *Observer) ObserveERC20Deposited(startBlock, toBlock uint64) uint64 { // ObserverTSSReceive queries the incoming gas asset to TSS address and posts to zetacore // returns the last block successfully scanned -func (ob *Observer) ObserverTSSReceive(startBlock, toBlock uint64) uint64 { - // query incoming gas asset - for bn := startBlock; bn <= toBlock; bn++ { +func (ob *Observer) ObserverTSSReceive(ctx context.Context, startBlock, toBlock uint64) (uint64, error) { + app, err := zctx.FromContext(ctx) + if err != nil { + return 0, err + } + + var ( // post new block header (if any) to zetacore and ignore error // TODO: consider having a independent ticker(from TSS scaning) for posting block headers // https://github.com/zeta-chain/node/issues/1847 - blockHeaderVerification, found := ob.AppContext().GetBlockHeaderEnabledChains(ob.Chain().ChainId) - if found && blockHeaderVerification.Enabled { + chainID = ob.Chain().ChainId + blockHeaderVerification, found = app.GetBlockHeaderEnabledChains(chainID) + shouldPostBlockHeader = found && blockHeaderVerification.Enabled + ) + + // query incoming gas asset + for bn := startBlock; bn <= toBlock; bn++ { + if shouldPostBlockHeader { // post block header for supported chains // TODO: move this logic in its own routine // https://github.com/zeta-chain/node/issues/2204 - err := ob.postBlockHeader(toBlock) - if err != nil { - ob.Logger().Inbound.Error().Err(err).Msg("error posting block header") + if err := ob.postBlockHeader(ctx, toBlock); err != nil { + ob.Logger().Inbound. + Error().Err(err). + Uint64("tss.to_block", toBlock). + Msg("error posting block header") } } // observe TSS received gas token in block 'bn' - err := ob.ObserveTSSReceiveInBlock(bn) + err := ob.ObserveTSSReceiveInBlock(ctx, bn) if err != nil { ob.Logger().Inbound.Error(). Err(err). - Msgf("ObserverTSSReceive: error observing TSS received token in block %d for chain %d", bn, ob.Chain().ChainId) - return bn - 1 // we have to re-scan from this block next time + Int64("tss.chain_id", chainID). + Uint64("tss.block_number", bn). + Msg("ObserverTSSReceive: unable to ObserveTSSReceiveInBlock") + + // we have to re-scan from this block next time + return bn - 1, nil } } + // successful processed all gas asset deposits in [startBlock, toBlock] - return toBlock + return toBlock, nil } // CheckAndVoteInboundTokenZeta checks and votes on the given inbound Zeta token func (ob *Observer) CheckAndVoteInboundTokenZeta( + ctx context.Context, tx *ethrpc.Transaction, receipt *ethtypes.Receipt, vote bool, ) (string, error) { + app, err := zctx.FromContext(ctx) + if err != nil { + return "", err + } + // check confirmations if confirmed := ob.HasEnoughConfirmations(receipt, ob.LastBlock()); !confirmed { return "", fmt.Errorf( @@ -447,7 +496,7 @@ func (ob *Observer) CheckAndVoteInboundTokenZeta( // sanity check tx event err = evm.ValidateEvmTxLog(&event.Raw, addrConnector, tx.Hash, evm.TopicsZetaSent) if err == nil { - msg = ob.BuildInboundVoteMsgForZetaSentEvent(event) + msg = ob.BuildInboundVoteMsgForZetaSentEvent(app, event) } else { ob.Logger().Inbound.Error().Err(err).Msgf("CheckEvmTxLog error on inbound %s chain %d", tx.Hash, ob.Chain().ChainId) return "", err @@ -461,7 +510,7 @@ func (ob *Observer) CheckAndVoteInboundTokenZeta( return "", nil } if vote { - return ob.PostVoteInbound(msg, coin.CoinType_Zeta, zetacore.PostVoteInboundMessagePassingExecutionGasLimit) + return ob.PostVoteInbound(ctx, msg, coin.CoinType_Zeta, zetacore.PostVoteInboundMessagePassingExecutionGasLimit) } return msg.Digest(), nil @@ -469,6 +518,7 @@ func (ob *Observer) CheckAndVoteInboundTokenZeta( // CheckAndVoteInboundTokenERC20 checks and votes on the given inbound ERC20 token func (ob *Observer) CheckAndVoteInboundTokenERC20( + ctx context.Context, tx *ethrpc.Transaction, receipt *ethtypes.Receipt, vote bool, @@ -511,7 +561,7 @@ func (ob *Observer) CheckAndVoteInboundTokenERC20( return "", nil } if vote { - return ob.PostVoteInbound(msg, coin.CoinType_ERC20, zetacore.PostVoteInboundExecutionGasLimit) + return ob.PostVoteInbound(ctx, msg, coin.CoinType_ERC20, zetacore.PostVoteInboundExecutionGasLimit) } return msg.Digest(), nil @@ -519,6 +569,7 @@ func (ob *Observer) CheckAndVoteInboundTokenERC20( // CheckAndVoteInboundTokenGas checks and votes on the given inbound gas token func (ob *Observer) CheckAndVoteInboundTokenGas( + ctx context.Context, tx *ethrpc.Transaction, receipt *ethtypes.Receipt, vote bool, @@ -549,7 +600,7 @@ func (ob *Observer) CheckAndVoteInboundTokenGas( return "", nil } if vote { - return ob.PostVoteInbound(msg, coin.CoinType_Gas, zetacore.PostVoteInboundExecutionGasLimit) + return ob.PostVoteInbound(ctx, msg, coin.CoinType_Gas, zetacore.PostVoteInboundExecutionGasLimit) } return msg.Digest(), nil @@ -557,13 +608,15 @@ func (ob *Observer) CheckAndVoteInboundTokenGas( // PostVoteInbound posts a vote for the given vote message func (ob *Observer) PostVoteInbound( + ctx context.Context, msg *types.MsgVoteInbound, coinType coin.CoinType, retryGasLimit uint64, ) (string, error) { txHash := msg.InboundHash chainID := ob.Chain().ChainId - zetaHash, ballot, err := ob.ZetacoreClient().PostVoteInbound(zetacore.PostVoteInboundGasLimit, retryGasLimit, msg) + zetaHash, ballot, err := ob.ZetacoreClient(). + PostVoteInbound(ctx, zetacore.PostVoteInboundGasLimit, retryGasLimit, msg) if err != nil { ob.Logger().Inbound.Err(err). Msgf("inbound detected: error posting vote for chain %d token %s inbound %s", chainID, coinType, txHash) @@ -640,11 +693,12 @@ func (ob *Observer) BuildInboundVoteMsgForDepositedEvent( // BuildInboundVoteMsgForZetaSentEvent builds a inbound vote message for a ZetaSent event func (ob *Observer) BuildInboundVoteMsgForZetaSentEvent( + appContext *zctx.AppContext, event *zetaconnector.ZetaConnectorNonEthZetaSent, ) *types.MsgVoteInbound { destChain, found := chains.GetChainFromChainID( event.DestinationChainId.Int64(), - ob.AppContext().GetAdditionalChains(), + appContext.GetAdditionalChains(), ) if !found { ob.Logger().Inbound.Warn().Msgf("chain id not supported %d", event.DestinationChainId.Int64()) @@ -661,7 +715,7 @@ func (ob *Observer) BuildInboundVoteMsgForZetaSentEvent( } if !destChain.IsZetaChain() { - paramsDest, found := ob.AppContext().GetEVMChainParams(destChain.ChainId) + paramsDest, found := appContext.GetEVMChainParams(destChain.ChainId) if !found { ob.Logger().Inbound.Warn(). Msgf("chain id not present in EVMChainParams %d", event.DestinationChainId.Int64()) @@ -747,7 +801,7 @@ func (ob *Observer) BuildInboundVoteMsgForTokenSentToTSS( } // ObserveTSSReceiveInBlock queries the incoming gas asset to TSS address in a single block and posts votes -func (ob *Observer) ObserveTSSReceiveInBlock(blockNumber uint64) error { +func (ob *Observer) ObserveTSSReceiveInBlock(ctx context.Context, blockNumber uint64) error { block, err := ob.GetBlockByNumberCached(blockNumber) if err != nil { return errors.Wrapf(err, "error getting block %d for chain %d", blockNumber, ob.Chain().ChainId) @@ -756,12 +810,12 @@ func (ob *Observer) ObserveTSSReceiveInBlock(blockNumber uint64) error { for i := range block.Transactions { tx := block.Transactions[i] if ethcommon.HexToAddress(tx.To) == ob.TSS().EVMAddress() { - receipt, err := ob.evmClient.TransactionReceipt(context.Background(), ethcommon.HexToHash(tx.Hash)) + receipt, err := ob.evmClient.TransactionReceipt(ctx, ethcommon.HexToHash(tx.Hash)) if err != nil { return errors.Wrapf(err, "error getting receipt for inbound %s chain %d", tx.Hash, ob.Chain().ChainId) } - _, err = ob.CheckAndVoteInboundTokenGas(&tx, receipt, true) + _, err = ob.CheckAndVoteInboundTokenGas(ctx, &tx, receipt, true) if err != nil { return errors.Wrapf( err, diff --git a/zetaclient/chains/evm/observer/inbound_test.go b/zetaclient/chains/evm/observer/inbound_test.go index bb8930f4ca..de1e003ab8 100644 --- a/zetaclient/chains/evm/observer/inbound_test.go +++ b/zetaclient/chains/evm/observer/inbound_test.go @@ -1,20 +1,23 @@ package observer_test import ( + "context" "encoding/hex" "testing" ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/onrik/ethrpc" + "github.com/rs/zerolog" "github.com/stretchr/testify/require" + zctx "github.com/zeta-chain/zetacore/zetaclient/context" + "github.com/zeta-chain/zetacore/zetaclient/keys" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/pkg/constant" "github.com/zeta-chain/zetacore/zetaclient/chains/evm" "github.com/zeta-chain/zetacore/zetaclient/config" - "github.com/zeta-chain/zetacore/zetaclient/keys" "github.com/zeta-chain/zetacore/zetaclient/testutils" "github.com/zeta-chain/zetacore/zetaclient/testutils/mocks" clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" @@ -29,6 +32,8 @@ func Test_CheckAndVoteInboundTokenZeta(t *testing.T) { chainParam := mocks.MockChainParams(chain.ChainId, confirmation) inboundHash := "0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76" + ctx, _ := makeAppContext(t) + t.Run("should pass for archived inbound, receipt and cctx", func(t *testing.T) { tx, receipt, cctx := testutils.LoadEVMInboundNReceiptNCctx( t, @@ -41,7 +46,7 @@ func Test_CheckAndVoteInboundTokenZeta(t *testing.T) { lastBlock := receipt.BlockNumber.Uint64() + confirmation ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, lastBlock, chainParam) - ballot, err := ob.CheckAndVoteInboundTokenZeta(tx, receipt, false) + ballot, err := ob.CheckAndVoteInboundTokenZeta(ctx, tx, receipt, false) require.NoError(t, err) require.Equal(t, cctx.InboundParams.BallotIndex, ballot) }) @@ -57,7 +62,7 @@ func Test_CheckAndVoteInboundTokenZeta(t *testing.T) { lastBlock := receipt.BlockNumber.Uint64() + confirmation - 1 ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, lastBlock, chainParam) - _, err := ob.CheckAndVoteInboundTokenZeta(tx, receipt, false) + _, err := ob.CheckAndVoteInboundTokenZeta(ctx, tx, receipt, false) require.ErrorContains(t, err, "not been confirmed") }) t.Run("should not act if no ZetaSent event", func(t *testing.T) { @@ -73,7 +78,7 @@ func Test_CheckAndVoteInboundTokenZeta(t *testing.T) { lastBlock := receipt.BlockNumber.Uint64() + confirmation ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, lastBlock, chainParam) - ballot, err := ob.CheckAndVoteInboundTokenZeta(tx, receipt, true) + ballot, err := ob.CheckAndVoteInboundTokenZeta(ctx, tx, receipt, true) require.NoError(t, err) require.Equal(t, "", ballot) }) @@ -100,7 +105,7 @@ func Test_CheckAndVoteInboundTokenZeta(t *testing.T) { lastBlock, mocks.MockChainParams(chainID, confirmation), ) - _, err := ob.CheckAndVoteInboundTokenZeta(tx, receipt, true) + _, err := ob.CheckAndVoteInboundTokenZeta(ctx, tx, receipt, true) require.ErrorContains(t, err, "emitter address mismatch") }) } @@ -114,6 +119,8 @@ func Test_CheckAndVoteInboundTokenERC20(t *testing.T) { chainParam := mocks.MockChainParams(chain.ChainId, confirmation) inboundHash := "0x4ea69a0e2ff36f7548ab75791c3b990e076e2a4bffeb616035b239b7d33843da" + ctx := context.Background() + t.Run("should pass for archived inbound, receipt and cctx", func(t *testing.T) { tx, receipt, cctx := testutils.LoadEVMInboundNReceiptNCctx( t, @@ -126,7 +133,7 @@ func Test_CheckAndVoteInboundTokenERC20(t *testing.T) { lastBlock := receipt.BlockNumber.Uint64() + confirmation ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, lastBlock, chainParam) - ballot, err := ob.CheckAndVoteInboundTokenERC20(tx, receipt, false) + ballot, err := ob.CheckAndVoteInboundTokenERC20(ctx, tx, receipt, false) require.NoError(t, err) require.Equal(t, cctx.InboundParams.BallotIndex, ballot) }) @@ -142,7 +149,7 @@ func Test_CheckAndVoteInboundTokenERC20(t *testing.T) { lastBlock := receipt.BlockNumber.Uint64() + confirmation - 1 ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, lastBlock, chainParam) - _, err := ob.CheckAndVoteInboundTokenERC20(tx, receipt, false) + _, err := ob.CheckAndVoteInboundTokenERC20(ctx, tx, receipt, false) require.ErrorContains(t, err, "not been confirmed") }) t.Run("should not act if no Deposit event", func(t *testing.T) { @@ -158,7 +165,7 @@ func Test_CheckAndVoteInboundTokenERC20(t *testing.T) { lastBlock := receipt.BlockNumber.Uint64() + confirmation ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, lastBlock, chainParam) - ballot, err := ob.CheckAndVoteInboundTokenERC20(tx, receipt, true) + ballot, err := ob.CheckAndVoteInboundTokenERC20(ctx, tx, receipt, true) require.NoError(t, err) require.Equal(t, "", ballot) }) @@ -185,7 +192,7 @@ func Test_CheckAndVoteInboundTokenERC20(t *testing.T) { lastBlock, mocks.MockChainParams(chainID, confirmation), ) - _, err := ob.CheckAndVoteInboundTokenERC20(tx, receipt, true) + _, err := ob.CheckAndVoteInboundTokenERC20(ctx, tx, receipt, true) require.ErrorContains(t, err, "emitter address mismatch") }) } @@ -199,6 +206,8 @@ func Test_CheckAndVoteInboundTokenGas(t *testing.T) { chainParam := mocks.MockChainParams(chain.ChainId, confirmation) inboundHash := "0xeaec67d5dd5d85f27b21bef83e01cbdf59154fd793ea7a22c297f7c3a722c532" + ctx := context.Background() + t.Run("should pass for archived inbound, receipt and cctx", func(t *testing.T) { tx, receipt, cctx := testutils.LoadEVMInboundNReceiptNCctx( t, @@ -211,7 +220,7 @@ func Test_CheckAndVoteInboundTokenGas(t *testing.T) { lastBlock := receipt.BlockNumber.Uint64() + confirmation ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, lastBlock, chainParam) - ballot, err := ob.CheckAndVoteInboundTokenGas(tx, receipt, false) + ballot, err := ob.CheckAndVoteInboundTokenGas(ctx, tx, receipt, false) require.NoError(t, err) require.Equal(t, cctx.InboundParams.BallotIndex, ballot) }) @@ -221,7 +230,7 @@ func Test_CheckAndVoteInboundTokenGas(t *testing.T) { lastBlock := receipt.BlockNumber.Uint64() + confirmation - 1 ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, lastBlock, chainParam) - _, err := ob.CheckAndVoteInboundTokenGas(tx, receipt, false) + _, err := ob.CheckAndVoteInboundTokenGas(ctx, tx, receipt, false) require.ErrorContains(t, err, "not been confirmed") }) t.Run("should not act if receiver is not TSS", func(t *testing.T) { @@ -231,7 +240,7 @@ func Test_CheckAndVoteInboundTokenGas(t *testing.T) { lastBlock := receipt.BlockNumber.Uint64() + confirmation ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, lastBlock, chainParam) - ballot, err := ob.CheckAndVoteInboundTokenGas(tx, receipt, false) + ballot, err := ob.CheckAndVoteInboundTokenGas(ctx, tx, receipt, false) require.ErrorContains(t, err, "not TSS address") require.Equal(t, "", ballot) }) @@ -242,7 +251,7 @@ func Test_CheckAndVoteInboundTokenGas(t *testing.T) { lastBlock := receipt.BlockNumber.Uint64() + confirmation ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, lastBlock, chainParam) - ballot, err := ob.CheckAndVoteInboundTokenGas(tx, receipt, false) + ballot, err := ob.CheckAndVoteInboundTokenGas(ctx, tx, receipt, false) require.ErrorContains(t, err, "not a successful tx") require.Equal(t, "", ballot) }) @@ -253,7 +262,7 @@ func Test_CheckAndVoteInboundTokenGas(t *testing.T) { lastBlock := receipt.BlockNumber.Uint64() + confirmation ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, lastBlock, chainParam) - ballot, err := ob.CheckAndVoteInboundTokenGas(tx, receipt, false) + ballot, err := ob.CheckAndVoteInboundTokenGas(ctx, tx, receipt, false) require.NoError(t, err) require.Equal(t, "", ballot) }) @@ -278,8 +287,10 @@ func Test_BuildInboundVoteMsgForZetaSentEvent(t *testing.T) { ComplianceConfig: config.ComplianceConfig{}, } + _, app := makeAppContext(t) + t.Run("should return vote msg for archived ZetaSent event", func(t *testing.T) { - msg := ob.BuildInboundVoteMsgForZetaSentEvent(event) + msg := ob.BuildInboundVoteMsgForZetaSentEvent(app, event) require.NotNil(t, msg) require.Equal(t, cctx.InboundParams.BallotIndex, msg.Digest()) }) @@ -287,21 +298,21 @@ func Test_BuildInboundVoteMsgForZetaSentEvent(t *testing.T) { sender := event.ZetaTxSenderAddress.Hex() cfg.ComplianceConfig.RestrictedAddresses = []string{sender} config.LoadComplianceConfig(cfg) - msg := ob.BuildInboundVoteMsgForZetaSentEvent(event) + msg := ob.BuildInboundVoteMsgForZetaSentEvent(app, event) require.Nil(t, msg) }) t.Run("should return nil msg if receiver is restricted", func(t *testing.T) { receiver := clienttypes.BytesToEthHex(event.DestinationAddress) cfg.ComplianceConfig.RestrictedAddresses = []string{receiver} config.LoadComplianceConfig(cfg) - msg := ob.BuildInboundVoteMsgForZetaSentEvent(event) + msg := ob.BuildInboundVoteMsgForZetaSentEvent(app, event) require.Nil(t, msg) }) t.Run("should return nil msg if txOrigin is restricted", func(t *testing.T) { txOrigin := event.SourceTxOriginAddress.Hex() cfg.ComplianceConfig.RestrictedAddresses = []string{txOrigin} config.LoadComplianceConfig(cfg) - msg := ob.BuildInboundVoteMsgForZetaSentEvent(event) + msg := ob.BuildInboundVoteMsgForZetaSentEvent(app, event) require.Nil(t, msg) }) } @@ -439,41 +450,46 @@ func Test_ObserveTSSReceiveInBlock(t *testing.T) { // create mock client evmClient := mocks.NewMockEvmClient() evmJSONRPC := mocks.NewMockJSONRPCClient() - zetacoreClient := mocks.NewMockZetacoreClient().WithKeys(&keys.Keys{}) tss := mocks.NewTSSMainnet() lastBlock := receipt.BlockNumber.Uint64() + confirmation + zetacoreClient := mocks.NewZetacoreClient(t). + WithKeys(&keys.Keys{}). + WithZetaChain(). + WithPostVoteInbound("", ""). + WithPostVoteInbound("", "") + + ctx := context.Background() + t.Run("should observe TSS receive in block", func(t *testing.T) { ob := MockEVMObserver(t, chain, evmClient, evmJSONRPC, zetacoreClient, tss, memDBPath, lastBlock, chainParam) // feed archived block and receipt evmJSONRPC.WithBlock(block) evmClient.WithReceipt(receipt) - err := ob.ObserveTSSReceiveInBlock(blockNumber) + err := ob.ObserveTSSReceiveInBlock(ctx, blockNumber) require.NoError(t, err) }) t.Run("should not observe on error getting block", func(t *testing.T) { ob := MockEVMObserver(t, chain, evmClient, evmJSONRPC, zetacoreClient, tss, memDBPath, lastBlock, chainParam) - err := ob.ObserveTSSReceiveInBlock(blockNumber) + err := ob.ObserveTSSReceiveInBlock(ctx, blockNumber) // error getting block is expected because the mock JSONRPC contains no block require.ErrorContains(t, err, "error getting block") }) t.Run("should not observe on error getting receipt", func(t *testing.T) { ob := MockEVMObserver(t, chain, evmClient, evmJSONRPC, zetacoreClient, tss, memDBPath, lastBlock, chainParam) evmJSONRPC.WithBlock(block) - err := ob.ObserveTSSReceiveInBlock(blockNumber) + err := ob.ObserveTSSReceiveInBlock(ctx, blockNumber) // error getting block is expected because the mock evmClient contains no receipt require.ErrorContains(t, err, "error getting receipt") }) - t.Run("should not observe on error posting vote", func(t *testing.T) { - ob := MockEVMObserver(t, chain, evmClient, evmJSONRPC, zetacoreClient, tss, memDBPath, lastBlock, chainParam) +} - // feed archived block and pause zetacore client - evmJSONRPC.WithBlock(block) - evmClient.WithReceipt(receipt) - zetacoreClient.Pause() - err := ob.ObserveTSSReceiveInBlock(blockNumber) - // error posting vote is expected because the mock zetacoreClient is paused - require.ErrorContains(t, err, "error checking and voting") - }) +func makeAppContext(_ *testing.T) (context.Context, *zctx.AppContext) { + var ( + app = zctx.New(config.New(false), zerolog.Nop()) + ctx = context.Background() + ) + + return zctx.WithAppContext(ctx, app), app } diff --git a/zetaclient/chains/evm/observer/observer.go b/zetaclient/chains/evm/observer/observer.go index 3a60b16e40..126c8baf65 100644 --- a/zetaclient/chains/evm/observer/observer.go +++ b/zetaclient/chains/evm/observer/observer.go @@ -19,13 +19,13 @@ import ( zetaconnectoreth "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.eth.sol" "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.non-eth.sol" + "github.com/zeta-chain/zetacore/pkg/bg" "github.com/zeta-chain/zetacore/pkg/proofs" observertypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/chains/base" "github.com/zeta-chain/zetacore/zetaclient/chains/evm" "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" "github.com/zeta-chain/zetacore/zetaclient/config" - clientcontext "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/zetaclient/metrics" clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" ) @@ -55,10 +55,10 @@ type Observer struct { // NewObserver returns a new EVM chain observer func NewObserver( + ctx context.Context, evmCfg config.EVMConfig, evmClient interfaces.EVMRPCClient, chainParams observertypes.ChainParams, - appClient *clientcontext.AppContext, zetacoreClient interfaces.ZetacoreClient, tss interfaces.TSSSigner, dbpath string, @@ -69,7 +69,6 @@ func NewObserver( baseObserver, err := base.NewObserver( evmCfg.Chain, chainParams, - appClient, zetacoreClient, tss, base.DefaultBlockCacheSize, @@ -92,7 +91,7 @@ func NewObserver( } // open database and load data - err = ob.LoadDB(dbpath) + err = ob.LoadDB(ctx, dbpath) if err != nil { return nil, err } @@ -166,29 +165,20 @@ func FetchZetaTokenContract( } // Start all observation routines for the evm chain -func (ob *Observer) Start() { +func (ob *Observer) Start(ctx context.Context) { ob.Logger().Chain.Info().Msgf("observer is starting for chain %d", ob.Chain().ChainId) - // watch evm chain for incoming txs and post votes to zetacore - go ob.WatchInbound() - - // watch evm chain for outgoing txs status - go ob.WatchOutbound() - - // watch evm chain for gas prices and post to zetacore - go ob.WatchGasPrice() - - // watch zetacore for inbound trackers - go ob.WatchInboundTracker() - - // watch the RPC status of the evm chain - go ob.WatchRPCStatus() + bg.Work(ctx, ob.WatchInbound, bg.WithName("WatchInbound"), bg.WithLogger(ob.Logger().Inbound)) + bg.Work(ctx, ob.WatchOutbound, bg.WithName("WatchOutbound"), bg.WithLogger(ob.Logger().Outbound)) + bg.Work(ctx, ob.WatchGasPrice, bg.WithName("WatchGasPrice"), bg.WithLogger(ob.Logger().GasPrice)) + bg.Work(ctx, ob.WatchInboundTracker, bg.WithName("WatchInboundTracker"), bg.WithLogger(ob.Logger().Inbound)) + bg.Work(ctx, ob.WatchRPCStatus, bg.WithName("WatchRPCStatus"), bg.WithLogger(ob.Logger().Chain)) } // WatchRPCStatus watches the RPC status of the evm chain // TODO(revamp): move ticker to ticker file // TODO(revamp): move inner logic to a separate function -func (ob *Observer) WatchRPCStatus() { +func (ob *Observer) WatchRPCStatus(ctx context.Context) error { ob.Logger().Chain.Info().Msgf("Starting RPC status check for chain %d", ob.Chain().ChainId) ticker := time.NewTicker(60 * time.Second) for { @@ -197,22 +187,22 @@ func (ob *Observer) WatchRPCStatus() { if !ob.GetChainParams().IsSupported { continue } - bn, err := ob.evmClient.BlockNumber(context.Background()) + bn, err := ob.evmClient.BlockNumber(ctx) if err != nil { ob.Logger().Chain.Error().Err(err).Msg("RPC Status Check error: RPC down?") continue } - gasPrice, err := ob.evmClient.SuggestGasPrice(context.Background()) + gasPrice, err := ob.evmClient.SuggestGasPrice(ctx) if err != nil { ob.Logger().Chain.Error().Err(err).Msg("RPC Status Check error: RPC down?") continue } - header, err := ob.evmClient.HeaderByNumber(context.Background(), new(big.Int).SetUint64(bn)) + header, err := ob.evmClient.HeaderByNumber(ctx, new(big.Int).SetUint64(bn)) if err != nil { ob.Logger().Chain.Error().Err(err).Msg("RPC Status Check error: RPC down?") continue } - // #nosec G701 always in range + // #nosec G115 always in range blockTime := time.Unix(int64(header.Time), 0).UTC() elapsedSeconds := time.Since(blockTime).Seconds() if elapsedSeconds > 100 { @@ -223,7 +213,7 @@ func (ob *Observer) WatchRPCStatus() { ob.Logger().Chain.Info(). Msgf("[OK] RPC status: latest block num %d, timestamp %s ( %.0fs ago), suggested gas price %d", header.Number, blockTime.String(), elapsedSeconds, gasPrice.Uint64()) case <-ob.StopChannel(): - return + return nil } } } @@ -276,7 +266,7 @@ func (ob *Observer) CheckTxInclusion(tx *ethtypes.Transaction, receipt *ethtypes receipt.BlockNumber.Uint64(), tx.Hash(), tx.Nonce()) } - // #nosec G701 non negative value + // #nosec G115 non negative value if receipt.TransactionIndex >= uint(len(block.Transactions)) { return fmt.Errorf("transaction index %d out of range [0, %d), txHash %s nonce %d block %d", receipt.TransactionIndex, len(block.Transactions), tx.Hash(), tx.Nonce(), receipt.BlockNumber.Uint64()) @@ -295,9 +285,9 @@ func (ob *Observer) CheckTxInclusion(tx *ethtypes.Transaction, receipt *ethtypes // WatchGasPrice watches evm chain for gas prices and post to zetacore // TODO(revamp): move ticker to ticker file // TODO(revamp): move inner logic to a separate function -func (ob *Observer) WatchGasPrice() { +func (ob *Observer) WatchGasPrice(ctx context.Context) error { // report gas price right away as the ticker takes time to kick in - err := ob.PostGasPrice() + err := ob.PostGasPrice(ctx) if err != nil { ob.Logger().GasPrice.Error().Err(err).Msgf("PostGasPrice error for chain %d", ob.Chain().ChainId) } @@ -309,7 +299,7 @@ func (ob *Observer) WatchGasPrice() { ) if err != nil { ob.Logger().GasPrice.Error().Err(err).Msg("NewDynamicTicker error") - return + return err } ob.Logger().GasPrice.Info().Msgf("WatchGasPrice started for chain %d with interval %d", ob.Chain().ChainId, ob.GetChainParams().GasPriceTicker) @@ -321,28 +311,28 @@ func (ob *Observer) WatchGasPrice() { if !ob.GetChainParams().IsSupported { continue } - err = ob.PostGasPrice() + err = ob.PostGasPrice(ctx) if err != nil { ob.Logger().GasPrice.Error().Err(err).Msgf("PostGasPrice error for chain %d", ob.Chain().ChainId) } ticker.UpdateInterval(ob.GetChainParams().GasPriceTicker, ob.Logger().GasPrice) case <-ob.StopChannel(): ob.Logger().GasPrice.Info().Msg("WatchGasPrice stopped") - return + return nil } } } // PostGasPrice posts gas price to zetacore // TODO(revamp): move to gas price file -func (ob *Observer) PostGasPrice() error { +func (ob *Observer) PostGasPrice(ctx context.Context) error { // GAS PRICE - gasPrice, err := ob.evmClient.SuggestGasPrice(context.TODO()) + gasPrice, err := ob.evmClient.SuggestGasPrice(ctx) if err != nil { ob.Logger().GasPrice.Err(err).Msg("Err SuggestGasPrice:") return err } - blockNum, err := ob.evmClient.BlockNumber(context.TODO()) + blockNum, err := ob.evmClient.BlockNumber(ctx) if err != nil { ob.Logger().GasPrice.Err(err).Msg("Err Fetching Most recent Block : ") return err @@ -351,7 +341,7 @@ func (ob *Observer) PostGasPrice() error { // SUPPLY supply := "100" // lockedAmount on ETH, totalSupply on other chains - zetaHash, err := ob.ZetacoreClient().PostGasPrice(ob.Chain(), gasPrice.Uint64(), supply, blockNum) + zetaHash, err := ob.ZetacoreClient().PostVoteGasPrice(ctx, ob.Chain(), gasPrice.Uint64(), supply, blockNum) if err != nil { ob.Logger().GasPrice.Err(err).Msg("PostGasPrice to zetacore failed") return err @@ -376,14 +366,14 @@ func (ob *Observer) TransactionByHash(txHash string) (*ethrpc.Transaction, bool, } // GetBlockHeaderCached get block header by number from cache -func (ob *Observer) GetBlockHeaderCached(blockNumber uint64) (*ethtypes.Header, error) { +func (ob *Observer) GetBlockHeaderCached(ctx context.Context, blockNumber uint64) (*ethtypes.Header, error) { if result, ok := ob.HeaderCache().Get(blockNumber); ok { if header, ok := result.(*ethtypes.Header); ok { return header, nil } return nil, errors.New("cached value is not of type *ethtypes.Header") } - header, err := ob.evmClient.HeaderByNumber(context.Background(), new(big.Int).SetUint64(blockNumber)) + header, err := ob.evmClient.HeaderByNumber(ctx, new(big.Int).SetUint64(blockNumber)) if err != nil { return nil, err } @@ -403,7 +393,7 @@ func (ob *Observer) GetBlockByNumberCached(blockNumber uint64) (*ethrpc.Block, e if blockNumber > math.MaxInt32 { return nil, fmt.Errorf("block number %d is too large", blockNumber) } - // #nosec G701 always in range, checked above + // #nosec G115 always in range, checked above block, err := ob.BlockByNumber(int(blockNumber)) if err != nil { return nil, err @@ -434,7 +424,7 @@ func (ob *Observer) BlockByNumber(blockNumber int) (*ethrpc.Block, error) { // LoadDB open sql database and load data into EVM observer // TODO(revamp): move to a db file -func (ob *Observer) LoadDB(dbPath string) error { +func (ob *Observer) LoadDB(ctx context.Context, dbPath string) error { if dbPath == "" { return errors.New("empty db path") } @@ -456,14 +446,14 @@ func (ob *Observer) LoadDB(dbPath string) error { } // load last block scanned - err = ob.LoadLastBlockScanned() + err = ob.LoadLastBlockScanned(ctx) return err } // LoadLastBlockScanned loads the last scanned block from the database // TODO(revamp): move to a db file -func (ob *Observer) LoadLastBlockScanned() error { +func (ob *Observer) LoadLastBlockScanned(ctx context.Context) error { err := ob.Observer.LoadLastBlockScanned(ob.Logger().Chain) if err != nil { return errors.Wrapf(err, "error LoadLastBlockScanned for chain %d", ob.Chain().ChainId) @@ -473,7 +463,7 @@ func (ob *Observer) LoadLastBlockScanned() error { // 1. environment variable is set explicitly to "latest" // 2. environment variable is empty and last scanned block is not found in DB if ob.LastBlockScanned() == 0 { - blockNumber, err := ob.evmClient.BlockNumber(context.Background()) + blockNumber, err := ob.evmClient.BlockNumber(ctx) if err != nil { return errors.Wrapf(err, "error BlockNumber for chain %d", ob.Chain().ChainId) } @@ -486,20 +476,20 @@ func (ob *Observer) LoadLastBlockScanned() error { // postBlockHeader posts the block header to zetacore // TODO(revamp): move to a block header file -func (ob *Observer) postBlockHeader(tip uint64) error { +func (ob *Observer) postBlockHeader(ctx context.Context, tip uint64) error { bn := tip - res, err := ob.ZetacoreClient().GetBlockHeaderChainState(ob.Chain().ChainId) - if err == nil && res.ChainState != nil && res.ChainState.EarliestHeight > 0 { - // #nosec G701 always positive - bn = uint64(res.ChainState.LatestHeight) + 1 // the next header to post + chainState, err := ob.ZetacoreClient().GetBlockHeaderChainState(ctx, ob.Chain().ChainId) + if err == nil && chainState != nil && chainState.EarliestHeight > 0 { + // #nosec G115 always positive + bn = uint64(chainState.LatestHeight) + 1 // the next header to post } if bn > tip { return fmt.Errorf("postBlockHeader: must post block confirmed block header: %d > %d", bn, tip) } - header, err := ob.GetBlockHeaderCached(bn) + header, err := ob.GetBlockHeaderCached(ctx, bn) if err != nil { ob.Logger().Inbound.Error().Err(err).Msgf("postBlockHeader: error getting block: %d", bn) return err @@ -511,6 +501,7 @@ func (ob *Observer) postBlockHeader(tip uint64) error { } _, err = ob.ZetacoreClient().PostVoteBlockHeader( + ctx, ob.Chain().ChainId, header.Hash().Bytes(), header.Number.Int64(), diff --git a/zetaclient/chains/evm/observer/observer_test.go b/zetaclient/chains/evm/observer/observer_test.go index e38ff877a1..cc7c9f0a68 100644 --- a/zetaclient/chains/evm/observer/observer_test.go +++ b/zetaclient/chains/evm/observer/observer_test.go @@ -1,6 +1,7 @@ package observer_test import ( + "context" "fmt" "math/big" "os" @@ -12,6 +13,8 @@ import ( "github.com/onrik/ethrpc" "github.com/rs/zerolog" "github.com/stretchr/testify/require" + zctx "github.com/zeta-chain/zetacore/zetaclient/context" + "github.com/zeta-chain/zetacore/zetaclient/keys" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" @@ -22,8 +25,6 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/chains/evm/observer" "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" "github.com/zeta-chain/zetacore/zetaclient/config" - "github.com/zeta-chain/zetacore/zetaclient/context" - "github.com/zeta-chain/zetacore/zetaclient/keys" "github.com/zeta-chain/zetacore/zetaclient/metrics" "github.com/zeta-chain/zetacore/zetaclient/testutils" "github.com/zeta-chain/zetacore/zetaclient/testutils/mocks" @@ -37,21 +38,21 @@ func getZetacoreContext( evmChain chains.Chain, endpoint string, evmChainParams *observertypes.ChainParams, -) (*context.AppContext, config.EVMConfig) { +) (*zctx.AppContext, config.EVMConfig) { // use default endpoint if not provided if endpoint == "" { endpoint = "http://localhost:8545" } // create config - cfg := config.NewConfig() + cfg := config.New(false) cfg.EVMChainConfigs[evmChain.ChainId] = config.EVMConfig{ Chain: evmChain, Endpoint: endpoint, } // create zetacore context - appContext := context.New(cfg, zerolog.Nop()) + appContext := zctx.New(cfg, zerolog.Nop()) evmChainParamsMap := make(map[int64]*observertypes.ChainParams) evmChainParamsMap[evmChain.ChainId] = evmChainParams @@ -83,6 +84,8 @@ func MockEVMObserver( lastBlock uint64, params observertypes.ChainParams, ) *observer.Observer { + ctx := context.Background() + // use default mock evm client if not provided if evmClient == nil { evmClient = mocks.NewMockEvmClient().WithBlockNumber(1000) @@ -90,17 +93,21 @@ func MockEVMObserver( // use default mock zetacore client if not provided if zetacoreClient == nil { - zetacoreClient = mocks.NewMockZetacoreClient().WithKeys(&keys.Keys{}) + zetacoreClient = mocks.NewZetacoreClient(t). + WithKeys(&keys.Keys{}). + WithZetaChain(). + WithPostVoteInbound("", ""). + WithPostVoteOutbound("", "") } // use default mock tss if not provided if tss == nil { tss = mocks.NewTSSMainnet() } // create zetacore context - coreCtx, evmCfg := getZetacoreContext(chain, "", ¶ms) + _, evmCfg := getZetacoreContext(chain, "", ¶ms) // create observer - ob, err := observer.NewObserver(evmCfg, evmClient, params, coreCtx, zetacoreClient, tss, dbpath, base.Logger{}, nil) + ob, err := observer.NewObserver(ctx, evmCfg, evmClient, params, zetacoreClient, tss, dbpath, base.Logger{}, nil) require.NoError(t, err) ob.WithEvmJSONRPC(evmJSONRPC) ob.WithLastBlock(lastBlock) @@ -109,6 +116,8 @@ func MockEVMObserver( } func Test_NewObserver(t *testing.T) { + ctx := context.Background() + // use Ethereum chain for testing chain := chains.Ethereum params := mocks.MockChainParams(chain.ChainId, 10) @@ -176,15 +185,15 @@ func Test_NewObserver(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // create zetacore context, client and tss - zetacoreCtx, _ := getZetacoreContext(tt.evmCfg.Chain, tt.evmCfg.Endpoint, ¶ms) - zetacoreClient := mocks.NewMockZetacoreClient().WithKeys(&keys.Keys{}) + //zetacoreCtx, _ := getZetacoreContext(tt.evmCfg.Chain, tt.evmCfg.Endpoint, ¶ms) + zetacoreClient := mocks.NewZetacoreClient(t) // create observer ob, err := observer.NewObserver( + ctx, tt.evmCfg, tt.evmClient, tt.chainParams, - zetacoreCtx, zetacoreClient, tt.tss, tt.dbpath, @@ -205,6 +214,8 @@ func Test_NewObserver(t *testing.T) { } func Test_LoadDB(t *testing.T) { + ctx := context.Background() + // use Ethereum chain for testing chain := chains.Ethereum params := mocks.MockChainParams(chain.ChainId, 10) @@ -212,17 +223,17 @@ func Test_LoadDB(t *testing.T) { ob := MockEVMObserver(t, chain, nil, nil, nil, nil, dbpath, 1, params) t.Run("should load db successfully", func(t *testing.T) { - err := ob.LoadDB(dbpath) + err := ob.LoadDB(ctx, dbpath) require.NoError(t, err) require.EqualValues(t, 1000, ob.LastBlockScanned()) }) t.Run("should fail on invalid dbpath", func(t *testing.T) { // load db with empty dbpath - err := ob.LoadDB("") + err := ob.LoadDB(ctx, "") require.ErrorContains(t, err, "empty db path") // load db with invalid dbpath - err = ob.LoadDB("/invalid/dbpath") + err = ob.LoadDB(ctx, "/invalid/dbpath") require.ErrorContains(t, err, "error OpenDB") }) t.Run("should fail on invalid env var", func(t *testing.T) { @@ -232,7 +243,7 @@ func Test_LoadDB(t *testing.T) { defer os.Unsetenv(envvar) // load db - err := ob.LoadDB(dbpath) + err := ob.LoadDB(ctx, dbpath) require.ErrorContains(t, err, "error LoadLastBlockScanned") }) t.Run("should fail on RPC error", func(t *testing.T) { @@ -244,12 +255,14 @@ func Test_LoadDB(t *testing.T) { tempClient.WithError(fmt.Errorf("error RPC")) // load db - err := ob.LoadDB(dbpath) + err := ob.LoadDB(ctx, dbpath) require.ErrorContains(t, err, "error RPC") }) } func Test_LoadLastBlockScanned(t *testing.T) { + ctx := context.Background() + // use Ethereum chain for testing chain := chains.Ethereum params := mocks.MockChainParams(chain.ChainId, 10) @@ -264,7 +277,7 @@ func Test_LoadLastBlockScanned(t *testing.T) { ob.WriteLastBlockScannedToDB(123) // load last block scanned - err := ob.LoadLastBlockScanned() + err := ob.LoadLastBlockScanned(ctx) require.NoError(t, err) require.EqualValues(t, 123, ob.LastBlockScanned()) }) @@ -275,7 +288,7 @@ func Test_LoadLastBlockScanned(t *testing.T) { defer os.Unsetenv(envvar) // load last block scanned - err := ob.LoadLastBlockScanned() + err := ob.LoadLastBlockScanned(ctx) require.ErrorContains(t, err, "error LoadLastBlockScanned") }) t.Run("should fail on RPC error", func(t *testing.T) { @@ -290,7 +303,7 @@ func Test_LoadLastBlockScanned(t *testing.T) { evmClient.WithError(fmt.Errorf("error RPC")) // load last block scanned - err := obOther.LoadLastBlockScanned() + err := obOther.LoadLastBlockScanned(ctx) require.ErrorContains(t, err, "error RPC") }) } @@ -364,6 +377,8 @@ func Test_BlockCache(t *testing.T) { } func Test_HeaderCache(t *testing.T) { + ctx := context.Background() + t.Run("should get block header from cache", func(t *testing.T) { // create observer ob := &observer.Observer{} @@ -380,7 +395,7 @@ func Test_HeaderCache(t *testing.T) { evmClient.WithHeader(header) // get block header from observer - resHeader, err := ob.GetBlockHeaderCached(uint64(100)) + resHeader, err := ob.GetBlockHeaderCached(ctx, uint64(100)) require.NoError(t, err) require.EqualValues(t, header, resHeader) }) @@ -396,7 +411,7 @@ func Test_HeaderCache(t *testing.T) { headerCache.Add(blockNumber, "a string value") // get block header from cache - header, err := ob.GetBlockHeaderCached(blockNumber) + header, err := ob.GetBlockHeaderCached(ctx, blockNumber) require.ErrorContains(t, err, "cached value is not of type *ethtypes.Header") require.Nil(t, header) }) @@ -431,7 +446,7 @@ func Test_CheckTxInclusion(t *testing.T) { t.Run("should fail on tx index out of range", func(t *testing.T) { // modify tx index to invalid number copyReceipt := *receipt - // #nosec G701 non negative value + // #nosec G115 non negative value copyReceipt.TransactionIndex = uint(len(block.Transactions)) err := ob.CheckTxInclusion(tx, ©Receipt) require.ErrorContains(t, err, "out of range") diff --git a/zetaclient/chains/evm/observer/outbound.go b/zetaclient/chains/evm/observer/outbound.go index 84103620dd..54ac2aab1d 100644 --- a/zetaclient/chains/evm/observer/outbound.go +++ b/zetaclient/chains/evm/observer/outbound.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "cosmossdk.io/math" "github.com/ethereum/go-ethereum" ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" @@ -23,7 +24,9 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/chains/evm" "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" "github.com/zeta-chain/zetacore/zetaclient/compliance" + zctx "github.com/zeta-chain/zetacore/zetaclient/context" clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" + "github.com/zeta-chain/zetacore/zetaclient/zetacore" ) // GetTxID returns a unique id for outbound tx @@ -35,14 +38,19 @@ func (ob *Observer) GetTxID(nonce uint64) string { // WatchOutbound watches evm chain for outgoing txs status // TODO(revamp): move ticker function to ticker file // TODO(revamp): move inner logic to a separate function -func (ob *Observer) WatchOutbound() { +func (ob *Observer) WatchOutbound(ctx context.Context) error { ticker, err := clienttypes.NewDynamicTicker( fmt.Sprintf("EVM_WatchOutbound_%d", ob.Chain().ChainId), ob.GetChainParams().OutboundTicker, ) if err != nil { ob.Logger().Outbound.Error().Err(err).Msg("error creating ticker") - return + return err + } + + app, err := zctx.FromContext(ctx) + if err != nil { + return err } ob.Logger().Outbound.Info().Msgf("WatchOutbound started for chain %d", ob.Chain().ChainId) @@ -51,12 +59,13 @@ func (ob *Observer) WatchOutbound() { for { select { case <-ticker.C(): - if !ob.AppContext().IsOutboundObservationEnabled(ob.GetChainParams()) { + if !app.IsOutboundObservationEnabled(ob.GetChainParams()) { sampledLogger.Info(). Msgf("WatchOutbound: outbound observation is disabled for chain %d", ob.Chain().ChainId) continue } - trackers, err := ob.ZetacoreClient().GetAllOutboundTrackerByChain(ob.Chain().ChainId, interfaces.Ascending) + trackers, err := ob.ZetacoreClient(). + GetAllOutboundTrackerByChain(ctx, ob.Chain().ChainId, interfaces.Ascending) if err != nil { continue } @@ -69,7 +78,7 @@ func (ob *Observer) WatchOutbound() { var outboundReceipt *ethtypes.Receipt var outbound *ethtypes.Transaction for _, txHash := range tracker.HashList { - if receipt, tx, ok := ob.checkConfirmedTx(txHash.TxHash, nonceInt); ok { + if receipt, tx, ok := ob.checkConfirmedTx(ctx, txHash.TxHash, nonceInt); ok { txCount++ outboundReceipt = receipt outbound = tx @@ -90,49 +99,78 @@ func (ob *Observer) WatchOutbound() { ticker.UpdateInterval(ob.GetChainParams().OutboundTicker, ob.Logger().Outbound) case <-ob.StopChannel(): ob.Logger().Outbound.Info().Msg("WatchOutbound: stopped") - return + return nil } } } // PostVoteOutbound posts vote to zetacore for the confirmed outbound func (ob *Observer) PostVoteOutbound( + ctx context.Context, cctxIndex string, receipt *ethtypes.Receipt, transaction *ethtypes.Transaction, receiveValue *big.Int, receiveStatus chains.ReceiveStatus, nonce uint64, - cointype coin.CoinType, + coinType coin.CoinType, logger zerolog.Logger, ) { chainID := ob.Chain().ChainId - zetaTxHash, ballot, err := ob.ZetacoreClient().PostVoteOutbound( + logFields := map[string]any{ + "outbound.chain_id": chainID, + "outbound.external_tx_hash": receipt.TxHash.String(), + "outbound.nonce": nonce, + } + + signerAddress := ob.ZetacoreClient().GetKeys().GetOperatorAddress() + + msg := crosschaintypes.NewMsgVoteOutbound( + signerAddress.String(), cctxIndex, receipt.TxHash.Hex(), receipt.BlockNumber.Uint64(), receipt.GasUsed, - transaction.GasPrice(), + math.NewIntFromBigInt(transaction.GasPrice()), transaction.Gas(), - receiveValue, + math.NewUintFromBigInt(receiveValue), receiveStatus, - ob.Chain(), + chainID, nonce, - cointype, + coinType, ) + + const gasLimit = zetacore.PostVoteOutboundGasLimit + + var retryGasLimit uint64 + if msg.Status == chains.ReceiveStatus_failed { + retryGasLimit = zetacore.PostVoteOutboundRevertGasLimit + } + + zetaTxHash, ballot, err := ob.ZetacoreClient().PostVoteOutbound(ctx, gasLimit, retryGasLimit, msg) if err != nil { - logger.Error(). - Err(err). - Msgf("PostVoteOutbound: error posting vote for chain %d nonce %d outbound %s ", chainID, nonce, receipt.TxHash) - } else if zetaTxHash != "" { - logger.Info().Msgf("PostVoteOutbound: posted vote for chain %d nonce %d outbound %s vote %s ballot %s", chainID, nonce, receipt.TxHash, zetaTxHash, ballot) + logger.Error().Err(err).Fields(logFields).Msgf("PostVoteOutbound: error posting vote for chain %d", chainID) + return + } + + if zetaTxHash == "" { + return } + + logFields["outbound.zeta_tx_hash"] = zetaTxHash + logFields["outbound.ballot"] = ballot + + logger.Info().Fields(logFields).Msgf("PostVoteOutbound: posted vote for chain %d", chainID) } // IsOutboundProcessed checks outbound status and returns (isIncluded, isConfirmed, error) // It also posts vote to zetacore if the tx is confirmed // TODO(revamp): rename as it also vote the outbound -func (ob *Observer) IsOutboundProcessed(cctx *crosschaintypes.CrossChainTx, logger zerolog.Logger) (bool, bool, error) { +func (ob *Observer) IsOutboundProcessed( + ctx context.Context, + cctx *crosschaintypes.CrossChainTx, + logger zerolog.Logger, +) (bool, bool, error) { // skip if outbound is not confirmed nonce := cctx.GetCurrentOutboundParam().TssNonce if !ob.IsTxConfirmed(nonce) { @@ -165,7 +203,7 @@ func (ob *Observer) IsOutboundProcessed(cctx *crosschaintypes.CrossChainTx, logg if receipt.Status == ethtypes.ReceiptStatusSuccessful { receiveStatus = chains.ReceiveStatus_success } - ob.PostVoteOutbound(cctx.Index, receipt, transaction, receiveValue, receiveStatus, nonce, cointype, logger) + ob.PostVoteOutbound(ctx, cctx.Index, receipt, transaction, receiveValue, receiveStatus, nonce, cointype, logger) return true, true, nil } @@ -188,7 +226,7 @@ func (ob *Observer) IsOutboundProcessed(cctx *crosschaintypes.CrossChainTx, logg } // post vote to zetacore - ob.PostVoteOutbound(cctx.Index, receipt, transaction, receiveValue, receiveStatus, nonce, cointype, logger) + ob.PostVoteOutbound(ctx, cctx.Index, receipt, transaction, receiveValue, receiveStatus, nonce, cointype, logger) return true, true, nil } @@ -339,12 +377,16 @@ func ParseOutboundReceivedValue( // checkConfirmedTx checks if a txHash is confirmed // returns (receipt, transaction, true) if confirmed or (nil, nil, false) otherwise -func (ob *Observer) checkConfirmedTx(txHash string, nonce uint64) (*ethtypes.Receipt, *ethtypes.Transaction, bool) { - ctxt, cancel := context.WithTimeout(context.Background(), 3*time.Second) +func (ob *Observer) checkConfirmedTx( + ctx context.Context, + txHash string, + nonce uint64, +) (*ethtypes.Receipt, *ethtypes.Transaction, bool) { + ctx, cancel := context.WithTimeout(ctx, 3*time.Second) defer cancel() // query transaction - transaction, isPending, err := ob.evmClient.TransactionByHash(ctxt, ethcommon.HexToHash(txHash)) + transaction, isPending, err := ob.evmClient.TransactionByHash(ctx, ethcommon.HexToHash(txHash)) if err != nil { log.Error(). Err(err). @@ -383,7 +425,7 @@ func (ob *Observer) checkConfirmedTx(txHash string, nonce uint64) (*ethtypes.Rec } // query receipt - receipt, err := ob.evmClient.TransactionReceipt(ctxt, ethcommon.HexToHash(txHash)) + 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) diff --git a/zetaclient/chains/evm/observer/outbound_test.go b/zetaclient/chains/evm/observer/outbound_test.go index 72023d8f57..7342139343 100644 --- a/zetaclient/chains/evm/observer/outbound_test.go +++ b/zetaclient/chains/evm/observer/outbound_test.go @@ -1,6 +1,7 @@ package observer_test import ( + "context" "testing" ethcommon "github.com/ethereum/go-ethereum/common" @@ -8,7 +9,6 @@ import ( "github.com/stretchr/testify/require" "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/erc20custody.sol" "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.non-eth.sol" - "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/testutil/sample" @@ -58,13 +58,15 @@ func Test_IsOutboundProcessed(t *testing.T) { testutils.EventZetaReceived, ) + ctx := context.Background() + t.Run("should post vote and return true if outbound is processed", func(t *testing.T) { // create evm observer and set outbound and receipt ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, 1, chainParam) ob.SetTxNReceipt(nonce, receipt, outbound) // post outbound vote - isIncluded, isConfirmed, err := ob.IsOutboundProcessed(cctx, zerolog.Logger{}) + isIncluded, isConfirmed, err := ob.IsOutboundProcessed(ctx, cctx, zerolog.Nop()) require.NoError(t, err) require.True(t, isIncluded) require.True(t, isConfirmed) @@ -88,7 +90,7 @@ func Test_IsOutboundProcessed(t *testing.T) { config.LoadComplianceConfig(cfg) // post outbound vote - isIncluded, isConfirmed, err := ob.IsOutboundProcessed(cctx, zerolog.Logger{}) + isIncluded, isConfirmed, err := ob.IsOutboundProcessed(ctx, cctx, zerolog.Nop()) require.NoError(t, err) require.True(t, isIncluded) require.True(t, isConfirmed) @@ -96,7 +98,7 @@ func Test_IsOutboundProcessed(t *testing.T) { t.Run("should return false if outbound is not confirmed", func(t *testing.T) { // create evm observer and DO NOT set outbound as confirmed ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, 1, chainParam) - isIncluded, isConfirmed, err := ob.IsOutboundProcessed(cctx, zerolog.Logger{}) + isIncluded, isConfirmed, err := ob.IsOutboundProcessed(ctx, cctx, zerolog.Nop()) require.NoError(t, err) require.False(t, isIncluded) require.False(t, isConfirmed) @@ -110,7 +112,7 @@ func Test_IsOutboundProcessed(t *testing.T) { chainParamsNew := ob.GetChainParams() chainParamsNew.ConnectorContractAddress = sample.EthAddress().Hex() ob.SetChainParams(chainParamsNew) - isIncluded, isConfirmed, err := ob.IsOutboundProcessed(cctx, zerolog.Logger{}) + isIncluded, isConfirmed, err := ob.IsOutboundProcessed(ctx, cctx, zerolog.Nop()) require.Error(t, err) require.False(t, isIncluded) require.False(t, isConfirmed) @@ -149,6 +151,8 @@ func Test_IsOutboundProcessed_ContractError(t *testing.T) { testutils.EventZetaReceived, ) + ctx := context.Background() + t.Run("should fail if unable to get connector/custody contract", func(t *testing.T) { // create evm observer and set outbound and receipt ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, 1, chainParam) @@ -158,7 +162,7 @@ func Test_IsOutboundProcessed_ContractError(t *testing.T) { // set invalid connector ABI zetaconnector.ZetaConnectorNonEthMetaData.ABI = "invalid abi" - isIncluded, isConfirmed, err := ob.IsOutboundProcessed(cctx, zerolog.Logger{}) + isIncluded, isConfirmed, err := ob.IsOutboundProcessed(ctx, cctx, zerolog.Nop()) zetaconnector.ZetaConnectorNonEthMetaData.ABI = abiConnector // reset connector ABI require.ErrorContains(t, err, "error getting zeta connector") require.False(t, isIncluded) @@ -166,7 +170,7 @@ func Test_IsOutboundProcessed_ContractError(t *testing.T) { // set invalid custody ABI erc20custody.ERC20CustodyMetaData.ABI = "invalid abi" - isIncluded, isConfirmed, err = ob.IsOutboundProcessed(cctx, zerolog.Logger{}) + isIncluded, isConfirmed, err = ob.IsOutboundProcessed(ctx, cctx, zerolog.Nop()) require.ErrorContains(t, err, "error getting erc20 custody") require.False(t, isIncluded) require.False(t, isConfirmed) @@ -189,28 +193,17 @@ func Test_PostVoteOutbound(t *testing.T) { testutils.EventZetaReceived, ) + ctx := context.Background() + t.Run("post vote outbound successfully", func(t *testing.T) { // the amount and status to be used for vote receiveValue := cctx.GetCurrentOutboundParam().Amount.BigInt() receiveStatus := chains.ReceiveStatus_success // create evm client using mock zetacore client and post outbound vote - zetacoreClient := mocks.NewMockZetacoreClient() - ob := MockEVMObserver(t, chain, nil, nil, zetacoreClient, nil, memDBPath, 1, observertypes.ChainParams{}) - ob.PostVoteOutbound( - cctx.Index, - receipt, - outbound, - receiveValue, - receiveStatus, - nonce, - coinType, - zerolog.Logger{}, - ) - - // pause the mock zetacore client to simulate error posting vote - zetacoreClient.Pause() + ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, 1, observertypes.ChainParams{}) ob.PostVoteOutbound( + ctx, cctx.Index, receipt, outbound, @@ -218,7 +211,7 @@ func Test_PostVoteOutbound(t *testing.T) { receiveStatus, nonce, coinType, - zerolog.Logger{}, + zerolog.Nop(), ) }) } diff --git a/zetaclient/chains/evm/signer/outbound_data.go b/zetaclient/chains/evm/signer/outbound_data.go index 3e84fa83db..2509b79198 100644 --- a/zetaclient/chains/evm/signer/outbound_data.go +++ b/zetaclient/chains/evm/signer/outbound_data.go @@ -16,7 +16,7 @@ import ( "github.com/zeta-chain/zetacore/x/crosschain/types" "github.com/zeta-chain/zetacore/zetaclient/chains/evm/observer" "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" - clientcontext "github.com/zeta-chain/zetacore/zetaclient/context" + zctx "github.com/zeta-chain/zetacore/zetaclient/context" ) const ( @@ -112,7 +112,7 @@ func (txData *OutboundData) SetupGas( // cctx will be skipped and false otherwise. // 3. error func NewOutboundData( - appontext *clientcontext.AppContext, + ctx context.Context, cctx *types.CrossChainTx, evmObserver *observer.Observer, evmRPC interfaces.EVMRPCClient, @@ -134,14 +134,19 @@ func NewOutboundData( return nil, true, nil } - toChain, found := chains.GetChainFromChainID(txData.toChainID.Int64(), appontext.GetAdditionalChains()) + app, err := zctx.FromContext(ctx) + if err != nil { + return nil, false, err + } + + toChain, found := chains.GetChainFromChainID(txData.toChainID.Int64(), app.GetAdditionalChains()) if !found { return nil, true, fmt.Errorf("unknown chain: %d", txData.toChainID.Int64()) } // Get nonce, Early return if the cctx is already processed nonce := cctx.GetCurrentOutboundParam().TssNonce - included, confirmed, err := evmObserver.IsOutboundProcessed(cctx, logger) + included, confirmed, err := evmObserver.IsOutboundProcessed(ctx, cctx, logger) if err != nil { return nil, true, errors.New("IsOutboundProcessed failed") } diff --git a/zetaclient/chains/evm/signer/outbound_data_test.go b/zetaclient/chains/evm/signer/outbound_data_test.go index f5e3d39d3b..ac2b7061b5 100644 --- a/zetaclient/chains/evm/signer/outbound_data_test.go +++ b/zetaclient/chains/evm/signer/outbound_data_test.go @@ -1,12 +1,15 @@ package signer import ( + "context" "math/big" "testing" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/rs/zerolog" "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/zetaclient/config" + zctx "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/x/crosschain/types" @@ -66,6 +69,9 @@ func TestSigner_SetupGas(t *testing.T) { } func TestSigner_NewOutboundData(t *testing.T) { + app := zctx.New(config.New(false), zerolog.Nop()) + ctx := zctx.WithAppContext(context.Background(), app) + // Setup evm signer evmSigner, err := getNewEvmSigner(nil) require.NoError(t, err) @@ -75,14 +81,7 @@ func TestSigner_NewOutboundData(t *testing.T) { t.Run("NewOutboundData success", func(t *testing.T) { cctx := getCCTX(t) - _, skip, err := NewOutboundData( - evmSigner.AppContext(), - cctx, - mockObserver, - evmSigner.EvmClient(), - zerolog.Logger{}, - 123, - ) + _, skip, err := NewOutboundData(ctx, cctx, mockObserver, evmSigner.EvmClient(), zerolog.Logger{}, 123) require.False(t, skip) require.NoError(t, err) }) @@ -90,14 +89,7 @@ func TestSigner_NewOutboundData(t *testing.T) { t.Run("NewOutboundData skip", func(t *testing.T) { cctx := getCCTX(t) cctx.CctxStatus.Status = types.CctxStatus_Aborted - _, skip, err := NewOutboundData( - evmSigner.AppContext(), - cctx, - mockObserver, - evmSigner.EvmClient(), - zerolog.Logger{}, - 123, - ) + _, skip, err := NewOutboundData(ctx, cctx, mockObserver, evmSigner.EvmClient(), zerolog.Logger{}, 123) require.NoError(t, err) require.True(t, skip) }) @@ -105,14 +97,7 @@ func TestSigner_NewOutboundData(t *testing.T) { t.Run("NewOutboundData unknown chain", func(t *testing.T) { cctx := getInvalidCCTX(t) require.NoError(t, err) - _, skip, err := NewOutboundData( - evmSigner.AppContext(), - cctx, - mockObserver, - evmSigner.EvmClient(), - zerolog.Logger{}, - 123, - ) + _, skip, err := NewOutboundData(ctx, cctx, mockObserver, evmSigner.EvmClient(), zerolog.Logger{}, 123) require.ErrorContains(t, err, "unknown chain") require.True(t, skip) }) @@ -121,14 +106,7 @@ func TestSigner_NewOutboundData(t *testing.T) { cctx := getCCTX(t) require.NoError(t, err) cctx.GetCurrentOutboundParam().GasPrice = "invalidGasPrice" - _, skip, err := NewOutboundData( - evmSigner.AppContext(), - cctx, - mockObserver, - evmSigner.EvmClient(), - zerolog.Logger{}, - 123, - ) + _, skip, err := NewOutboundData(ctx, cctx, mockObserver, evmSigner.EvmClient(), zerolog.Logger{}, 123) require.True(t, skip) require.ErrorContains(t, err, "cannot convert gas price") }) diff --git a/zetaclient/chains/evm/signer/signer.go b/zetaclient/chains/evm/signer/signer.go index db4a15c856..7235aa4456 100644 --- a/zetaclient/chains/evm/signer/signer.go +++ b/zetaclient/chains/evm/signer/signer.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ethereum/go-ethereum/accounts/abi" ethcommon "github.com/ethereum/go-ethereum/common" @@ -31,7 +32,7 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/chains/evm/observer" "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" "github.com/zeta-chain/zetacore/zetaclient/compliance" - clientcontext "github.com/zeta-chain/zetacore/zetaclient/context" + zctx "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/zetaclient/metrics" "github.com/zeta-chain/zetacore/zetaclient/outboundprocessor" "github.com/zeta-chain/zetacore/zetaclient/testutils/mocks" @@ -81,8 +82,8 @@ type Signer struct { // NewSigner creates a new EVM signer func NewSigner( + ctx context.Context, chain chains.Chain, - appContext *clientcontext.AppContext, tss interfaces.TSSSigner, ts *metrics.TelemetryServer, logger base.Logger, @@ -93,22 +94,23 @@ func NewSigner( erc20CustodyAddress ethcommon.Address, ) (*Signer, error) { // create base signer - baseSigner := base.NewSigner(chain, appContext, tss, ts, logger) + baseSigner := base.NewSigner(chain, tss, ts, logger) // create EVM client - client, ethSigner, err := getEVMRPC(endpoint) + client, ethSigner, err := getEVMRPC(ctx, endpoint) if err != nil { - return nil, err + return nil, errors.Wrap(err, "unable to create EVM client") } // prepare ABIs connectorABI, err := abi.JSON(strings.NewReader(zetaConnectorABI)) if err != nil { - return nil, err + return nil, errors.Wrap(err, "unable to build ZetaConnector ABI") } + custodyABI, err := abi.JSON(strings.NewReader(erc20CustodyABI)) if err != nil { - return nil, err + return nil, errors.Wrap(err, "unable to build ERC20Custody ABI") } return &Signer{ @@ -154,6 +156,7 @@ func (signer *Signer) GetERC20CustodyAddress() ethcommon.Address { // Sign given data, and metadata (gas, nonce, etc) // returns a signed transaction, sig bytes, hash bytes, and error func (signer *Signer) Sign( + ctx context.Context, data []byte, to ethcommon.Address, amount *big.Int, @@ -162,14 +165,14 @@ func (signer *Signer) Sign( nonce uint64, height uint64, ) (*ethtypes.Transaction, []byte, []byte, error) { - log.Debug().Msgf("Sign: TSS signer: %s", signer.TSS().Pubkey()) + log.Debug().Str("tss.pub_key", signer.TSS().EVMAddress().String()).Msg("Sign: TSS signer") // TODO: use EIP-1559 transaction type // https://github.com/zeta-chain/node/issues/1952 tx := ethtypes.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data) hashBytes := signer.ethSigner.Hash(tx).Bytes() - sig, err := signer.TSS().Sign(hashBytes, height, nonce, signer.Chain().ChainId, "") + sig, err := signer.TSS().Sign(ctx, hashBytes, height, nonce, signer.Chain().ChainId, "") if err != nil { return nil, nil, nil, err } @@ -208,7 +211,7 @@ func (signer *Signer) Broadcast(tx *ethtypes.Transaction) error { // bytes32 internalSendHash // // ) external virtual {} -func (signer *Signer) SignOutbound(txData *OutboundData) (*ethtypes.Transaction, error) { +func (signer *Signer) SignOutbound(ctx context.Context, txData *OutboundData) (*ethtypes.Transaction, error) { var data []byte var err error @@ -223,7 +226,9 @@ func (signer *Signer) SignOutbound(txData *OutboundData) (*ethtypes.Transaction, return nil, fmt.Errorf("onReceive pack error: %w", err) } - tx, _, _, err := signer.Sign(data, + tx, _, _, err := signer.Sign( + ctx, + data, signer.zetaConnectorAddress, zeroValue, txData.gasLimit, @@ -247,7 +252,7 @@ func (signer *Signer) SignOutbound(txData *OutboundData) (*ethtypes.Transaction, // bytes calldata message, // bytes32 internalSendHash // ) external override whenNotPaused onlyTssAddress -func (signer *Signer) SignRevertTx(txData *OutboundData) (*ethtypes.Transaction, error) { +func (signer *Signer) SignRevertTx(ctx context.Context, txData *OutboundData) (*ethtypes.Transaction, error) { var data []byte var err error @@ -263,7 +268,9 @@ func (signer *Signer) SignRevertTx(txData *OutboundData) (*ethtypes.Transaction, return nil, fmt.Errorf("onRevert pack error: %w", err) } - tx, _, _, err := signer.Sign(data, + tx, _, _, err := signer.Sign( + ctx, + data, signer.zetaConnectorAddress, zeroValue, txData.gasLimit, @@ -278,8 +285,9 @@ func (signer *Signer) SignRevertTx(txData *OutboundData) (*ethtypes.Transaction, } // SignCancelTx signs a transaction from TSS address to itself with a zero amount in order to increment the nonce -func (signer *Signer) SignCancelTx(txData *OutboundData) (*ethtypes.Transaction, error) { +func (signer *Signer) SignCancelTx(ctx context.Context, txData *OutboundData) (*ethtypes.Transaction, error) { tx, _, _, err := signer.Sign( + ctx, nil, signer.TSS().EVMAddress(), zeroValue, // zero out the amount to cancel the tx @@ -296,8 +304,9 @@ func (signer *Signer) SignCancelTx(txData *OutboundData) (*ethtypes.Transaction, } // SignWithdrawTx signs a withdrawal transaction sent from the TSS address to the destination -func (signer *Signer) SignWithdrawTx(txData *OutboundData) (*ethtypes.Transaction, error) { +func (signer *Signer) SignWithdrawTx(ctx context.Context, txData *OutboundData) (*ethtypes.Transaction, error) { tx, _, _, err := signer.Sign( + ctx, nil, txData.to, txData.amount, @@ -317,12 +326,17 @@ func (signer *Signer) SignWithdrawTx(txData *OutboundData) (*ethtypes.Transactio // // cmd_whitelist_erc20 // cmd_migrate_tss_funds -func (signer *Signer) SignCommandTx(txData *OutboundData, cmd string, params string) (*ethtypes.Transaction, error) { +func (signer *Signer) SignCommandTx( + ctx context.Context, + txData *OutboundData, + cmd string, + params string, +) (*ethtypes.Transaction, error) { switch cmd { case constant.CmdWhitelistERC20: - return signer.SignWhitelistERC20Cmd(txData, params) + return signer.SignWhitelistERC20Cmd(ctx, txData, params) case constant.CmdMigrateTssFunds: - return signer.SignMigrateTssFundsCmd(txData) + return signer.SignMigrateTssFundsCmd(ctx, txData) } return nil, fmt.Errorf("SignCommandTx: unknown command %s", cmd) } @@ -332,6 +346,7 @@ func (signer *Signer) SignCommandTx(txData *OutboundData, cmd string, params str // It will then broadcast the signed transaction to the outbound chain. // TODO(revamp): simplify function func (signer *Signer) TryProcessOutbound( + ctx context.Context, cctx *types.CrossChainTx, outboundProc *outboundprocessor.Processor, outboundID string, @@ -339,6 +354,12 @@ func (signer *Signer) TryProcessOutbound( zetacoreClient interfaces.ZetacoreClient, height uint64, ) { + app, err := zctx.FromContext(ctx) + if err != nil { + signer.Logger().Std.Error().Err(err).Msg("error getting app context") + return + } + logger := signer.Logger().Std.With(). Str("outboundID", outboundID). Str("SendHash", cctx.Index). @@ -363,7 +384,7 @@ func (signer *Signer) TryProcessOutbound( } // Setup Transaction input - txData, skipTx, err := NewOutboundData(signer.AppContext(), cctx, evmObserver, signer.client, logger, height) + txData, skipTx, err := NewOutboundData(ctx, cctx, evmObserver, signer.client, logger, height) if err != nil { logger.Err(err).Msg("error setting up transaction input fields") return @@ -372,17 +393,14 @@ func (signer *Signer) TryProcessOutbound( return } - toChain, found := chains.GetChainFromChainID( - txData.toChainID.Int64(), - signer.AppContext().GetAdditionalChains(), - ) + toChain, found := chains.GetChainFromChainID(txData.toChainID.Int64(), app.GetAdditionalChains()) if !found { logger.Warn().Msgf("unknown chain: %d", txData.toChainID.Int64()) return } // Get cross-chain flags - crossChainflags := signer.AppContext().GetCrossChainFlags() + crossChainflags := app.GetCrossChainFlags() // https://github.com/zeta-chain/node/issues/2050 var tx *ethtypes.Transaction // compliance check goes first @@ -398,7 +416,7 @@ func (signer *Signer) TryProcessOutbound( cctx.GetCurrentOutboundParam().CoinType.String(), ) - tx, err = signer.SignCancelTx(txData) // cancel the tx + tx, err = signer.SignCancelTx(ctx, txData) // cancel the tx if err != nil { logger.Warn().Err(err).Msg(ErrorMsg(cctx)) return @@ -420,7 +438,7 @@ func (signer *Signer) TryProcessOutbound( // params field is used to pass input parameters for command requests, currently it is used to pass the ERC20 // contract address when a whitelist command is requested params := msg[1] - tx, err = signer.SignCommandTx(txData, cmd, params) + tx, err = signer.SignCommandTx(ctx, txData, cmd, params) if err != nil { logger.Warn().Err(err).Msg(ErrorMsg(cctx)) return @@ -435,7 +453,7 @@ func (signer *Signer) TryProcessOutbound( cctx.GetCurrentOutboundParam().TssNonce, txData.gasPrice, ) - tx, err = signer.SignWithdrawTx(txData) + tx, err = signer.SignWithdrawTx(ctx, txData) case coin.CoinType_ERC20: logger.Info().Msgf( "SignERC20WithdrawTx: %d => %s, nonce %d, gasPrice %d", @@ -444,7 +462,7 @@ func (signer *Signer) TryProcessOutbound( cctx.GetCurrentOutboundParam().TssNonce, txData.gasPrice, ) - tx, err = signer.SignERC20WithdrawTx(txData) + tx, err = signer.SignERC20WithdrawTx(ctx, txData) case coin.CoinType_Zeta: logger.Info().Msgf( "SignOutbound: %d => %s, nonce %d, gasPrice %d", @@ -453,7 +471,7 @@ func (signer *Signer) TryProcessOutbound( cctx.GetCurrentOutboundParam().TssNonce, txData.gasPrice, ) - tx, err = signer.SignOutbound(txData) + tx, err = signer.SignOutbound(ctx, txData) } if err != nil { logger.Warn().Err(err).Msg(ErrorMsg(cctx)) @@ -470,7 +488,7 @@ func (signer *Signer) TryProcessOutbound( ) txData.srcChainID = big.NewInt(cctx.OutboundParams[0].ReceiverChainId) txData.toChainID = big.NewInt(cctx.GetCurrentOutboundParam().ReceiverChainId) - tx, err = signer.SignRevertTx(txData) + tx, err = signer.SignRevertTx(ctx, txData) case coin.CoinType_Gas: logger.Info().Msgf( "SignWithdrawTx: %d => %s, nonce %d, gasPrice %d", @@ -479,7 +497,7 @@ func (signer *Signer) TryProcessOutbound( cctx.GetCurrentOutboundParam().TssNonce, txData.gasPrice, ) - tx, err = signer.SignWithdrawTx(txData) + tx, err = signer.SignWithdrawTx(ctx, txData) case coin.CoinType_ERC20: logger.Info().Msgf("SignERC20WithdrawTx: %d => %s, nonce %d, gasPrice %d", cctx.InboundParams.SenderChainId, @@ -487,7 +505,7 @@ func (signer *Signer) TryProcessOutbound( cctx.GetCurrentOutboundParam().TssNonce, txData.gasPrice, ) - tx, err = signer.SignERC20WithdrawTx(txData) + tx, err = signer.SignERC20WithdrawTx(ctx, txData) } if err != nil { logger.Warn().Err(err).Msg(ErrorMsg(cctx)) @@ -504,7 +522,7 @@ func (signer *Signer) TryProcessOutbound( txData.srcChainID = big.NewInt(cctx.OutboundParams[0].ReceiverChainId) txData.toChainID = big.NewInt(cctx.GetCurrentOutboundParam().ReceiverChainId) - tx, err = signer.SignRevertTx(txData) + tx, err = signer.SignRevertTx(ctx, txData) if err != nil { logger.Warn().Err(err).Msg(ErrorMsg(cctx)) return @@ -517,7 +535,7 @@ func (signer *Signer) TryProcessOutbound( cctx.GetCurrentOutboundParam().TssNonce, txData.gasPrice, ) - tx, err = signer.SignOutbound(txData) + tx, err = signer.SignOutbound(ctx, txData) if err != nil { logger.Warn().Err(err).Msg(ErrorMsg(cctx)) return @@ -532,11 +550,12 @@ func (signer *Signer) TryProcessOutbound( ) // Broadcast Signed Tx - signer.BroadcastOutbound(tx, cctx, logger, myID, zetacoreClient, txData) + signer.BroadcastOutbound(ctx, tx, cctx, logger, myID, zetacoreClient, txData) } // BroadcastOutbound signed transaction through evm rpc client func (signer *Signer) BroadcastOutbound( + ctx context.Context, tx *ethtypes.Transaction, cctx *types.CrossChainTx, logger zerolog.Logger, @@ -544,11 +563,14 @@ func (signer *Signer) BroadcastOutbound( zetacoreClient interfaces.ZetacoreClient, txData *OutboundData, ) { + app, err := zctx.FromContext(ctx) + if err != nil { + logger.Err(err).Msg("error getting app context") + return + } + // Get destination chain for logging - toChain, found := chains.GetChainFromChainID( - txData.toChainID.Int64(), - signer.AppContext().GetAdditionalChains(), - ) + toChain, found := chains.GetChainFromChainID(txData.toChainID.Int64(), app.GetAdditionalChains()) if !found { logger.Warn().Msgf("BroadcastOutbound: unknown chain %d", txData.toChainID.Int64()) return @@ -579,7 +601,7 @@ func (signer *Signer) BroadcastOutbound( outboundHash, ) if report { - signer.reportToOutboundTracker(zetacoreClient, toChain.ChainId, tx.Nonce(), outboundHash, logger) + signer.reportToOutboundTracker(ctx, zetacoreClient, toChain.ChainId, tx.Nonce(), outboundHash, logger) } if !retry { break @@ -589,7 +611,7 @@ func (signer *Signer) BroadcastOutbound( } logger.Info().Msgf("BroadcastOutbound: broadcasted tx %s on chain %d nonce %d signer %s", outboundHash, toChain.ChainId, cctx.GetCurrentOutboundParam().TssNonce, myID) - signer.reportToOutboundTracker(zetacoreClient, toChain.ChainId, tx.Nonce(), outboundHash, logger) + signer.reportToOutboundTracker(ctx, zetacoreClient, toChain.ChainId, tx.Nonce(), outboundHash, logger) break // successful broadcast; no need to retry } } @@ -600,7 +622,7 @@ func (signer *Signer) BroadcastOutbound( // address asset, // uint256 amount, // ) external onlyTssAddress -func (signer *Signer) SignERC20WithdrawTx(txData *OutboundData) (*ethtypes.Transaction, error) { +func (signer *Signer) SignERC20WithdrawTx(ctx context.Context, txData *OutboundData) (*ethtypes.Transaction, error) { var data []byte var err error data, err = signer.erc20CustodyABI.Pack("withdraw", txData.to, txData.asset, txData.amount) @@ -609,6 +631,7 @@ func (signer *Signer) SignERC20WithdrawTx(txData *OutboundData) (*ethtypes.Trans } tx, _, _, err := signer.Sign( + ctx, data, signer.er20CustodyAddress, zeroValue, @@ -666,7 +689,11 @@ func ErrorMsg(cctx *types.CrossChainTx) string { // SignWhitelistERC20Cmd signs a whitelist command for ERC20 token // TODO(revamp): move the cmd in a specific file -func (signer *Signer) SignWhitelistERC20Cmd(txData *OutboundData, params string) (*ethtypes.Transaction, error) { +func (signer *Signer) SignWhitelistERC20Cmd( + ctx context.Context, + txData *OutboundData, + params string, +) (*ethtypes.Transaction, error) { outboundParams := txData.outboundParams erc20 := ethcommon.HexToAddress(params) if erc20 == (ethcommon.Address{}) { @@ -681,6 +708,7 @@ func (signer *Signer) SignWhitelistERC20Cmd(txData *OutboundData, params string) return nil, fmt.Errorf("whitelist pack error: %w", err) } tx, _, _, err := signer.Sign( + ctx, data, txData.to, zeroValue, @@ -697,8 +725,9 @@ func (signer *Signer) SignWhitelistERC20Cmd(txData *OutboundData, params string) // SignMigrateTssFundsCmd signs a migrate TSS funds command // TODO(revamp): move the cmd in a specific file -func (signer *Signer) SignMigrateTssFundsCmd(txData *OutboundData) (*ethtypes.Transaction, error) { +func (signer *Signer) SignMigrateTssFundsCmd(ctx context.Context, txData *OutboundData) (*ethtypes.Transaction, error) { tx, _, _, err := signer.Sign( + ctx, nil, txData.to, txData.amount, @@ -716,6 +745,7 @@ func (signer *Signer) SignMigrateTssFundsCmd(txData *OutboundData) (*ethtypes.Tr // reportToOutboundTracker reports outboundHash to tracker only when tx receipt is available // TODO(revamp): move outbound tracker function to a outbound tracker file func (signer *Signer) reportToOutboundTracker( + ctx context.Context, zetacoreClient interfaces.ZetacoreClient, chainID int64, nonce uint64, @@ -760,7 +790,7 @@ func (signer *Signer) reportToOutboundTracker( break } // try getting the tx - _, isPending, err = signer.client.TransactionByHash(context.TODO(), ethcommon.HexToHash(outboundHash)) + _, isPending, err = signer.client.TransactionByHash(ctx, ethcommon.HexToHash(outboundHash)) if err != nil { logger.Info(). Err(err). @@ -770,7 +800,7 @@ func (signer *Signer) reportToOutboundTracker( // if tx is include in a block, try getting receipt if !isPending { report = true // included - receipt, err := signer.client.TransactionReceipt(context.TODO(), ethcommon.HexToHash(outboundHash)) + receipt, err := signer.client.TransactionReceipt(ctx, ethcommon.HexToHash(outboundHash)) if err != nil { logger.Info(). Err(err). @@ -797,7 +827,7 @@ func (signer *Signer) reportToOutboundTracker( break } // stop if the cctx is already finalized - cctx, err := zetacoreClient.GetCctxByNonce(chainID, nonce) + cctx, err := zetacoreClient.GetCctxByNonce(ctx, chainID, nonce) if err != nil { logger.Err(err). Msgf("reportToOutboundTracker: error getting cctx for chain %d nonce %d outboundHash %s", chainID, nonce, outboundHash) @@ -806,7 +836,7 @@ func (signer *Signer) reportToOutboundTracker( break } // report to outbound tracker - zetaHash, err := zetacoreClient.AddOutboundTracker(chainID, nonce, outboundHash, nil, "", -1) + zetaHash, err := zetacoreClient.AddOutboundTracker(ctx, chainID, nonce, outboundHash, nil, "", -1) if err != nil { logger.Err(err). Msgf("reportToOutboundTracker: error adding to outbound tracker for chain %d nonce %d outboundHash %s", chainID, nonce, outboundHash) @@ -826,7 +856,7 @@ func (signer *Signer) reportToOutboundTracker( } // getEVMRPC is a helper function to set up the client and signer, also initializes a mock client for unit tests -func getEVMRPC(endpoint string) (interfaces.EVMRPCClient, ethtypes.Signer, error) { +func getEVMRPC(ctx context.Context, endpoint string) (interfaces.EVMRPCClient, ethtypes.Signer, error) { if endpoint == mocks.EVMRPCEnabled { chainID := big.NewInt(chains.BscMainnet.ChainId) ethSigner := ethtypes.NewLondonSigner(chainID) @@ -836,14 +866,16 @@ func getEVMRPC(endpoint string) (interfaces.EVMRPCClient, ethtypes.Signer, error client, err := ethclient.Dial(endpoint) if err != nil { - return nil, nil, err + return nil, nil, errors.Wrapf(err, "unable to dial EVM client (endpoint %q)", endpoint) } - chainID, err := client.ChainID(context.TODO()) + chainID, err := client.ChainID(ctx) if err != nil { - return nil, nil, err + return nil, nil, errors.Wrap(err, "unable to get chain ID") } + ethSigner := ethtypes.LatestSignerForChainID(chainID) + return client, ethSigner, nil } diff --git a/zetaclient/chains/evm/signer/signer_test.go b/zetaclient/chains/evm/signer/signer_test.go index fd412b4bfd..9880bb233e 100644 --- a/zetaclient/chains/evm/signer/signer_test.go +++ b/zetaclient/chains/evm/signer/signer_test.go @@ -1,6 +1,7 @@ package signer import ( + "context" "math/big" "testing" @@ -10,6 +11,8 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/rs/zerolog" "github.com/stretchr/testify/require" + zctx "github.com/zeta-chain/zetacore/zetaclient/context" + "github.com/zeta-chain/zetacore/zetaclient/keys" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/constant" @@ -19,8 +22,6 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/chains/evm/observer" "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" "github.com/zeta-chain/zetacore/zetaclient/config" - "github.com/zeta-chain/zetacore/zetaclient/context" - "github.com/zeta-chain/zetacore/zetaclient/keys" "github.com/zeta-chain/zetacore/zetaclient/metrics" "github.com/zeta-chain/zetacore/zetaclient/outboundprocessor" "github.com/zeta-chain/zetacore/zetaclient/testutils" @@ -35,6 +36,8 @@ var ( // getNewEvmSigner creates a new EVM chain signer for testing func getNewEvmSigner(tss interfaces.TSSSigner) (*Signer, error) { + ctx := context.Background() + // use default mock TSS if not provided if tss == nil { tss = mocks.NewTSSMainnet() @@ -43,11 +46,10 @@ func getNewEvmSigner(tss interfaces.TSSSigner) (*Signer, error) { mpiAddress := ConnectorAddress erc20CustodyAddress := ERC20CustodyAddress logger := base.Logger{} - cfg := config.NewConfig() return NewSigner( + ctx, chains.BscMainnet, - context.New(cfg, zerolog.Nop()), tss, nil, logger, @@ -55,33 +57,36 @@ func getNewEvmSigner(tss interfaces.TSSSigner) (*Signer, error) { config.GetConnectorABI(), config.GetERC20CustodyABI(), mpiAddress, - erc20CustodyAddress) + erc20CustodyAddress, + ) } // getNewEvmChainObserver creates a new EVM chain observer for testing func getNewEvmChainObserver(t *testing.T, tss interfaces.TSSSigner) (*observer.Observer, error) { + ctx := context.Background() + // use default mock TSS if not provided if tss == nil { tss = mocks.NewTSSMainnet() } - cfg := config.NewConfig() + cfg := config.New(false) // prepare mock arguments to create observer evmcfg := config.EVMConfig{Chain: chains.BscMainnet, Endpoint: "http://localhost:8545"} evmClient := mocks.NewMockEvmClient().WithBlockNumber(1000) params := mocks.MockChainParams(evmcfg.Chain.ChainId, 10) cfg.EVMChainConfigs[chains.BscMainnet.ChainId] = evmcfg - appContext := context.New(cfg, zerolog.Nop()) + //appContext := context.New(cfg, zerolog.Nop()) dbpath := sample.CreateTempDir(t) logger := base.Logger{} ts := &metrics.TelemetryServer{} return observer.NewObserver( + ctx, evmcfg, evmClient, params, - appContext, - mocks.NewMockZetacoreClient(), + mocks.NewZetacoreClient(t), tss, dbpath, logger, @@ -153,6 +158,8 @@ func TestSigner_SetGetERC20CustodyAddress(t *testing.T) { } func TestSigner_TryProcessOutbound(t *testing.T) { + ctx := makeCtx() + evmSigner, err := getNewEvmSigner(nil) require.NoError(t, err) cctx := getCCTX(t) @@ -161,8 +168,12 @@ func TestSigner_TryProcessOutbound(t *testing.T) { require.NoError(t, err) // Test with mock client that has keys - client := mocks.NewMockZetacoreClient().WithKeys(&keys.Keys{}) - evmSigner.TryProcessOutbound(cctx, processor, "123", mockObserver, client, 123) + client := mocks.NewZetacoreClient(t). + WithKeys(&keys.Keys{}). + WithZetaChain(). + WithPostVoteOutbound("", "") + + evmSigner.TryProcessOutbound(ctx, cctx, processor, "123", mockObserver, client, 123) // Check if cctx was signed and broadcasted list := evmSigner.GetReportedTxList() @@ -170,6 +181,8 @@ func TestSigner_TryProcessOutbound(t *testing.T) { } func TestSigner_SignOutbound(t *testing.T) { + ctx := makeCtx() + // Setup evm signer tss := mocks.NewTSSMainnet() evmSigner, err := getNewEvmSigner(tss) @@ -180,20 +193,13 @@ func TestSigner_SignOutbound(t *testing.T) { cctx := getCCTX(t) mockObserver, err := getNewEvmChainObserver(t, tss) require.NoError(t, err) - txData, skip, err := NewOutboundData( - evmSigner.AppContext(), - cctx, - mockObserver, - evmSigner.EvmClient(), - zerolog.Logger{}, - 123, - ) + txData, skip, err := NewOutboundData(ctx, cctx, mockObserver, evmSigner.EvmClient(), zerolog.Logger{}, 123) require.False(t, skip) require.NoError(t, err) t.Run("SignOutbound - should successfully sign", func(t *testing.T) { // Call SignOutbound - tx, err := evmSigner.SignOutbound(txData) + tx, err := evmSigner.SignOutbound(ctx, txData) require.NoError(t, err) // Verify Signature @@ -205,13 +211,15 @@ func TestSigner_SignOutbound(t *testing.T) { tss.Pause() // Call SignOutbound - tx, err := evmSigner.SignOutbound(txData) + tx, err := evmSigner.SignOutbound(ctx, txData) require.ErrorContains(t, err, "sign onReceive error") require.Nil(t, tx) }) } func TestSigner_SignRevertTx(t *testing.T) { + ctx := makeCtx() + // Setup evm signer tss := mocks.NewTSSMainnet() evmSigner, err := getNewEvmSigner(tss) @@ -221,20 +229,13 @@ func TestSigner_SignRevertTx(t *testing.T) { cctx := getCCTX(t) mockObserver, err := getNewEvmChainObserver(t, tss) require.NoError(t, err) - txData, skip, err := NewOutboundData( - evmSigner.AppContext(), - cctx, - mockObserver, - evmSigner.EvmClient(), - zerolog.Logger{}, - 123, - ) + txData, skip, err := NewOutboundData(ctx, cctx, mockObserver, evmSigner.EvmClient(), zerolog.Logger{}, 123) require.False(t, skip) require.NoError(t, err) t.Run("SignRevertTx - should successfully sign", func(t *testing.T) { // Call SignRevertTx - tx, err := evmSigner.SignRevertTx(txData) + tx, err := evmSigner.SignRevertTx(ctx, txData) require.NoError(t, err) // Verify tx signature @@ -250,13 +251,15 @@ func TestSigner_SignRevertTx(t *testing.T) { tss.Pause() // Call SignRevertTx - tx, err := evmSigner.SignRevertTx(txData) + tx, err := evmSigner.SignRevertTx(ctx, txData) require.ErrorContains(t, err, "sign onRevert error") require.Nil(t, tx) }) } func TestSigner_SignCancelTx(t *testing.T) { + ctx := makeCtx() + // Setup evm signer tss := mocks.NewTSSMainnet() evmSigner, err := getNewEvmSigner(tss) @@ -266,20 +269,13 @@ func TestSigner_SignCancelTx(t *testing.T) { cctx := getCCTX(t) mockObserver, err := getNewEvmChainObserver(t, tss) require.NoError(t, err) - txData, skip, err := NewOutboundData( - evmSigner.AppContext(), - cctx, - mockObserver, - evmSigner.EvmClient(), - zerolog.Logger{}, - 123, - ) + txData, skip, err := NewOutboundData(ctx, cctx, mockObserver, evmSigner.EvmClient(), zerolog.Logger{}, 123) require.False(t, skip) require.NoError(t, err) t.Run("SignCancelTx - should successfully sign", func(t *testing.T) { // Call SignRevertTx - tx, err := evmSigner.SignCancelTx(txData) + tx, err := evmSigner.SignCancelTx(ctx, txData) require.NoError(t, err) // Verify tx signature @@ -295,13 +291,15 @@ func TestSigner_SignCancelTx(t *testing.T) { tss.Pause() // Call SignCancelTx - tx, err := evmSigner.SignCancelTx(txData) + tx, err := evmSigner.SignCancelTx(ctx, txData) require.ErrorContains(t, err, "SignCancelTx error") require.Nil(t, tx) }) } func TestSigner_SignWithdrawTx(t *testing.T) { + ctx := makeCtx() + // Setup evm signer tss := mocks.NewTSSMainnet() evmSigner, err := getNewEvmSigner(tss) @@ -311,20 +309,13 @@ func TestSigner_SignWithdrawTx(t *testing.T) { cctx := getCCTX(t) mockObserver, err := getNewEvmChainObserver(t, tss) require.NoError(t, err) - txData, skip, err := NewOutboundData( - evmSigner.AppContext(), - cctx, - mockObserver, - evmSigner.EvmClient(), - zerolog.Logger{}, - 123, - ) + txData, skip, err := NewOutboundData(ctx, cctx, mockObserver, evmSigner.EvmClient(), zerolog.Logger{}, 123) require.False(t, skip) require.NoError(t, err) t.Run("SignWithdrawTx - should successfully sign", func(t *testing.T) { // Call SignWithdrawTx - tx, err := evmSigner.SignWithdrawTx(txData) + tx, err := evmSigner.SignWithdrawTx(ctx, txData) require.NoError(t, err) // Verify tx signature @@ -339,13 +330,15 @@ func TestSigner_SignWithdrawTx(t *testing.T) { tss.Pause() // Call SignWithdrawTx - tx, err := evmSigner.SignWithdrawTx(txData) + tx, err := evmSigner.SignWithdrawTx(ctx, txData) require.ErrorContains(t, err, "SignWithdrawTx error") require.Nil(t, tx) }) } func TestSigner_SignCommandTx(t *testing.T) { + ctx := makeCtx() + // Setup evm signer evmSigner, err := getNewEvmSigner(nil) require.NoError(t, err) @@ -354,14 +347,7 @@ func TestSigner_SignCommandTx(t *testing.T) { cctx := getCCTX(t) mockObserver, err := getNewEvmChainObserver(t, nil) require.NoError(t, err) - txData, skip, err := NewOutboundData( - evmSigner.AppContext(), - cctx, - mockObserver, - evmSigner.EvmClient(), - zerolog.Logger{}, - 123, - ) + txData, skip, err := NewOutboundData(ctx, cctx, mockObserver, evmSigner.EvmClient(), zerolog.Logger{}, 123) require.False(t, skip) require.NoError(t, err) @@ -369,7 +355,7 @@ func TestSigner_SignCommandTx(t *testing.T) { cmd := constant.CmdWhitelistERC20 params := ConnectorAddress.Hex() // Call SignCommandTx - tx, err := evmSigner.SignCommandTx(txData, cmd, params) + tx, err := evmSigner.SignCommandTx(ctx, txData, cmd, params) require.NoError(t, err) // Verify tx signature @@ -384,7 +370,7 @@ func TestSigner_SignCommandTx(t *testing.T) { t.Run("SignCommandTx CmdMigrateTssFunds", func(t *testing.T) { cmd := constant.CmdMigrateTssFunds // Call SignCommandTx - tx, err := evmSigner.SignCommandTx(txData, cmd, "") + tx, err := evmSigner.SignCommandTx(ctx, txData, cmd, "") require.NoError(t, err) // Verify tx signature @@ -397,6 +383,8 @@ func TestSigner_SignCommandTx(t *testing.T) { } func TestSigner_SignERC20WithdrawTx(t *testing.T) { + ctx := makeCtx() + // Setup evm signer tss := mocks.NewTSSMainnet() evmSigner, err := getNewEvmSigner(tss) @@ -406,20 +394,13 @@ func TestSigner_SignERC20WithdrawTx(t *testing.T) { cctx := getCCTX(t) mockObserver, err := getNewEvmChainObserver(t, tss) require.NoError(t, err) - txData, skip, err := NewOutboundData( - evmSigner.AppContext(), - cctx, - mockObserver, - evmSigner.EvmClient(), - zerolog.Logger{}, - 123, - ) + txData, skip, err := NewOutboundData(ctx, cctx, mockObserver, evmSigner.EvmClient(), zerolog.Logger{}, 123) require.False(t, skip) require.NoError(t, err) t.Run("SignERC20WithdrawTx - should successfully sign", func(t *testing.T) { // Call SignERC20WithdrawTx - tx, err := evmSigner.SignERC20WithdrawTx(txData) + tx, err := evmSigner.SignERC20WithdrawTx(ctx, txData) require.NoError(t, err) // Verify tx signature @@ -436,13 +417,15 @@ func TestSigner_SignERC20WithdrawTx(t *testing.T) { tss.Pause() // Call SignERC20WithdrawTx - tx, err := evmSigner.SignERC20WithdrawTx(txData) + tx, err := evmSigner.SignERC20WithdrawTx(ctx, txData) require.ErrorContains(t, err, "sign withdraw error") require.Nil(t, tx) }) } func TestSigner_BroadcastOutbound(t *testing.T) { + ctx := makeCtx() + // Setup evm signer evmSigner, err := getNewEvmSigner(nil) require.NoError(t, err) @@ -451,28 +434,22 @@ func TestSigner_BroadcastOutbound(t *testing.T) { cctx := getCCTX(t) mockObserver, err := getNewEvmChainObserver(t, nil) require.NoError(t, err) - txData, skip, err := NewOutboundData( - evmSigner.AppContext(), - cctx, - mockObserver, - evmSigner.EvmClient(), - zerolog.Logger{}, - 123, - ) + txData, skip, err := NewOutboundData(ctx, cctx, mockObserver, evmSigner.EvmClient(), zerolog.Logger{}, 123) require.False(t, skip) require.NoError(t, err) t.Run("BroadcastOutbound - should successfully broadcast", func(t *testing.T) { // Call SignERC20WithdrawTx - tx, err := evmSigner.SignERC20WithdrawTx(txData) + tx, err := evmSigner.SignERC20WithdrawTx(ctx, txData) require.NoError(t, err) evmSigner.BroadcastOutbound( + ctx, tx, cctx, zerolog.Logger{}, sdktypes.AccAddress{}, - mocks.NewMockZetacoreClient(), + mocks.NewZetacoreClient(t), txData, ) @@ -483,8 +460,10 @@ func TestSigner_BroadcastOutbound(t *testing.T) { } func TestSigner_getEVMRPC(t *testing.T) { + ctx := context.Background() + t.Run("getEVMRPC error dialing", func(t *testing.T) { - client, signer, err := getEVMRPC("invalidEndpoint") + client, signer, err := getEVMRPC(ctx, "invalidEndpoint") require.Nil(t, client) require.Nil(t, signer) require.Error(t, err) @@ -499,6 +478,8 @@ func TestSigner_SignerErrorMsg(t *testing.T) { } func TestSigner_SignWhitelistERC20Cmd(t *testing.T) { + ctx := makeCtx() + // Setup evm signer tss := mocks.NewTSSMainnet() evmSigner, err := getNewEvmSigner(tss) @@ -508,20 +489,13 @@ func TestSigner_SignWhitelistERC20Cmd(t *testing.T) { cctx := getCCTX(t) mockObserver, err := getNewEvmChainObserver(t, tss) require.NoError(t, err) - txData, skip, err := NewOutboundData( - evmSigner.AppContext(), - cctx, - mockObserver, - evmSigner.EvmClient(), - zerolog.Logger{}, - 123, - ) + txData, skip, err := NewOutboundData(ctx, cctx, mockObserver, evmSigner.EvmClient(), zerolog.Logger{}, 123) require.False(t, skip) require.NoError(t, err) t.Run("SignWhitelistERC20Cmd - should successfully sign", func(t *testing.T) { // Call SignWhitelistERC20Cmd - tx, err := evmSigner.SignWhitelistERC20Cmd(txData, sample.EthAddress().Hex()) + tx, err := evmSigner.SignWhitelistERC20Cmd(ctx, txData, sample.EthAddress().Hex()) require.NoError(t, err) require.NotNil(t, tx) @@ -533,7 +507,7 @@ func TestSigner_SignWhitelistERC20Cmd(t *testing.T) { verifyTxBodyBasics(t, tx, txData.to, txData.nonce, zeroValue) }) t.Run("SignWhitelistERC20Cmd - should fail on invalid erc20 address", func(t *testing.T) { - tx, err := evmSigner.SignWhitelistERC20Cmd(txData, "") + tx, err := evmSigner.SignWhitelistERC20Cmd(ctx, txData, "") require.Nil(t, tx) require.ErrorContains(t, err, "invalid erc20 address") }) @@ -542,13 +516,15 @@ func TestSigner_SignWhitelistERC20Cmd(t *testing.T) { tss.Pause() // Call SignWhitelistERC20Cmd - tx, err := evmSigner.SignWhitelistERC20Cmd(txData, sample.EthAddress().Hex()) + tx, err := evmSigner.SignWhitelistERC20Cmd(ctx, txData, sample.EthAddress().Hex()) require.ErrorContains(t, err, "sign whitelist error") require.Nil(t, tx) }) } func TestSigner_SignMigrateTssFundsCmd(t *testing.T) { + ctx := makeCtx() + // Setup evm signer tss := mocks.NewTSSMainnet() evmSigner, err := getNewEvmSigner(tss) @@ -558,20 +534,13 @@ func TestSigner_SignMigrateTssFundsCmd(t *testing.T) { cctx := getCCTX(t) mockObserver, err := getNewEvmChainObserver(t, tss) require.NoError(t, err) - txData, skip, err := NewOutboundData( - evmSigner.AppContext(), - cctx, - mockObserver, - evmSigner.EvmClient(), - zerolog.Logger{}, - 123, - ) + txData, skip, err := NewOutboundData(ctx, cctx, mockObserver, evmSigner.EvmClient(), zerolog.Logger{}, 123) require.False(t, skip) require.NoError(t, err) t.Run("SignMigrateTssFundsCmd - should successfully sign", func(t *testing.T) { // Call SignMigrateTssFundsCmd - tx, err := evmSigner.SignMigrateTssFundsCmd(txData) + tx, err := evmSigner.SignMigrateTssFundsCmd(ctx, txData) require.NoError(t, err) require.NotNil(t, tx) @@ -588,8 +557,13 @@ func TestSigner_SignMigrateTssFundsCmd(t *testing.T) { tss.Pause() // Call SignMigrateTssFundsCmd - tx, err := evmSigner.SignMigrateTssFundsCmd(txData) + tx, err := evmSigner.SignMigrateTssFundsCmd(ctx, txData) require.ErrorContains(t, err, "SignMigrateTssFundsCmd error") require.Nil(t, tx) }) } +func makeCtx() context.Context { + app := zctx.New(config.New(false), zerolog.Nop()) + + return zctx.WithAppContext(context.Background(), app) +} diff --git a/zetaclient/chains/interfaces/interfaces.go b/zetaclient/chains/interfaces/interfaces.go index 2272ef1dfe..7ed2d7bb4d 100644 --- a/zetaclient/chains/interfaces/interfaces.go +++ b/zetaclient/chains/interfaces/interfaces.go @@ -19,7 +19,6 @@ import ( "github.com/zeta-chain/go-tss/blame" "github.com/zeta-chain/zetacore/pkg/chains" - "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/pkg/proofs" crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" lightclienttypes "github.com/zeta-chain/zetacore/x/lightclient/types" @@ -38,18 +37,23 @@ const ( // ChainObserver is the interface for chain observer type ChainObserver interface { - Start() + Start(ctx context.Context) Stop() - IsOutboundProcessed(cctx *crosschaintypes.CrossChainTx, logger zerolog.Logger) (bool, bool, error) + IsOutboundProcessed( + ctx context.Context, + cctx *crosschaintypes.CrossChainTx, + logger zerolog.Logger, + ) (bool, bool, error) SetChainParams(observertypes.ChainParams) GetChainParams() observertypes.ChainParams GetTxID(nonce uint64) string - WatchInboundTracker() + WatchInboundTracker(ctx context.Context) error } // ChainSigner is the interface to sign transactions for a chain type ChainSigner interface { TryProcessOutbound( + ctx context.Context, cctx *crosschaintypes.CrossChainTx, outboundProc *outboundprocessor.Processor, outboundID string, @@ -63,28 +67,75 @@ type ChainSigner interface { GetERC20CustodyAddress() ethcommon.Address } -// ZetacoreClient is the client interface to interact with zetacore -type ZetacoreClient interface { - PostVoteInbound(gasLimit, retryGasLimit uint64, msg *crosschaintypes.MsgVoteInbound) (string, string, error) - PostVoteOutbound( - sendHash string, - outboundHash string, - outBlockHeight uint64, - outboundGasUsed uint64, - outboundEffectiveGasPrice *big.Int, - outboundEffectiveGasLimit uint64, - amount *big.Int, - status chains.ReceiveStatus, +// ZetacoreVoter represents voter interface. +type ZetacoreVoter interface { + PostVoteBlockHeader( + ctx context.Context, + chainID int64, + txhash []byte, + height int64, + header proofs.HeaderData, + ) (string, error) + PostVoteGasPrice( + ctx context.Context, chain chains.Chain, - nonce uint64, - coinType coin.CoinType, + gasPrice uint64, + supply string, + blockNum uint64, + ) (string, error) + PostVoteInbound( + ctx context.Context, + gasLimit, retryGasLimit uint64, + msg *crosschaintypes.MsgVoteInbound, + ) (string, string, error) + PostVoteOutbound( + ctx context.Context, + gasLimit, retryGasLimit uint64, + msg *crosschaintypes.MsgVoteOutbound, ) (string, string, error) - PostGasPrice(chain chains.Chain, gasPrice uint64, supply string, blockNum uint64) (string, error) - PostVoteBlockHeader(chainID int64, txhash []byte, height int64, header proofs.HeaderData) (string, error) - GetBlockHeaderChainState(chainID int64) (lightclienttypes.QueryGetChainStateResponse, error) + PostVoteBlameData(ctx context.Context, blame *blame.Blame, chainID int64, index string) (string, error) +} - PostBlameData(blame *blame.Blame, chainID int64, index string) (string, error) +// ZetacoreClient is the client interface to interact with zetacore +// +//go:generate mockery --name ZetacoreClient --filename zetacore_client.go --case underscore --output ../../testutils/mocks +type ZetacoreClient interface { + ZetacoreVoter + + Chain() chains.Chain + GetLogger() *zerolog.Logger + GetKeys() keyinterfaces.ObserverKeys + + GetKeyGen(ctx context.Context) (*observertypes.Keygen, error) + + GetBlockHeight(ctx context.Context) (int64, error) + GetBlockHeaderChainState(ctx context.Context, chainID int64) (*lightclienttypes.ChainState, error) + + ListPendingCCTX(ctx context.Context, chainID int64) ([]*crosschaintypes.CrossChainTx, uint64, error) + ListPendingCCTXWithinRateLimit( + ctx context.Context, + ) (*crosschaintypes.QueryListPendingCctxWithinRateLimitResponse, error) + + GetRateLimiterInput(ctx context.Context, window int64) (*crosschaintypes.QueryRateLimiterInputResponse, error) + GetPendingNoncesByChain(ctx context.Context, chainID int64) (observertypes.PendingNonces, error) + + GetCctxByNonce(ctx context.Context, chainID int64, nonce uint64) (*crosschaintypes.CrossChainTx, error) + GetOutboundTracker(ctx context.Context, chain chains.Chain, nonce uint64) (*crosschaintypes.OutboundTracker, error) + GetAllOutboundTrackerByChain( + ctx context.Context, + chainID int64, + order Order, + ) ([]crosschaintypes.OutboundTracker, error) + GetCrosschainFlags(ctx context.Context) (observertypes.CrosschainFlags, error) + GetRateLimiterFlags(ctx context.Context) (crosschaintypes.RateLimiterFlags, error) + GetObserverList(ctx context.Context) ([]string, error) + GetBTCTSSAddress(ctx context.Context, chainID int64) (string, error) + GetZetaHotKeyBalance(ctx context.Context) (sdkmath.Int, error) + GetInboundTrackersForChain(ctx context.Context, chainID int64) ([]crosschaintypes.InboundTracker, error) + + // todo(revamp): refactor input to struct AddOutboundTracker( + ctx context.Context, chainID int64, nonce uint64, txHash string, @@ -92,26 +143,9 @@ type ZetacoreClient interface { blockHash string, txIndex int64, ) (string, error) - Chain() chains.Chain - GetLogger() *zerolog.Logger - GetKeys() keyinterfaces.ObserverKeys - GetKeyGen() (*observertypes.Keygen, error) - GetBlockHeight() (int64, error) - ListPendingCctx(chainID int64) ([]*crosschaintypes.CrossChainTx, uint64, error) - ListPendingCctxWithinRatelimit() ([]*crosschaintypes.CrossChainTx, uint64, int64, string, bool, error) - GetRateLimiterInput(window int64) (crosschaintypes.QueryRateLimiterInputResponse, error) - GetPendingNoncesByChain(chainID int64) (observertypes.PendingNonces, error) - GetCctxByNonce(chainID int64, nonce uint64) (*crosschaintypes.CrossChainTx, error) - GetOutboundTracker(chain chains.Chain, nonce uint64) (*crosschaintypes.OutboundTracker, error) - GetAllOutboundTrackerByChain(chainID int64, order Order) ([]crosschaintypes.OutboundTracker, error) - GetCrosschainFlags() (observertypes.CrosschainFlags, error) - GetRateLimiterFlags() (crosschaintypes.RateLimiterFlags, error) - GetObserverList() ([]string, error) - GetBtcTssAddress(chainID int64) (string, error) - GetZetaHotKeyBalance() (sdkmath.Int, error) - GetInboundTrackersForChain(chainID int64) ([]crosschaintypes.InboundTracker, error) - Pause() - Unpause() + + Stop() + OnBeforeStop(callback func()) } // BTCRPCClient is the interface for BTC RPC client @@ -167,10 +201,17 @@ type TSSSigner interface { // Note: it specifies optionalPubkey to use a different pubkey than the current pubkey set during keygen // TODO: check if optionalPubkey is needed // https://github.com/zeta-chain/node/issues/2085 - Sign(data []byte, height uint64, nonce uint64, chainID int64, optionalPubkey string) ([65]byte, error) + Sign( + ctx context.Context, + data []byte, + height uint64, + nonce uint64, + chainID int64, + optionalPubkey string, + ) ([65]byte, error) // SignBatch signs the data in batch - SignBatch(digests [][]byte, height uint64, nonce uint64, chainID int64) ([][65]byte, error) + SignBatch(ctx context.Context, digests [][]byte, height uint64, nonce uint64, chainID int64) ([][65]byte, error) EVMAddress() ethcommon.Address BTCAddress() string diff --git a/zetaclient/config/config.go b/zetaclient/config/config.go index 6efd149628..82cb3f97f8 100644 --- a/zetaclient/config/config.go +++ b/zetaclient/config/config.go @@ -21,7 +21,7 @@ const folder string = "config" // Save saves ZetaClient config func Save(config *Config, path string) error { folderPath := filepath.Join(path, folder) - err := os.MkdirAll(folderPath, os.ModePerm) + err := os.MkdirAll(folderPath, 0o750) if err != nil { return err } @@ -53,7 +53,7 @@ func Load(path string) (Config, error) { file = filepath.Clean(file) // read config - cfg := NewConfig() + cfg := New(false) input, err := os.ReadFile(file) if err != nil { return Config{}, err diff --git a/zetaclient/config/config_chain.go b/zetaclient/config/config_chain.go index 5946c4ca62..2da362d19f 100644 --- a/zetaclient/config/config_chain.go +++ b/zetaclient/config/config_chain.go @@ -11,72 +11,83 @@ const ( ) const ( - // ConnectorAbiString is the ABI of the connector contract + // connectorAbiString is the ABI of the connector contract // TODO(revamp): we should be able to use info from Go binding - ConnectorAbiString = ` + connectorAbiString = ` [{"inputs":[{"internalType":"address","name":"_zetaTokenAddress","type":"address"},{"internalType":"address","name":"_tssAddress","type":"address"},{"internalType":"address","name":"_tssAddressUpdater","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"originSenderAddress","type":"bytes"},{"indexed":true,"internalType":"uint256","name":"originChainId","type":"uint256"},{"indexed":true,"internalType":"address","name":"destinationAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"zetaAmount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"message","type":"bytes"},{"indexed":true,"internalType":"bytes32","name":"internalSendHash","type":"bytes32"}],"name":"ZetaReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"originSenderAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"originChainId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"indexed":true,"internalType":"bytes","name":"destinationAddress","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"zetaAmount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"message","type":"bytes"},{"indexed":true,"internalType":"bytes32","name":"internalSendHash","type":"bytes32"}],"name":"ZetaReverted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"originSenderAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"destinationAddress","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"zetaAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gasLimit","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"message","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"zetaParams","type":"bytes"}],"name":"ZetaSent","type":"event"},{"inputs":[],"name":"getLockedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"originSenderAddress","type":"bytes"},{"internalType":"uint256","name":"originChainId","type":"uint256"},{"internalType":"address","name":"destinationAddress","type":"address"},{"internalType":"uint256","name":"zetaAmount","type":"uint256"},{"internalType":"bytes","name":"message","type":"bytes"},{"internalType":"bytes32","name":"internalSendHash","type":"bytes32"}],"name":"onReceive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"originSenderAddress","type":"address"},{"internalType":"uint256","name":"originChainId","type":"uint256"},{"internalType":"bytes","name":"destinationAddress","type":"bytes"},{"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"internalType":"uint256","name":"zetaAmount","type":"uint256"},{"internalType":"bytes","name":"message","type":"bytes"},{"internalType":"bytes32","name":"internalSendHash","type":"bytes32"}],"name":"onRevert","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceTssAddressUpdater","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"internalType":"bytes","name":"destinationAddress","type":"bytes"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"bytes","name":"message","type":"bytes"},{"internalType":"uint256","name":"zetaAmount","type":"uint256"},{"internalType":"bytes","name":"zetaParams","type":"bytes"}],"internalType":"struct ZetaInterfaces.SendInput","name":"input","type":"tuple"}],"name":"send","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tssAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tssAddressUpdater","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tssAddress","type":"address"}],"name":"updateTssAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"zetaToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]` - // ERC20CustodyAbiString is the ABI of the erc20 custodu contract + // erc20CustodyAbiString is the ABI of the erc20 custodu contract // TODO(revamp): we should be able to use info from Go binding - ERC20CustodyAbiString = ` + erc20CustodyAbiString = ` [{"inputs":[{"internalType":"address","name":"_TSSAddress","type":"address"},{"internalType":"address","name":"_TSSAddressUpdater","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidSender","type":"error"},{"inputs":[],"name":"InvalidTSSUpdater","type":"error"},{"inputs":[],"name":"IsPaused","type":"error"},{"inputs":[],"name":"NotPaused","type":"error"},{"inputs":[],"name":"NotWhitelisted","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"recipient","type":"bytes"},{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"message","type":"bytes"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"asset","type":"address"}],"name":"Unwhitelisted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"asset","type":"address"}],"name":"Whitelisted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdrawn","type":"event"},{"inputs":[],"name":"TSSAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TSSAddressUpdater","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"recipient","type":"bytes"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"message","type":"bytes"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceTSSAddressUpdater","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"unwhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"updateTSSAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"whitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]` ) // GetConnectorABI returns the ABI of the connector contract func GetConnectorABI() string { - return ConnectorAbiString + return connectorAbiString } // GetERC20CustodyABI returns the ABI of the erc20 custody contract func GetERC20CustodyABI() string { - return ERC20CustodyAbiString + return erc20CustodyAbiString } -// New returns a new config -// It is initialize with default chain configs -func New() Config { - return Config{ - cfgLock: &sync.RWMutex{}, - EVMChainConfigs: evmChainsConfigs, - BitcoinConfig: bitcoinConfigRegnet, +// New constructs Config optionally with default values. +func New(setDefaults bool) Config { + cfg := Config{ + EVMChainConfigs: make(map[int64]EVMConfig), + BitcoinConfig: BTCConfig{}, + + mu: &sync.RWMutex{}, + } + + if setDefaults { + cfg.BitcoinConfig = bitcoinConfigRegnet() + cfg.EVMChainConfigs = evmChainsConfigs() } + + return cfg } // bitcoinConfigRegnet contains Bitcoin config for regnet -var bitcoinConfigRegnet = BTCConfig{ - RPCUsername: "smoketest", // smoketest is the previous name for E2E test, we keep this name for compatibility between client versions in upgrade test - RPCPassword: "123", - RPCHost: "bitcoin:18443", - RPCParams: "regtest", +func bitcoinConfigRegnet() BTCConfig { + return BTCConfig{ + RPCUsername: "smoketest", // smoketest is the previous name for E2E test, we keep this name for compatibility between client versions in upgrade test + RPCPassword: "123", + RPCHost: "bitcoin:18443", + RPCParams: "regtest", + } } // evmChainsConfigs contains EVM chain configs // it contains list of EVM chains with empty endpoint except for localnet -var evmChainsConfigs = map[int64]EVMConfig{ - chains.Ethereum.ChainId: { - Chain: chains.Ethereum, - }, - chains.BscMainnet.ChainId: { - Chain: chains.BscMainnet, - }, - chains.Goerli.ChainId: { - Chain: chains.Goerli, - Endpoint: "", - }, - chains.Sepolia.ChainId: { - Chain: chains.Sepolia, - Endpoint: "", - }, - chains.BscTestnet.ChainId: { - Chain: chains.BscTestnet, - Endpoint: "", - }, - chains.Mumbai.ChainId: { - Chain: chains.Mumbai, - Endpoint: "", - }, - chains.GoerliLocalnet.ChainId: { - Chain: chains.GoerliLocalnet, - Endpoint: "http://eth:8545", - }, +func evmChainsConfigs() map[int64]EVMConfig { + return map[int64]EVMConfig{ + chains.Ethereum.ChainId: { + Chain: chains.Ethereum, + }, + chains.BscMainnet.ChainId: { + Chain: chains.BscMainnet, + }, + chains.Goerli.ChainId: { + Chain: chains.Goerli, + Endpoint: "", + }, + chains.Sepolia.ChainId: { + Chain: chains.Sepolia, + Endpoint: "", + }, + chains.BscTestnet.ChainId: { + Chain: chains.BscTestnet, + Endpoint: "", + }, + chains.Mumbai.ChainId: { + Chain: chains.Mumbai, + Endpoint: "", + }, + chains.GoerliLocalnet.ChainId: { + Chain: chains.GoerliLocalnet, + Endpoint: "http://eth:8545", + }, + } } diff --git a/zetaclient/config/types.go b/zetaclient/config/types.go index 96cdf24a4c..2ff5f1a657 100644 --- a/zetaclient/config/types.go +++ b/zetaclient/config/types.go @@ -57,8 +57,6 @@ type ComplianceConfig struct { // TODO: use snake case for json fields // https://github.com/zeta-chain/node/issues/1020 type Config struct { - cfgLock *sync.RWMutex `json:"-"` - Peer string `json:"Peer"` PublicIP string `json:"PublicIP"` LogFormat string `json:"LogFormat"` @@ -84,29 +82,22 @@ type Config struct { // compliance config ComplianceConfig ComplianceConfig `json:"ComplianceConfig"` -} -// NewConfig returns a new Config with initialize EVM chain mapping and a new mutex -// TODO(revamp): consolidate with New function -func NewConfig() Config { - return Config{ - cfgLock: &sync.RWMutex{}, - EVMChainConfigs: make(map[int64]EVMConfig), - } + mu *sync.RWMutex } // GetEVMConfig returns the EVM config for the given chain ID func (c Config) GetEVMConfig(chainID int64) (EVMConfig, bool) { - c.cfgLock.RLock() - defer c.cfgLock.RUnlock() + c.mu.RLock() + defer c.mu.RUnlock() evmCfg, found := c.EVMChainConfigs[chainID] return evmCfg, found } // GetAllEVMConfigs returns a map of all EVM configs func (c Config) GetAllEVMConfigs() map[int64]EVMConfig { - c.cfgLock.RLock() - defer c.cfgLock.RUnlock() + c.mu.RLock() + defer c.mu.RUnlock() // deep copy evm configs copied := make(map[int64]EVMConfig, len(c.EVMChainConfigs)) @@ -118,8 +109,8 @@ func (c Config) GetAllEVMConfigs() map[int64]EVMConfig { // GetBTCConfig returns the BTC config func (c Config) GetBTCConfig() (BTCConfig, bool) { - c.cfgLock.RLock() - defer c.cfgLock.RUnlock() + c.mu.RLock() + defer c.mu.RUnlock() return c.BitcoinConfig, c.BitcoinConfig != (BTCConfig{}) } @@ -146,8 +137,8 @@ func (c Config) GetRestrictedAddressBook() map[string]bool { } // GetKeyringBackend returns the keyring backend -func (c *Config) GetKeyringBackend() KeyringBackend { - c.cfgLock.RLock() - defer c.cfgLock.RUnlock() +func (c Config) GetKeyringBackend() KeyringBackend { + c.mu.RLock() + defer c.mu.RUnlock() return c.KeyringBackend } diff --git a/zetaclient/config/types_test.go b/zetaclient/config/types_test.go deleted file mode 100644 index d7b82b3200..0000000000 --- a/zetaclient/config/types_test.go +++ /dev/null @@ -1 +0,0 @@ -package config_test diff --git a/zetaclient/context/app_test.go b/zetaclient/context/app_test.go index 640e5d7386..39847e2097 100644 --- a/zetaclient/context/app_test.go +++ b/zetaclient/context/app_test.go @@ -17,7 +17,7 @@ import ( func TestNew(t *testing.T) { var ( - testCfg = config.NewConfig() + testCfg = config.New(false) logger = zerolog.Nop() ) @@ -51,7 +51,7 @@ func TestNew(t *testing.T) { t.Run("should return nil chain params if chain id is not found", func(t *testing.T) { // create config with btc config - testCfg := config.NewConfig() + testCfg := config.New(false) testCfg.BitcoinConfig = config.BTCConfig{ RPCUsername: "test_user", RPCPassword: "test_password", @@ -69,7 +69,7 @@ func TestNew(t *testing.T) { }) t.Run("should create new zetacore context with config containing evm chain params", func(t *testing.T) { - testCfg := config.NewConfig() + testCfg := config.New(false) testCfg.EVMChainConfigs = map[int64]config.EVMConfig{ 1: { Chain: chains.Chain{ @@ -103,7 +103,7 @@ func TestNew(t *testing.T) { }) t.Run("should create new zetacore context with config containing btc config", func(t *testing.T) { - testCfg := config.NewConfig() + testCfg := config.New(false) testCfg.BitcoinConfig = config.BTCConfig{ RPCUsername: "test username", RPCPassword: "test password", @@ -117,7 +117,7 @@ func TestNew(t *testing.T) { func TestAppContextUpdate(t *testing.T) { var ( - testCfg = config.NewConfig() + testCfg = config.New(false) logger = zerolog.Nop() ) @@ -203,7 +203,7 @@ func TestAppContextUpdate(t *testing.T) { t.Run( "should update zetacore context after being created from config with evm and btc chain params", func(t *testing.T) { - testCfg := config.NewConfig() + testCfg := config.New(false) testCfg.EVMChainConfigs = map[int64]config.EVMConfig{ 1: { Chain: chains.Chain{ @@ -373,8 +373,8 @@ func TestIsInboundObservationEnabled(t *testing.T) { func TestGetBTCChainAndConfig(t *testing.T) { logger := zerolog.Nop() - emptyConfig := config.NewConfig() - nonEmptyConfig := config.New() + emptyConfig := config.New(false) + nonEmptyConfig := config.New(true) assertEmpty := func(t *testing.T, chain chains.Chain, btcConfig config.BTCConfig, enabled bool) { assert.Empty(t, chain) @@ -463,7 +463,7 @@ func TestGetBTCChainAndConfig(t *testing.T) { func TestGetBlockHeaderEnabledChains(t *testing.T) { // ARRANGE // Given app config - appContext := context.New(config.New(), zerolog.Nop()) + appContext := context.New(config.New(false), zerolog.Nop()) // That was eventually updated appContext.Update( @@ -499,7 +499,7 @@ func TestGetBlockHeaderEnabledChains(t *testing.T) { func TestGetAdditionalChains(t *testing.T) { // ARRANGE // Given app config - appContext := context.New(config.New(), zerolog.Nop()) + appContext := context.New(config.New(false), zerolog.Nop()) additionalChains := []chains.Chain{ sample.Chain(1), @@ -536,7 +536,7 @@ func makeAppContext( headerSupportedChains []lightclienttypes.HeaderSupportedChain, ) *context.AppContext { // create config - cfg := config.NewConfig() + cfg := config.New(false) logger := zerolog.Nop() cfg.EVMChainConfigs[evmChain.ChainId] = config.EVMConfig{ Chain: evmChain, diff --git a/zetaclient/context/context.go b/zetaclient/context/context.go index 3f8b6177fa..4d0b06866a 100644 --- a/zetaclient/context/context.go +++ b/zetaclient/context/context.go @@ -24,3 +24,15 @@ func FromContext(ctx goctx.Context) (*AppContext, error) { return app, nil } + +// Copy copies AppContext from one context to another (is present). +// This is useful when you want to drop timeouts and deadlines from the context +// (e.g. run something in another goroutine). +func Copy(from, to goctx.Context) goctx.Context { + app, err := FromContext(from) + if err != nil { + return to + } + + return WithAppContext(to, app) +} diff --git a/zetaclient/context/context_test.go b/zetaclient/context/context_test.go index 5bde4596d6..be9dab83a4 100644 --- a/zetaclient/context/context_test.go +++ b/zetaclient/context/context_test.go @@ -24,7 +24,7 @@ func TestFromContext(t *testing.T) { // ARRANGE #2 // Given basic app - app := context.New(config.NewConfig(), zerolog.Nop()) + app := context.New(config.New(false), zerolog.Nop()) // That is included in the ctx ctx = context.WithAppContext(ctx, app) @@ -38,3 +38,20 @@ func TestFromContext(t *testing.T) { assert.Equal(t, app, app2) assert.NotEmpty(t, app.Config()) } + +func TestCopy(t *testing.T) { + // ARRANGE + var ( + app = context.New(config.New(false), zerolog.Nop()) + ctx1 = context.WithAppContext(goctx.Background(), app) + ) + + // ACT + ctx2 := context.Copy(ctx1, goctx.Background()) + + // ASSERT + app2, err := context.FromContext(ctx2) + assert.NoError(t, err) + assert.NotNil(t, app2) + assert.Equal(t, app, app2) +} diff --git a/zetaclient/orchestrator/orchestrator.go b/zetaclient/orchestrator/orchestrator.go index b233c9d582..951c4146f6 100644 --- a/zetaclient/orchestrator/orchestrator.go +++ b/zetaclient/orchestrator/orchestrator.go @@ -2,6 +2,7 @@ package orchestrator import ( + "context" "fmt" "math" "time" @@ -10,13 +11,14 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" "github.com/rs/zerolog" + "github.com/zeta-chain/zetacore/pkg/bg" "github.com/zeta-chain/zetacore/pkg/chains" zetamath "github.com/zeta-chain/zetacore/pkg/math" "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" btcobserver "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/observer" "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" - "github.com/zeta-chain/zetacore/zetaclient/context" + zctx "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/zetaclient/metrics" "github.com/zeta-chain/zetacore/zetaclient/outboundprocessor" "github.com/zeta-chain/zetacore/zetaclient/ratelimiter" @@ -65,6 +67,7 @@ type Orchestrator struct { // NewOrchestrator creates a new orchestrator func NewOrchestrator( + ctx context.Context, zetacoreClient interfaces.ZetacoreClient, signerMap map[int64]interfaces.ChainSigner, observerMap map[int64]interfaces.ChainObserver, @@ -90,7 +93,7 @@ func NewOrchestrator( // create outbound processor oc.outboundProc = outboundprocessor.NewProcessor(logger) - balance, err := zetacoreClient.GetZetaHotKeyBalance() + balance, err := zetacoreClient.GetZetaHotKeyBalance(ctx) if err != nil { oc.logger.Std.Error().Err(err).Msg("error getting last balance of the hot key") } @@ -100,7 +103,7 @@ func NewOrchestrator( } // MonitorCore starts the orchestrator for CCTXs -func (oc *Orchestrator) MonitorCore(appContext *context.AppContext) error { +func (oc *Orchestrator) MonitorCore(ctx context.Context) error { signerAddress, err := oc.zetacoreClient.GetKeys().GetAddress() if err != nil { return fmt.Errorf("failed to get signer address: %w", err) @@ -108,26 +111,24 @@ func (oc *Orchestrator) MonitorCore(appContext *context.AppContext) error { oc.logger.Std.Info().Msgf("Starting orchestrator for signer: %s", signerAddress) // start cctx scheduler - go oc.StartCctxScheduler(appContext) - - // watch for upgrade plan from zetacore - go func() { - // wait for upgrade plan signal to arrive - oc.zetacoreClient.Pause() + bg.Work(ctx, oc.StartCctxScheduler, bg.WithName("StartCctxScheduler"), bg.WithLogger(oc.logger.Std)) + shutdownOrchestrator := func() { // now stop orchestrator and all observers close(oc.stop) for _, c := range oc.observerMap { c.Stop() } - }() + } + + oc.zetacoreClient.OnBeforeStop(shutdownOrchestrator) return nil } // GetUpdatedSigner returns signer with updated chain parameters func (oc *Orchestrator) GetUpdatedSigner( - appContext *context.AppContext, + appContext *zctx.AppContext, chainID int64, ) (interfaces.ChainSigner, error) { signer, found := oc.signerMap[chainID] @@ -158,7 +159,7 @@ func (oc *Orchestrator) GetUpdatedSigner( // GetUpdatedChainObserver returns chain observer with updated chain parameters func (oc *Orchestrator) GetUpdatedChainObserver( - appContext *context.AppContext, + appContext *zctx.AppContext, chainID int64, ) (interfaces.ChainObserver, error) { observer, found := oc.observerMap[chainID] @@ -186,12 +187,13 @@ func (oc *Orchestrator) GetUpdatedChainObserver( return observer, nil } -// GetPendingCctxsWithinRatelimit get pending cctxs across foreign chains within rate limit -func (oc *Orchestrator) GetPendingCctxsWithinRatelimit( +// GetPendingCctxsWithinRateLimit get pending cctxs across foreign chains within rate limit +func (oc *Orchestrator) GetPendingCctxsWithinRateLimit( + ctx context.Context, foreignChains []chains.Chain, ) (map[int64][]*types.CrossChainTx, error) { // get rate limiter flags - rateLimitFlags, err := oc.zetacoreClient.GetRateLimiterFlags() + rateLimitFlags, err := oc.zetacoreClient.GetRateLimiterFlags(ctx) if err != nil { return nil, err } @@ -203,7 +205,7 @@ func (oc *Orchestrator) GetPendingCctxsWithinRatelimit( cctxsMap := make(map[int64][]*types.CrossChainTx) if !rateLimiterUsable { for _, chain := range foreignChains { - resp, _, err := oc.zetacoreClient.ListPendingCctx(chain.ChainId) + resp, _, err := oc.zetacoreClient.ListPendingCCTX(ctx, chain.ChainId) if err == nil && resp != nil { cctxsMap[chain.ChainId] = resp } @@ -212,11 +214,11 @@ func (oc *Orchestrator) GetPendingCctxsWithinRatelimit( } // query rate limiter input - resp, err := oc.zetacoreClient.GetRateLimiterInput(rateLimitFlags.Window) + resp, err := oc.zetacoreClient.GetRateLimiterInput(ctx, rateLimitFlags.Window) if err != nil { return nil, err } - input, ok := ratelimiter.NewInput(resp) + input, ok := ratelimiter.NewInput(*resp) if !ok { return nil, fmt.Errorf("failed to create rate limiter input") } @@ -238,17 +240,22 @@ func (oc *Orchestrator) GetPendingCctxsWithinRatelimit( // StartCctxScheduler schedules keysigns for cctxs on each ZetaChain block (the ticker) // TODO(revamp): make this function simpler -func (oc *Orchestrator) StartCctxScheduler(appContext *context.AppContext) { +func (oc *Orchestrator) StartCctxScheduler(ctx context.Context) error { + app, err := zctx.FromContext(ctx) + if err != nil { + return err + } + observeTicker := time.NewTicker(3 * time.Second) var lastBlockNum int64 for { select { case <-oc.stop: oc.logger.Std.Warn().Msg("StartCctxScheduler: stopped") - return + return nil case <-observeTicker.C: { - bn, err := oc.zetacoreClient.GetBlockHeight() + bn, err := oc.zetacoreClient.GetBlockHeight(ctx) if err != nil { oc.logger.Std.Error().Err(err).Msg("StartCctxScheduler: GetBlockHeight fail") continue @@ -266,7 +273,7 @@ func (oc *Orchestrator) StartCctxScheduler(appContext *context.AppContext) { oc.logger.Std.Debug().Msgf("StartCctxScheduler: zetacore heart beat: %d", bn) } - balance, err := oc.zetacoreClient.GetZetaHotKeyBalance() + balance, err := oc.zetacoreClient.GetZetaHotKeyBalance(ctx) if err != nil { oc.logger.Std.Error().Err(err).Msgf("couldn't get operator balance") } else { @@ -281,10 +288,10 @@ func (oc *Orchestrator) StartCctxScheduler(appContext *context.AppContext) { metrics.HotKeyBurnRate.Set(float64(oc.ts.HotKeyBurnRate.GetBurnRate().Int64())) // get supported external chains - externalChains := appContext.GetEnabledExternalChains() + externalChains := app.GetEnabledExternalChains() // query pending cctxs across all external chains within rate limit - cctxMap, err := oc.GetPendingCctxsWithinRatelimit(externalChains) + cctxMap, err := oc.GetPendingCctxsWithinRateLimit(ctx, externalChains) if err != nil { oc.logger.Std.Error().Err(err).Msgf("StartCctxScheduler: GetPendingCctxsWithinRatelimit failed") } @@ -299,30 +306,30 @@ func (oc *Orchestrator) StartCctxScheduler(appContext *context.AppContext) { } // update chain parameters for signer and chain observer - signer, err := oc.GetUpdatedSigner(appContext, c.ChainId) + signer, err := oc.GetUpdatedSigner(app, c.ChainId) if err != nil { oc.logger.Std.Error(). Err(err). Msgf("StartCctxScheduler: GetUpdatedSigner failed for chain %d", c.ChainId) continue } - ob, err := oc.GetUpdatedChainObserver(appContext, c.ChainId) + ob, err := oc.GetUpdatedChainObserver(app, c.ChainId) if err != nil { oc.logger.Std.Error(). Err(err). Msgf("StartCctxScheduler: GetUpdatedChainObserver failed for chain %d", c.ChainId) continue } - if !appContext.IsOutboundObservationEnabled(ob.GetChainParams()) { + if !app.IsOutboundObservationEnabled(ob.GetChainParams()) { continue } - // #nosec G701 range is verified + // #nosec G115 range is verified zetaHeight := uint64(bn) - if chains.IsEVMChain(c.ChainId, appContext.GetAdditionalChains()) { - oc.ScheduleCctxEVM(zetaHeight, c.ChainId, cctxList, ob, signer) - } else if chains.IsBitcoinChain(c.ChainId, appContext.GetAdditionalChains()) { - oc.ScheduleCctxBTC(zetaHeight, c.ChainId, cctxList, ob, signer) + if chains.IsEVMChain(c.ChainId, app.GetAdditionalChains()) { + oc.ScheduleCctxEVM(ctx, zetaHeight, c.ChainId, cctxList, ob, signer) + } else if chains.IsBitcoinChain(c.ChainId, app.GetAdditionalChains()) { + oc.ScheduleCctxBTC(ctx, zetaHeight, c.ChainId, cctxList, ob, signer) } else { oc.logger.Std.Error().Msgf("StartCctxScheduler: unsupported chain %d", c.ChainId) continue @@ -340,13 +347,14 @@ func (oc *Orchestrator) StartCctxScheduler(appContext *context.AppContext) { // ScheduleCctxEVM schedules evm outbound keysign on each ZetaChain block (the ticker) func (oc *Orchestrator) ScheduleCctxEVM( + ctx context.Context, zetaHeight uint64, chainID int64, cctxList []*types.CrossChainTx, observer interfaces.ChainObserver, signer interfaces.ChainSigner, ) { - res, err := oc.zetacoreClient.GetAllOutboundTrackerByChain(chainID, interfaces.Ascending) + res, err := oc.zetacoreClient.GetAllOutboundTrackerByChain(ctx, chainID, interfaces.Ascending) if err != nil { oc.logger.Std.Warn().Err(err).Msgf("ScheduleCctxEVM: GetAllOutboundTrackerByChain failed for chain %d", chainID) return @@ -356,9 +364,9 @@ func (oc *Orchestrator) ScheduleCctxEVM( trackerMap[v.Nonce] = true } outboundScheduleLookahead := observer.GetChainParams().OutboundScheduleLookahead - // #nosec G701 always in range + // #nosec G115 always in range outboundScheduleLookback := uint64(float64(outboundScheduleLookahead) * evmOutboundLookbackFactor) - // #nosec G701 positive + // #nosec G115 positive outboundScheduleInterval := uint64(observer.GetChainParams().OutboundScheduleInterval) for idx, cctx := range cctxList { @@ -378,7 +386,7 @@ func (oc *Orchestrator) ScheduleCctxEVM( } // try confirming the outbound - included, _, err := observer.IsOutboundProcessed(cctx, oc.logger.Std) + included, _, err := observer.IsOutboundProcessed(ctx, cctx, oc.logger.Std) if err != nil { oc.logger.Std.Error(). Err(err). @@ -418,10 +426,18 @@ func (oc *Orchestrator) ScheduleCctxEVM( oc.outboundProc.StartTryProcess(outboundID) oc.logger.Std.Debug(). Msgf("ScheduleCctxEVM: sign outbound %s with value %d\n", outboundID, cctx.GetCurrentOutboundParam().Amount) - go signer.TryProcessOutbound(cctx, oc.outboundProc, outboundID, observer, oc.zetacoreClient, zetaHeight) + go signer.TryProcessOutbound( + ctx, + cctx, + oc.outboundProc, + outboundID, + observer, + oc.zetacoreClient, + zetaHeight, + ) } - // #nosec G701 always in range + // #nosec G115 always in range if int64(idx) >= outboundScheduleLookahead-1 { // only look at 'lookahead' cctxs per chain break } @@ -433,6 +449,7 @@ func (oc *Orchestrator) ScheduleCctxEVM( // 2. schedule keysign only when nonce-mark UTXO is available // 3. stop keysign when lookahead is reached func (oc *Orchestrator) ScheduleCctxBTC( + ctx context.Context, zetaHeight uint64, chainID int64, cctxList []*types.CrossChainTx, @@ -444,7 +461,7 @@ func (oc *Orchestrator) ScheduleCctxBTC( oc.logger.Std.Error().Msgf("ScheduleCctxBTC: chain observer is not a bitcoin observer") return } - // #nosec G701 positive + // #nosec G115 positive interval := uint64(observer.GetChainParams().OutboundScheduleInterval) lookahead := observer.GetChainParams().OutboundScheduleLookahead @@ -460,7 +477,7 @@ func (oc *Orchestrator) ScheduleCctxBTC( continue } // try confirming the outbound - included, confirmed, err := btcObserver.IsOutboundProcessed(cctx, oc.logger.Std) + included, confirmed, err := btcObserver.IsOutboundProcessed(ctx, cctx, oc.logger.Std) if err != nil { oc.logger.Std.Error(). Err(err). @@ -489,7 +506,15 @@ func (oc *Orchestrator) ScheduleCctxBTC( if nonce%interval == zetaHeight%interval && !oc.outboundProc.IsOutboundActive(outboundID) { oc.outboundProc.StartTryProcess(outboundID) oc.logger.Std.Debug().Msgf("ScheduleCctxBTC: sign outbound %s with value %d\n", outboundID, params.Amount) - go signer.TryProcessOutbound(cctx, oc.outboundProc, outboundID, observer, oc.zetacoreClient, zetaHeight) + go signer.TryProcessOutbound( + ctx, + cctx, + oc.outboundProc, + outboundID, + observer, + oc.zetacoreClient, + zetaHeight, + ) } } } diff --git a/zetaclient/orchestrator/orchestrator_test.go b/zetaclient/orchestrator/orchestrator_test.go index 9eab334bfe..8637834a6e 100644 --- a/zetaclient/orchestrator/orchestrator_test.go +++ b/zetaclient/orchestrator/orchestrator_test.go @@ -1,12 +1,15 @@ package orchestrator import ( + "context" "testing" sdk "github.com/cosmos/cosmos-sdk/types" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + zctx "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" @@ -16,7 +19,6 @@ 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" - "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/zetaclient/testutils" "github.com/zeta-chain/zetacore/zetaclient/testutils/mocks" ) @@ -56,9 +58,9 @@ func MockOrchestrator( func CreateAppContext( evmChain, btcChain chains.Chain, evmChainParams, btcChainParams *observertypes.ChainParams, -) *context.AppContext { +) *zctx.AppContext { // new config - cfg := config.NewConfig() + cfg := config.New(false) cfg.EVMChainConfigs[evmChain.ChainId] = config.EVMConfig{ Chain: evmChain, } @@ -66,7 +68,7 @@ func CreateAppContext( RPCHost: "localhost", } // new zetacore context - appContext := context.New(cfg, zerolog.Nop()) + appContext := zctx.New(cfg, zerolog.Nop()) evmChainParamsMap := make(map[int64]*observertypes.ChainParams) evmChainParamsMap[evmChain.ChainId] = evmChainParams ccFlags := sample.CrosschainFlags() @@ -204,7 +206,9 @@ func Test_GetUpdatedChainObserver(t *testing.T) { }) } -func Test_GetPendingCctxsWithinRatelimit(t *testing.T) { +func Test_GetPendingCctxsWithinRateLimit(t *testing.T) { + ctx := context.Background() + // define test foreign chains ethChain := chains.Ethereum btcChain := chains.BitcoinMainnet @@ -308,7 +312,7 @@ func Test_GetPendingCctxsWithinRatelimit(t *testing.T) { Height: 100, CctxsMissed: allCctxsMissed, CctxsPending: allCctxsPending, - // #nosec G701 len always positive + // #nosec G115 len always positive TotalPending: uint64(len(allCctxsPending) + len(allCctxsMissed)), PastCctxsValue: sdk.NewInt(10).Mul(sdk.NewInt(1e18)).String(), // 10 ZETA PendingCctxsValue: sdk.NewInt(90).Mul(sdk.NewInt(1e18)).String(), // 90 ZETA @@ -353,25 +357,25 @@ func Test_GetPendingCctxsWithinRatelimit(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // create mock zetacore client - client := mocks.NewMockZetacoreClient() + client := mocks.NewZetacoreClient(t) // load mock data client.WithRateLimiterFlags(tt.rateLimiterFlags) + client.WithRateLimiterInput(tt.response) client.WithPendingCctx(ethChain.ChainId, tt.ethCctxsFallback) client.WithPendingCctx(btcChain.ChainId, tt.btcCctxsFallback) - client.WithRateLimiterInput(tt.response) // create orchestrator orchestrator := MockOrchestrator(t, client, ethChain, btcChain, ethChainParams, btcChainParams) // run the test - cctxsMap, err := orchestrator.GetPendingCctxsWithinRatelimit(foreignChains) + cctxsMap, err := orchestrator.GetPendingCctxsWithinRateLimit(ctx, foreignChains) if tt.fail { - require.Error(t, err) - require.Nil(t, cctxsMap) + assert.Error(t, err) + assert.Empty(t, cctxsMap) } else { - require.NoError(t, err) - require.Equal(t, tt.expectedCctxsMap, cctxsMap) + assert.NoError(t, err) + assert.Equal(t, tt.expectedCctxsMap, cctxsMap) } }) } diff --git a/zetaclient/supplychecker/zeta_supply_checker.go b/zetaclient/supplychecker/zeta_supply_checker.go index 3355d4ff5e..53a61c707b 100644 --- a/zetaclient/supplychecker/zeta_supply_checker.go +++ b/zetaclient/supplychecker/zeta_supply_checker.go @@ -3,6 +3,7 @@ package supplychecker import ( + "context" "fmt" sdkmath "cosmossdk.io/math" @@ -16,14 +17,13 @@ import ( "github.com/zeta-chain/zetacore/x/crosschain/types" "github.com/zeta-chain/zetacore/zetaclient/chains/evm/observer" "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" - "github.com/zeta-chain/zetacore/zetaclient/context" + zctx "github.com/zeta-chain/zetacore/zetaclient/context" clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" "github.com/zeta-chain/zetacore/zetaclient/zetacore" ) // ZetaSupplyChecker is a utility to check the total supply of Zeta tokens type ZetaSupplyChecker struct { - appContext *context.AppContext evmClient map[int64]*ethclient.Client zetaClient *zetacore.Client ticker *clienttypes.DynamicTicker @@ -36,39 +36,44 @@ type ZetaSupplyChecker struct { // NewZetaSupplyChecker creates a new ZetaSupplyChecker func NewZetaSupplyChecker( - appContext *context.AppContext, + ctx context.Context, zetaClient *zetacore.Client, logger zerolog.Logger, -) (ZetaSupplyChecker, error) { +) (*ZetaSupplyChecker, error) { dynamicTicker, err := clienttypes.NewDynamicTicker("ZETASupplyTicker", 15) if err != nil { - return ZetaSupplyChecker{}, err + return nil, err } - zetaSupplyChecker := ZetaSupplyChecker{ + app, err := zctx.FromContext(ctx) + if err != nil { + return nil, err + } + + zetaSupplyChecker := &ZetaSupplyChecker{ stop: make(chan struct{}), ticker: dynamicTicker, evmClient: make(map[int64]*ethclient.Client), logger: logger.With(). Str("module", "ZetaSupplyChecker"). Logger(), - appContext: appContext, zetaClient: zetaClient, } - for _, evmConfig := range appContext.Config().GetAllEVMConfigs() { + for _, evmConfig := range app.Config().GetAllEVMConfigs() { if evmConfig.Chain.IsZetaChain() { continue } client, err := ethclient.Dial(evmConfig.Endpoint) if err != nil { - return zetaSupplyChecker, err + return nil, err } + zetaSupplyChecker.evmClient[evmConfig.Chain.ChainId] = client } for chainID := range zetaSupplyChecker.evmClient { - chain, found := chains.GetChainFromChainID(chainID, appContext.GetAdditionalChains()) + chain, found := chains.GetChainFromChainID(chainID, app.GetAdditionalChains()) if !found { return zetaSupplyChecker, fmt.Errorf("chain not found for chain id %d", chainID) } @@ -80,15 +85,16 @@ func NewZetaSupplyChecker( } } - balances, err := zetaSupplyChecker.zetaClient.GetGenesisSupply() + balances, err := zetaSupplyChecker.zetaClient.GetGenesisSupply(ctx) if err != nil { - return zetaSupplyChecker, err + return nil, err } tokensMintedAtBeginBlock, ok := sdkmath.NewIntFromString("200000000000000000") if !ok { - return zetaSupplyChecker, fmt.Errorf("error parsing tokens minted at begin block") + return nil, fmt.Errorf("error parsing tokens minted at begin block") } + zetaSupplyChecker.genesisSupply = balances.Add(tokensMintedAtBeginBlock) logger.Info(). @@ -98,12 +104,12 @@ func NewZetaSupplyChecker( } // Start starts the ZetaSupplyChecker -func (zs *ZetaSupplyChecker) Start() { +func (zs *ZetaSupplyChecker) Start(ctx context.Context) { defer zs.ticker.Stop() for { select { case <-zs.ticker.C(): - err := zs.CheckZetaTokenSupply() + err := zs.CheckZetaTokenSupply(ctx) if err != nil { zs.logger.Error().Err(err).Msgf("ZetaSupplyChecker error") } @@ -120,10 +126,15 @@ func (zs *ZetaSupplyChecker) Stop() { } // CheckZetaTokenSupply checks the total supply of Zeta tokens -func (zs *ZetaSupplyChecker) CheckZetaTokenSupply() error { +func (zs *ZetaSupplyChecker) CheckZetaTokenSupply(ctx context.Context) error { + app, err := zctx.FromContext(ctx) + if err != nil { + return err + } + externalChainTotalSupply := sdkmath.ZeroInt() for _, chain := range zs.externalEvmChain { - externalEvmChainParams, ok := zs.appContext.GetEVMChainParams(chain.ChainId) + externalEvmChainParams, ok := app.GetEVMChainParams(chain.ChainId) if !ok { return fmt.Errorf("externalEvmChainParams not found for chain id %d", chain.ChainId) } @@ -149,7 +160,7 @@ func (zs *ZetaSupplyChecker) CheckZetaTokenSupply() error { externalChainTotalSupply = externalChainTotalSupply.Add(totalSupplyInt) } - evmChainParams, ok := zs.appContext.GetEVMChainParams(zs.ethereumChain.ChainId) + evmChainParams, ok := app.GetEVMChainParams(zs.ethereumChain.ChainId) if !ok { return fmt.Errorf("eth config not found for chain id %d", zs.ethereumChain.ChainId) } @@ -174,16 +185,16 @@ func (zs *ZetaSupplyChecker) CheckZetaTokenSupply() error { return fmt.Errorf("error parsing eth locked amount") } - zetaInTransit, err := zs.GetAmountOfZetaInTransit() + zetaInTransit, err := zs.GetAmountOfZetaInTransit(ctx) if err != nil { return err } - zetaTokenSupplyOnNode, err := zs.zetaClient.GetZetaTokenSupplyOnNode() + zetaTokenSupplyOnNode, err := zs.zetaClient.GetZetaTokenSupplyOnNode(ctx) if err != nil { return err } - abortedAmount, err := zs.AbortedTxAmount() + abortedAmount, err := zs.AbortedTxAmount(ctx) if err != nil { return err } @@ -202,8 +213,8 @@ func (zs *ZetaSupplyChecker) CheckZetaTokenSupply() error { } // AbortedTxAmount returns the amount of Zeta tokens in aborted transactions -func (zs *ZetaSupplyChecker) AbortedTxAmount() (sdkmath.Int, error) { - amount, err := zs.zetaClient.GetAbortedZetaAmount() +func (zs *ZetaSupplyChecker) AbortedTxAmount(ctx context.Context) (sdkmath.Int, error) { + amount, err := zs.zetaClient.GetAbortedZetaAmount(ctx) if err != nil { return sdkmath.ZeroInt(), errors.Wrap(err, "error getting aborted zeta amount") } @@ -215,10 +226,10 @@ func (zs *ZetaSupplyChecker) AbortedTxAmount() (sdkmath.Int, error) { } // GetAmountOfZetaInTransit returns the amount of Zeta tokens in transit -func (zs *ZetaSupplyChecker) GetAmountOfZetaInTransit() (sdkmath.Int, error) { +func (zs *ZetaSupplyChecker) GetAmountOfZetaInTransit(ctx context.Context) (sdkmath.Int, error) { chainsToCheck := make([]chains.Chain, len(zs.externalEvmChain)+1) chainsToCheck = append(append(chainsToCheck, zs.externalEvmChain...), zs.ethereumChain) - cctxs := zs.GetPendingCCTXInTransit(chainsToCheck) + cctxs := zs.GetPendingCCTXInTransit(ctx, chainsToCheck) amount := sdkmath.ZeroUint() for _, cctx := range cctxs { @@ -233,10 +244,13 @@ func (zs *ZetaSupplyChecker) GetAmountOfZetaInTransit() (sdkmath.Int, error) { } // GetPendingCCTXInTransit returns the pending CCTX in transit -func (zs *ZetaSupplyChecker) GetPendingCCTXInTransit(receivingChains []chains.Chain) []*types.CrossChainTx { +func (zs *ZetaSupplyChecker) GetPendingCCTXInTransit( + ctx context.Context, + receivingChains []chains.Chain, +) []*types.CrossChainTx { cctxInTransit := make([]*types.CrossChainTx, 0) for _, chain := range receivingChains { - cctx, _, err := zs.zetaClient.ListPendingCctx(chain.ChainId) + cctx, _, err := zs.zetaClient.ListPendingCCTX(ctx, chain.ChainId) if err != nil { continue } @@ -247,7 +261,7 @@ func (zs *ZetaSupplyChecker) GetPendingCCTXInTransit(receivingChains []chains.Ch } } - trackers, err := zs.zetaClient.GetAllOutboundTrackerByChain(chain.ChainId, interfaces.Ascending) + trackers, err := zs.zetaClient.GetAllOutboundTrackerByChain(ctx, chain.ChainId, interfaces.Ascending) if err != nil { continue } diff --git a/zetaclient/testutils/mocks/chain_clients.go b/zetaclient/testutils/mocks/chain_clients.go index 44f1a9ea71..6f004420e5 100644 --- a/zetaclient/testutils/mocks/chain_clients.go +++ b/zetaclient/testutils/mocks/chain_clients.go @@ -1,6 +1,8 @@ package mocks import ( + "context" + "github.com/rs/zerolog" crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" @@ -24,13 +26,14 @@ func NewEVMObserver(chainParams *observertypes.ChainParams) *EVMObserver { } } -func (ob *EVMObserver) Start() { -} - -func (ob *EVMObserver) Stop() { -} +func (ob *EVMObserver) Start(_ context.Context) {} +func (ob *EVMObserver) Stop() {} -func (ob *EVMObserver) IsOutboundProcessed(_ *crosschaintypes.CrossChainTx, _ zerolog.Logger) (bool, bool, error) { +func (ob *EVMObserver) IsOutboundProcessed( + _ context.Context, + _ *crosschaintypes.CrossChainTx, + _ zerolog.Logger, +) (bool, bool, error) { return false, false, nil } @@ -46,7 +49,8 @@ func (ob *EVMObserver) GetTxID(_ uint64) string { return "" } -func (ob *EVMObserver) WatchInboundTracker() { +func (ob *EVMObserver) WatchInboundTracker(_ context.Context) error { + return nil } // ---------------------------------------------------------------------------- @@ -65,13 +69,15 @@ func NewBTCObserver(chainParams *observertypes.ChainParams) *BTCObserver { } } -func (ob *BTCObserver) Start() { -} +func (ob *BTCObserver) Start(_ context.Context) {} -func (ob *BTCObserver) Stop() { -} +func (ob *BTCObserver) Stop() {} -func (ob *BTCObserver) IsOutboundProcessed(_ *crosschaintypes.CrossChainTx, _ zerolog.Logger) (bool, bool, error) { +func (ob *BTCObserver) IsOutboundProcessed( + _ context.Context, + _ *crosschaintypes.CrossChainTx, + _ zerolog.Logger, +) (bool, bool, error) { return false, false, nil } @@ -87,5 +93,4 @@ func (ob *BTCObserver) GetTxID(_ uint64) string { return "" } -func (ob *BTCObserver) WatchInboundTracker() { -} +func (ob *BTCObserver) WatchInboundTracker(_ context.Context) error { return nil } diff --git a/zetaclient/testutils/mocks/chain_signer.go b/zetaclient/testutils/mocks/chain_signer.go index 73135b387f..3785c34ee0 100644 --- a/zetaclient/testutils/mocks/chain_signer.go +++ b/zetaclient/testutils/mocks/chain_signer.go @@ -1,6 +1,8 @@ package mocks import ( + "context" + ethcommon "github.com/ethereum/go-ethereum/common" "github.com/zeta-chain/zetacore/pkg/chains" @@ -34,6 +36,7 @@ func NewEVMSigner( } func (s *EVMSigner) TryProcessOutbound( + _ context.Context, _ *crosschaintypes.CrossChainTx, _ *outboundprocessor.Processor, _ string, @@ -73,6 +76,7 @@ func NewBTCSigner() *BTCSigner { } func (s *BTCSigner) TryProcessOutbound( + _ context.Context, _ *crosschaintypes.CrossChainTx, _ *outboundprocessor.Processor, _ string, diff --git a/zetaclient/testutils/mocks/cometbft_client.go b/zetaclient/testutils/mocks/cometbft_client.go index 1058e7980e..dcb452621d 100644 --- a/zetaclient/testutils/mocks/cometbft_client.go +++ b/zetaclient/testutils/mocks/cometbft_client.go @@ -2,29 +2,38 @@ package mocks import ( "context" + "encoding/hex" + "testing" abci "github.com/cometbft/cometbft/abci/types" "github.com/cometbft/cometbft/libs/bytes" "github.com/cometbft/cometbft/rpc/client/mock" coretypes "github.com/cometbft/cometbft/rpc/core/types" tmtypes "github.com/cometbft/cometbft/types" + "github.com/stretchr/testify/require" ) type CometBFTClient struct { mock.Client - err error - code uint32 + + t *testing.T + err error + code uint32 + txHash bytes.HexBytes } -func (c CometBFTClient) BroadcastTxCommit(_ context.Context, _ tmtypes.Tx) (*coretypes.ResultBroadcastTxCommit, error) { +func (c *CometBFTClient) BroadcastTxCommit( + _ context.Context, + _ tmtypes.Tx, +) (*coretypes.ResultBroadcastTxCommit, error) { return nil, c.err } -func (c CometBFTClient) BroadcastTxAsync(_ context.Context, _ tmtypes.Tx) (*coretypes.ResultBroadcastTx, error) { +func (c *CometBFTClient) BroadcastTxAsync(_ context.Context, _ tmtypes.Tx) (*coretypes.ResultBroadcastTx, error) { return nil, c.err } -func (c CometBFTClient) BroadcastTxSync(_ context.Context, _ tmtypes.Tx) (*coretypes.ResultBroadcastTx, error) { +func (c *CometBFTClient) BroadcastTxSync(_ context.Context, _ tmtypes.Tx) (*coretypes.ResultBroadcastTx, error) { log := "" if c.err != nil { log = c.err.Error() @@ -34,11 +43,11 @@ func (c CometBFTClient) BroadcastTxSync(_ context.Context, _ tmtypes.Tx) (*coret Data: bytes.HexBytes{}, Log: log, Codespace: "", - Hash: bytes.HexBytes{}, + Hash: c.txHash, }, c.err } -func (c CometBFTClient) Tx(_ context.Context, _ []byte, _ bool) (*coretypes.ResultTx, error) { +func (c *CometBFTClient) Tx(_ context.Context, _ []byte, _ bool) (*coretypes.ResultTx, error) { return &coretypes.ResultTx{ Hash: bytes.HexBytes{}, Height: 0, @@ -51,7 +60,7 @@ func (c CometBFTClient) Tx(_ context.Context, _ []byte, _ bool) (*coretypes.Resu }, c.err } -func (c CometBFTClient) Block(_ context.Context, _ *int64) (*coretypes.ResultBlock, error) { +func (c *CometBFTClient) Block(_ context.Context, _ *int64) (*coretypes.ResultBlock, error) { return &coretypes.ResultBlock{Block: &tmtypes.Block{ Header: tmtypes.Header{}, Data: tmtypes.Data{}, @@ -59,8 +68,23 @@ func (c CometBFTClient) Block(_ context.Context, _ *int64) (*coretypes.ResultBlo }}, c.err } -func NewSDKClientWithErr(err error, code uint32) *CometBFTClient { +func (c *CometBFTClient) SetBroadcastTxHash(hash string) *CometBFTClient { + b, err := hex.DecodeString(hash) + require.NoError(c.t, err) + + c.txHash = b + + return c +} + +func (c *CometBFTClient) SetError(err error) *CometBFTClient { + c.err = err + return c +} + +func NewSDKClientWithErr(t *testing.T, err error, code uint32) *CometBFTClient { return &CometBFTClient{ + t: t, Client: mock.Client{}, err: err, code: code, diff --git a/zetaclient/testutils/mocks/tss_signer.go b/zetaclient/testutils/mocks/tss_signer.go index ea439e23b6..a7a9690293 100644 --- a/zetaclient/testutils/mocks/tss_signer.go +++ b/zetaclient/testutils/mocks/tss_signer.go @@ -1,6 +1,7 @@ package mocks import ( + "context" "crypto/ecdsa" "fmt" @@ -67,7 +68,7 @@ func (s *TSS) WithPrivKey(privKey *ecdsa.PrivateKey) *TSS { } // Sign uses test key unrelated to any tss key in production -func (s *TSS) Sign(data []byte, _ uint64, _ uint64, _ int64, _ string) ([65]byte, error) { +func (s *TSS) Sign(_ context.Context, data []byte, _ uint64, _ uint64, _ int64, _ string) ([65]byte, error) { // return error if tss is paused if s.paused { return [65]byte{}, fmt.Errorf("tss is paused") @@ -84,7 +85,7 @@ func (s *TSS) Sign(data []byte, _ uint64, _ uint64, _ int64, _ string) ([65]byte } // SignBatch uses test key unrelated to any tss key in production -func (s *TSS) SignBatch(_ [][]byte, _ uint64, _ uint64, _ int64) ([][65]byte, error) { +func (s *TSS) SignBatch(_ context.Context, _ [][]byte, _ uint64, _ uint64, _ int64) ([][65]byte, error) { // return error if tss is paused if s.paused { return nil, fmt.Errorf("tss is paused") diff --git a/zetaclient/testutils/mocks/zetacore_client.go b/zetaclient/testutils/mocks/zetacore_client.go index 2f413a8764..0dc6b8996d 100644 --- a/zetaclient/testutils/mocks/zetacore_client.go +++ b/zetaclient/testutils/mocks/zetacore_client.go @@ -1,280 +1,772 @@ +// Code generated by mockery v2.38.0. DO NOT EDIT. + package mocks import ( - "errors" - "math/big" - - "cosmossdk.io/math" - "github.com/rs/zerolog" - "github.com/zeta-chain/go-tss/blame" - - "github.com/zeta-chain/zetacore/pkg/chains" - "github.com/zeta-chain/zetacore/pkg/coin" - "github.com/zeta-chain/zetacore/pkg/proofs" - "github.com/zeta-chain/zetacore/testutil/sample" - crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" + blame "github.com/zeta-chain/go-tss/blame" + chains "github.com/zeta-chain/zetacore/pkg/chains" + + context "context" + + interfaces "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" + + keysinterfaces "github.com/zeta-chain/zetacore/zetaclient/keys/interfaces" + lightclienttypes "github.com/zeta-chain/zetacore/x/lightclient/types" - observerTypes "github.com/zeta-chain/zetacore/x/observer/types" - chaininterfaces "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" - keyinterfaces "github.com/zeta-chain/zetacore/zetaclient/keys/interfaces" - "github.com/zeta-chain/zetacore/zetaclient/testutils" -) -const ErrMsgPaused = "zetacore client is paused" -const ErrMsgRPCFailed = "rpc failed" + math "cosmossdk.io/math" -var _ chaininterfaces.ZetacoreClient = &MockZetacoreClient{} + mock "github.com/stretchr/testify/mock" -type MockZetacoreClient struct { - paused bool - zetaChain chains.Chain + observertypes "github.com/zeta-chain/zetacore/x/observer/types" - // the mock observer keys - keys keyinterfaces.ObserverKeys + proofs "github.com/zeta-chain/zetacore/pkg/proofs" - // the mock data for testing - // pending cctxs - pendingCctxs map[int64][]*crosschaintypes.CrossChainTx + types "github.com/zeta-chain/zetacore/x/crosschain/types" - // rate limiter flags - rateLimiterFlags *crosschaintypes.RateLimiterFlags + zerolog "github.com/rs/zerolog" +) - // rate limiter input - input *crosschaintypes.QueryRateLimiterInputResponse +// ZetacoreClient is an autogenerated mock type for the ZetacoreClient type +type ZetacoreClient struct { + mock.Mock } -func NewMockZetacoreClient() *MockZetacoreClient { - return &MockZetacoreClient{ - paused: false, - zetaChain: chains.ZetaChainMainnet, - pendingCctxs: map[int64][]*crosschaintypes.CrossChainTx{}, +// AddOutboundTracker provides a mock function with given fields: ctx, chainID, nonce, txHash, proof, blockHash, txIndex +func (_m *ZetacoreClient) AddOutboundTracker(ctx context.Context, chainID int64, nonce uint64, txHash string, proof *proofs.Proof, blockHash string, txIndex int64) (string, error) { + ret := _m.Called(ctx, chainID, nonce, txHash, proof, blockHash, txIndex) + + if len(ret) == 0 { + panic("no return value specified for AddOutboundTracker") } -} -func (m *MockZetacoreClient) PostVoteInbound(_, _ uint64, _ *crosschaintypes.MsgVoteInbound) (string, string, error) { - if m.paused { - return "", "", errors.New(ErrMsgPaused) + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64, uint64, string, *proofs.Proof, string, int64) (string, error)); ok { + return rf(ctx, chainID, nonce, txHash, proof, blockHash, txIndex) + } + if rf, ok := ret.Get(0).(func(context.Context, int64, uint64, string, *proofs.Proof, string, int64) string); ok { + r0 = rf(ctx, chainID, nonce, txHash, proof, blockHash, txIndex) + } else { + r0 = ret.Get(0).(string) } - return "", "", nil -} -func (m *MockZetacoreClient) PostVoteOutbound( - _ string, - _ string, - _ uint64, - _ uint64, - _ *big.Int, - _ uint64, - _ *big.Int, - _ chains.ReceiveStatus, - _ chains.Chain, - _ uint64, - _ coin.CoinType, -) (string, string, error) { - if m.paused { - return "", "", errors.New(ErrMsgPaused) - } - return sample.Hash().Hex(), "", nil + if rf, ok := ret.Get(1).(func(context.Context, int64, uint64, string, *proofs.Proof, string, int64) error); ok { + r1 = rf(ctx, chainID, nonce, txHash, proof, blockHash, txIndex) + } else { + r1 = ret.Error(1) + } + + return r0, r1 } -func (m *MockZetacoreClient) PostGasPrice(_ chains.Chain, _ uint64, _ string, _ uint64) (string, error) { - if m.paused { - return "", errors.New(ErrMsgPaused) +// Chain provides a mock function with given fields: +func (_m *ZetacoreClient) Chain() chains.Chain { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Chain") } - return "", nil -} -func (m *MockZetacoreClient) PostVoteBlockHeader(_ int64, _ []byte, _ int64, _ proofs.HeaderData) (string, error) { - if m.paused { - return "", errors.New(ErrMsgPaused) + var r0 chains.Chain + if rf, ok := ret.Get(0).(func() chains.Chain); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(chains.Chain) } - return "", nil + + return r0 } -func (m *MockZetacoreClient) GetBlockHeaderChainState(_ int64) (lightclienttypes.QueryGetChainStateResponse, error) { - if m.paused { - return lightclienttypes.QueryGetChainStateResponse{}, errors.New(ErrMsgPaused) +// GetAllOutboundTrackerByChain provides a mock function with given fields: ctx, chainID, order +func (_m *ZetacoreClient) GetAllOutboundTrackerByChain(ctx context.Context, chainID int64, order interfaces.Order) ([]types.OutboundTracker, error) { + ret := _m.Called(ctx, chainID, order) + + if len(ret) == 0 { + panic("no return value specified for GetAllOutboundTrackerByChain") } - return lightclienttypes.QueryGetChainStateResponse{}, nil -} -func (m *MockZetacoreClient) PostBlameData(_ *blame.Blame, _ int64, _ string) (string, error) { - if m.paused { - return "", errors.New(ErrMsgPaused) + var r0 []types.OutboundTracker + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64, interfaces.Order) ([]types.OutboundTracker, error)); ok { + return rf(ctx, chainID, order) + } + if rf, ok := ret.Get(0).(func(context.Context, int64, interfaces.Order) []types.OutboundTracker); ok { + r0 = rf(ctx, chainID, order) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]types.OutboundTracker) + } } - return "", nil -} -func (m *MockZetacoreClient) AddOutboundTracker( - _ int64, - _ uint64, - _ string, - _ *proofs.Proof, - _ string, - _ int64, -) (string, error) { - if m.paused { - return "", errors.New(ErrMsgPaused) - } - return "", nil + if rf, ok := ret.Get(1).(func(context.Context, int64, interfaces.Order) error); ok { + r1 = rf(ctx, chainID, order) + } else { + r1 = ret.Error(1) + } + + return r0, r1 } -func (m *MockZetacoreClient) Chain() chains.Chain { - return m.zetaChain +// GetBTCTSSAddress provides a mock function with given fields: ctx, chainID +func (_m *ZetacoreClient) GetBTCTSSAddress(ctx context.Context, chainID int64) (string, error) { + ret := _m.Called(ctx, chainID) + + if len(ret) == 0 { + panic("no return value specified for GetBTCTSSAddress") + } + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64) (string, error)); ok { + return rf(ctx, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, int64) string); ok { + r0 = rf(ctx, chainID) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok { + r1 = rf(ctx, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 } -func (m *MockZetacoreClient) GetLogger() *zerolog.Logger { - return nil +// GetBlockHeaderChainState provides a mock function with given fields: ctx, chainID +func (_m *ZetacoreClient) GetBlockHeaderChainState(ctx context.Context, chainID int64) (*lightclienttypes.ChainState, error) { + ret := _m.Called(ctx, chainID) + + if len(ret) == 0 { + panic("no return value specified for GetBlockHeaderChainState") + } + + var r0 *lightclienttypes.ChainState + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64) (*lightclienttypes.ChainState, error)); ok { + return rf(ctx, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, int64) *lightclienttypes.ChainState); ok { + r0 = rf(ctx, chainID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*lightclienttypes.ChainState) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok { + r1 = rf(ctx, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 } -func (m *MockZetacoreClient) GetKeys() keyinterfaces.ObserverKeys { - return m.keys +// GetBlockHeight provides a mock function with given fields: ctx +func (_m *ZetacoreClient) GetBlockHeight(ctx context.Context) (int64, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetBlockHeight") + } + + var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (int64, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) int64); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(int64) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 } -func (m *MockZetacoreClient) GetKeyGen() (*observerTypes.Keygen, error) { - if m.paused { - return nil, errors.New(ErrMsgPaused) +// GetCctxByNonce provides a mock function with given fields: ctx, chainID, nonce +func (_m *ZetacoreClient) GetCctxByNonce(ctx context.Context, chainID int64, nonce uint64) (*types.CrossChainTx, error) { + ret := _m.Called(ctx, chainID, nonce) + + if len(ret) == 0 { + panic("no return value specified for GetCctxByNonce") + } + + var r0 *types.CrossChainTx + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64, uint64) (*types.CrossChainTx, error)); ok { + return rf(ctx, chainID, nonce) + } + if rf, ok := ret.Get(0).(func(context.Context, int64, uint64) *types.CrossChainTx); ok { + r0 = rf(ctx, chainID, nonce) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.CrossChainTx) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, int64, uint64) error); ok { + r1 = rf(ctx, chainID, nonce) + } else { + r1 = ret.Error(1) } - return &observerTypes.Keygen{}, nil + + return r0, r1 } -func (m *MockZetacoreClient) GetBlockHeight() (int64, error) { - if m.paused { - return 0, errors.New(ErrMsgPaused) +// GetCrosschainFlags provides a mock function with given fields: ctx +func (_m *ZetacoreClient) GetCrosschainFlags(ctx context.Context) (observertypes.CrosschainFlags, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetCrosschainFlags") + } + + var r0 observertypes.CrosschainFlags + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (observertypes.CrosschainFlags, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) observertypes.CrosschainFlags); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(observertypes.CrosschainFlags) } - return 0, nil + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 } -func (m *MockZetacoreClient) GetRateLimiterInput(_ int64) (crosschaintypes.QueryRateLimiterInputResponse, error) { - if m.paused { - return crosschaintypes.QueryRateLimiterInputResponse{}, errors.New(ErrMsgPaused) +// GetInboundTrackersForChain provides a mock function with given fields: ctx, chainID +func (_m *ZetacoreClient) GetInboundTrackersForChain(ctx context.Context, chainID int64) ([]types.InboundTracker, error) { + ret := _m.Called(ctx, chainID) + + if len(ret) == 0 { + panic("no return value specified for GetInboundTrackersForChain") } - if m.input == nil { - return crosschaintypes.QueryRateLimiterInputResponse{}, errors.New(ErrMsgRPCFailed) + + var r0 []types.InboundTracker + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64) ([]types.InboundTracker, error)); ok { + return rf(ctx, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, int64) []types.InboundTracker); ok { + r0 = rf(ctx, chainID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]types.InboundTracker) + } } - return *m.input, nil + + if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok { + r1 = rf(ctx, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 } -func (m *MockZetacoreClient) ListPendingCctx(chainID int64) ([]*crosschaintypes.CrossChainTx, uint64, error) { - if m.paused { - return nil, 0, errors.New(ErrMsgPaused) +// GetKeyGen provides a mock function with given fields: ctx +func (_m *ZetacoreClient) GetKeyGen(ctx context.Context) (*observertypes.Keygen, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetKeyGen") + } + + var r0 *observertypes.Keygen + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*observertypes.Keygen, error)); ok { + return rf(ctx) } - return m.pendingCctxs[chainID], 0, nil + if rf, ok := ret.Get(0).(func(context.Context) *observertypes.Keygen); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*observertypes.Keygen) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 } -func (m *MockZetacoreClient) ListPendingCctxWithinRatelimit() ([]*crosschaintypes.CrossChainTx, uint64, int64, string, bool, error) { - if m.paused { - return nil, 0, 0, "", false, errors.New(ErrMsgPaused) +// GetKeys provides a mock function with given fields: +func (_m *ZetacoreClient) GetKeys() keysinterfaces.ObserverKeys { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetKeys") + } + + var r0 keysinterfaces.ObserverKeys + if rf, ok := ret.Get(0).(func() keysinterfaces.ObserverKeys); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(keysinterfaces.ObserverKeys) + } } - return []*crosschaintypes.CrossChainTx{}, 0, 0, "", false, nil + + return r0 } -func (m *MockZetacoreClient) GetPendingNoncesByChain(_ int64) (observerTypes.PendingNonces, error) { - if m.paused { - return observerTypes.PendingNonces{}, errors.New(ErrMsgPaused) +// GetLogger provides a mock function with given fields: +func (_m *ZetacoreClient) GetLogger() *zerolog.Logger { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetLogger") } - return observerTypes.PendingNonces{}, nil + + var r0 *zerolog.Logger + if rf, ok := ret.Get(0).(func() *zerolog.Logger); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*zerolog.Logger) + } + } + + return r0 } -func (m *MockZetacoreClient) GetCctxByNonce(_ int64, _ uint64) (*crosschaintypes.CrossChainTx, error) { - if m.paused { - return nil, errors.New(ErrMsgPaused) +// GetObserverList provides a mock function with given fields: ctx +func (_m *ZetacoreClient) GetObserverList(ctx context.Context) ([]string, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetObserverList") + } + + var r0 []string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) ([]string, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) []string); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]string) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) } - return &crosschaintypes.CrossChainTx{}, nil + + return r0, r1 } -func (m *MockZetacoreClient) GetOutboundTracker(_ chains.Chain, _ uint64) (*crosschaintypes.OutboundTracker, error) { - if m.paused { - return nil, errors.New(ErrMsgPaused) +// GetOutboundTracker provides a mock function with given fields: ctx, chain, nonce +func (_m *ZetacoreClient) GetOutboundTracker(ctx context.Context, chain chains.Chain, nonce uint64) (*types.OutboundTracker, error) { + ret := _m.Called(ctx, chain, nonce) + + if len(ret) == 0 { + panic("no return value specified for GetOutboundTracker") + } + + var r0 *types.OutboundTracker + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, chains.Chain, uint64) (*types.OutboundTracker, error)); ok { + return rf(ctx, chain, nonce) + } + if rf, ok := ret.Get(0).(func(context.Context, chains.Chain, uint64) *types.OutboundTracker); ok { + r0 = rf(ctx, chain, nonce) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.OutboundTracker) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, chains.Chain, uint64) error); ok { + r1 = rf(ctx, chain, nonce) + } else { + r1 = ret.Error(1) } - return &crosschaintypes.OutboundTracker{}, nil + + return r0, r1 } -func (m *MockZetacoreClient) GetAllOutboundTrackerByChain( - _ int64, - _ chaininterfaces.Order, -) ([]crosschaintypes.OutboundTracker, error) { - if m.paused { - return nil, errors.New(ErrMsgPaused) +// GetPendingNoncesByChain provides a mock function with given fields: ctx, chainID +func (_m *ZetacoreClient) GetPendingNoncesByChain(ctx context.Context, chainID int64) (observertypes.PendingNonces, error) { + ret := _m.Called(ctx, chainID) + + if len(ret) == 0 { + panic("no return value specified for GetPendingNoncesByChain") + } + + var r0 observertypes.PendingNonces + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64) (observertypes.PendingNonces, error)); ok { + return rf(ctx, chainID) } - return []crosschaintypes.OutboundTracker{}, nil + if rf, ok := ret.Get(0).(func(context.Context, int64) observertypes.PendingNonces); ok { + r0 = rf(ctx, chainID) + } else { + r0 = ret.Get(0).(observertypes.PendingNonces) + } + + if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok { + r1 = rf(ctx, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 } -func (m *MockZetacoreClient) GetCrosschainFlags() (observerTypes.CrosschainFlags, error) { - if m.paused { - return observerTypes.CrosschainFlags{}, errors.New(ErrMsgPaused) +// GetRateLimiterFlags provides a mock function with given fields: ctx +func (_m *ZetacoreClient) GetRateLimiterFlags(ctx context.Context) (types.RateLimiterFlags, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetRateLimiterFlags") } - return observerTypes.CrosschainFlags{}, nil + + var r0 types.RateLimiterFlags + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (types.RateLimiterFlags, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) types.RateLimiterFlags); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(types.RateLimiterFlags) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 } -func (m *MockZetacoreClient) GetRateLimiterFlags() (crosschaintypes.RateLimiterFlags, error) { - if m.paused { - return crosschaintypes.RateLimiterFlags{}, errors.New(ErrMsgPaused) +// GetRateLimiterInput provides a mock function with given fields: ctx, window +func (_m *ZetacoreClient) GetRateLimiterInput(ctx context.Context, window int64) (*types.QueryRateLimiterInputResponse, error) { + ret := _m.Called(ctx, window) + + if len(ret) == 0 { + panic("no return value specified for GetRateLimiterInput") } - if m.rateLimiterFlags == nil { - return crosschaintypes.RateLimiterFlags{}, errors.New(ErrMsgRPCFailed) + + var r0 *types.QueryRateLimiterInputResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64) (*types.QueryRateLimiterInputResponse, error)); ok { + return rf(ctx, window) + } + if rf, ok := ret.Get(0).(func(context.Context, int64) *types.QueryRateLimiterInputResponse); ok { + r0 = rf(ctx, window) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.QueryRateLimiterInputResponse) + } } - return *m.rateLimiterFlags, nil + + if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok { + r1 = rf(ctx, window) + } else { + r1 = ret.Error(1) + } + + return r0, r1 } -func (m *MockZetacoreClient) GetObserverList() ([]string, error) { - if m.paused { - return nil, errors.New(ErrMsgPaused) +// GetZetaHotKeyBalance provides a mock function with given fields: ctx +func (_m *ZetacoreClient) GetZetaHotKeyBalance(ctx context.Context) (math.Int, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetZetaHotKeyBalance") + } + + var r0 math.Int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (math.Int, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) math.Int); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(math.Int) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) } - return []string{}, nil + + return r0, r1 } -func (m *MockZetacoreClient) GetBtcTssAddress(_ int64) (string, error) { - if m.paused { - return "", errors.New(ErrMsgPaused) +// ListPendingCCTX provides a mock function with given fields: ctx, chainID +func (_m *ZetacoreClient) ListPendingCCTX(ctx context.Context, chainID int64) ([]*types.CrossChainTx, uint64, error) { + ret := _m.Called(ctx, chainID) + + if len(ret) == 0 { + panic("no return value specified for ListPendingCCTX") + } + + var r0 []*types.CrossChainTx + var r1 uint64 + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, int64) ([]*types.CrossChainTx, uint64, error)); ok { + return rf(ctx, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, int64) []*types.CrossChainTx); ok { + r0 = rf(ctx, chainID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*types.CrossChainTx) + } } - return testutils.TSSAddressBTCMainnet, nil + + if rf, ok := ret.Get(1).(func(context.Context, int64) uint64); ok { + r1 = rf(ctx, chainID) + } else { + r1 = ret.Get(1).(uint64) + } + + if rf, ok := ret.Get(2).(func(context.Context, int64) error); ok { + r2 = rf(ctx, chainID) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 } -func (m *MockZetacoreClient) GetInboundTrackersForChain(_ int64) ([]crosschaintypes.InboundTracker, error) { - if m.paused { - return nil, errors.New(ErrMsgPaused) +// ListPendingCCTXWithinRateLimit provides a mock function with given fields: ctx +func (_m *ZetacoreClient) ListPendingCCTXWithinRateLimit(ctx context.Context) (*types.QueryListPendingCctxWithinRateLimitResponse, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for ListPendingCCTXWithinRateLimit") } - return []crosschaintypes.InboundTracker{}, nil + + var r0 *types.QueryListPendingCctxWithinRateLimitResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*types.QueryListPendingCctxWithinRateLimitResponse, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) *types.QueryListPendingCctxWithinRateLimitResponse); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.QueryListPendingCctxWithinRateLimitResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 } -func (m *MockZetacoreClient) Pause() { - m.paused = true +// OnBeforeStop provides a mock function with given fields: callback +func (_m *ZetacoreClient) OnBeforeStop(callback func()) { + _m.Called(callback) } -func (m *MockZetacoreClient) Unpause() { - m.paused = false +// PostVoteBlameData provides a mock function with given fields: ctx, _a1, chainID, index +func (_m *ZetacoreClient) PostVoteBlameData(ctx context.Context, _a1 *blame.Blame, chainID int64, index string) (string, error) { + ret := _m.Called(ctx, _a1, chainID, index) + + if len(ret) == 0 { + panic("no return value specified for PostVoteBlameData") + } + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *blame.Blame, int64, string) (string, error)); ok { + return rf(ctx, _a1, chainID, index) + } + if rf, ok := ret.Get(0).(func(context.Context, *blame.Blame, int64, string) string); ok { + r0 = rf(ctx, _a1, chainID, index) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(context.Context, *blame.Blame, int64, string) error); ok { + r1 = rf(ctx, _a1, chainID, index) + } else { + r1 = ret.Error(1) + } + + return r0, r1 } -func (m *MockZetacoreClient) GetZetaHotKeyBalance() (math.Int, error) { - if m.paused { - return math.NewInt(0), errors.New(ErrMsgPaused) +// PostVoteBlockHeader provides a mock function with given fields: ctx, chainID, txhash, height, header +func (_m *ZetacoreClient) PostVoteBlockHeader(ctx context.Context, chainID int64, txhash []byte, height int64, header proofs.HeaderData) (string, error) { + ret := _m.Called(ctx, chainID, txhash, height, header) + + if len(ret) == 0 { + panic("no return value specified for PostVoteBlockHeader") + } + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64, []byte, int64, proofs.HeaderData) (string, error)); ok { + return rf(ctx, chainID, txhash, height, header) + } + if rf, ok := ret.Get(0).(func(context.Context, int64, []byte, int64, proofs.HeaderData) string); ok { + r0 = rf(ctx, chainID, txhash, height, header) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(context.Context, int64, []byte, int64, proofs.HeaderData) error); ok { + r1 = rf(ctx, chainID, txhash, height, header) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PostVoteGasPrice provides a mock function with given fields: ctx, chain, gasPrice, supply, blockNum +func (_m *ZetacoreClient) PostVoteGasPrice(ctx context.Context, chain chains.Chain, gasPrice uint64, supply string, blockNum uint64) (string, error) { + ret := _m.Called(ctx, chain, gasPrice, supply, blockNum) + + if len(ret) == 0 { + panic("no return value specified for PostVoteGasPrice") + } + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, chains.Chain, uint64, string, uint64) (string, error)); ok { + return rf(ctx, chain, gasPrice, supply, blockNum) + } + if rf, ok := ret.Get(0).(func(context.Context, chains.Chain, uint64, string, uint64) string); ok { + r0 = rf(ctx, chain, gasPrice, supply, blockNum) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(context.Context, chains.Chain, uint64, string, uint64) error); ok { + r1 = rf(ctx, chain, gasPrice, supply, blockNum) + } else { + r1 = ret.Error(1) } - return math.NewInt(0), nil + + return r0, r1 } -// ---------------------------------------------------------------------------- -// Feed data to the mock zetacore client for testing -// ---------------------------------------------------------------------------- +// PostVoteInbound provides a mock function with given fields: ctx, gasLimit, retryGasLimit, msg +func (_m *ZetacoreClient) PostVoteInbound(ctx context.Context, gasLimit uint64, retryGasLimit uint64, msg *types.MsgVoteInbound) (string, string, error) { + ret := _m.Called(ctx, gasLimit, retryGasLimit, msg) + + if len(ret) == 0 { + panic("no return value specified for PostVoteInbound") + } + + var r0 string + var r1 string + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64, *types.MsgVoteInbound) (string, string, error)); ok { + return rf(ctx, gasLimit, retryGasLimit, msg) + } + if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64, *types.MsgVoteInbound) string); ok { + r0 = rf(ctx, gasLimit, retryGasLimit, msg) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(context.Context, uint64, uint64, *types.MsgVoteInbound) string); ok { + r1 = rf(ctx, gasLimit, retryGasLimit, msg) + } else { + r1 = ret.Get(1).(string) + } + + if rf, ok := ret.Get(2).(func(context.Context, uint64, uint64, *types.MsgVoteInbound) error); ok { + r2 = rf(ctx, gasLimit, retryGasLimit, msg) + } else { + r2 = ret.Error(2) + } -func (m *MockZetacoreClient) WithKeys(keys keyinterfaces.ObserverKeys) *MockZetacoreClient { - m.keys = keys - return m + return r0, r1, r2 } -func (m *MockZetacoreClient) WithPendingCctx(chainID int64, cctxs []*crosschaintypes.CrossChainTx) *MockZetacoreClient { - m.pendingCctxs[chainID] = cctxs - return m +// PostVoteOutbound provides a mock function with given fields: ctx, gasLimit, retryGasLimit, msg +func (_m *ZetacoreClient) PostVoteOutbound(ctx context.Context, gasLimit uint64, retryGasLimit uint64, msg *types.MsgVoteOutbound) (string, string, error) { + ret := _m.Called(ctx, gasLimit, retryGasLimit, msg) + + if len(ret) == 0 { + panic("no return value specified for PostVoteOutbound") + } + + var r0 string + var r1 string + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64, *types.MsgVoteOutbound) (string, string, error)); ok { + return rf(ctx, gasLimit, retryGasLimit, msg) + } + if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64, *types.MsgVoteOutbound) string); ok { + r0 = rf(ctx, gasLimit, retryGasLimit, msg) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(context.Context, uint64, uint64, *types.MsgVoteOutbound) string); ok { + r1 = rf(ctx, gasLimit, retryGasLimit, msg) + } else { + r1 = ret.Get(1).(string) + } + + if rf, ok := ret.Get(2).(func(context.Context, uint64, uint64, *types.MsgVoteOutbound) error); ok { + r2 = rf(ctx, gasLimit, retryGasLimit, msg) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 } -func (m *MockZetacoreClient) WithRateLimiterFlags(flags *crosschaintypes.RateLimiterFlags) *MockZetacoreClient { - m.rateLimiterFlags = flags - return m +// Stop provides a mock function with given fields: +func (_m *ZetacoreClient) Stop() { + _m.Called() } -func (m *MockZetacoreClient) WithRateLimiterInput( - input *crosschaintypes.QueryRateLimiterInputResponse, -) *MockZetacoreClient { - m.input = input - return m +// NewZetacoreClient creates a new instance of ZetacoreClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewZetacoreClient(t interface { + mock.TestingT + Cleanup(func()) +}) *ZetacoreClient { + mock := &ZetacoreClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock } diff --git a/zetaclient/testutils/mocks/zetacore_client_opts.go b/zetaclient/testutils/mocks/zetacore_client_opts.go new file mode 100644 index 0000000000..7e1e0c2392 --- /dev/null +++ b/zetaclient/testutils/mocks/zetacore_client_opts.go @@ -0,0 +1,73 @@ +package mocks + +import ( + "errors" + + "github.com/stretchr/testify/mock" + + "github.com/zeta-chain/zetacore/pkg/chains" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" + keyinterfaces "github.com/zeta-chain/zetacore/zetaclient/keys/interfaces" +) + +var errSomethingIsWrong = errors.New("oopsie") + +// Note that this is NOT codegen but a handwritten mock improvement. + +func (_m *ZetacoreClient) WithKeys(keys keyinterfaces.ObserverKeys) *ZetacoreClient { + _m.On("GetKeys").Maybe().Return(keys) + + return _m +} + +func (_m *ZetacoreClient) WithZetaChain() *ZetacoreClient { + _m.On("Chain").Maybe().Return(chains.ZetaChainMainnet) + + return _m +} + +func (_m *ZetacoreClient) WithPostVoteOutbound(zetaTxHash string, ballotIndex string) *ZetacoreClient { + _m.On("PostVoteOutbound", mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Maybe(). + Return(zetaTxHash, ballotIndex, nil) + + return _m +} + +func (_m *ZetacoreClient) WithPostVoteInbound(zetaTxHash string, ballotIndex string) *ZetacoreClient { + _m.On("PostVoteInbound", mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Maybe(). + Return(zetaTxHash, ballotIndex, nil) + + return _m +} + +func (_m *ZetacoreClient) WithRateLimiterFlags(flags *crosschaintypes.RateLimiterFlags) *ZetacoreClient { + on := _m.On("GetRateLimiterFlags", mock.Anything).Maybe() + if flags != nil { + on.Return(*flags, nil) + } else { + on.Return(crosschaintypes.RateLimiterFlags{}, errSomethingIsWrong) + } + + return _m +} + +func (_m *ZetacoreClient) WithRateLimiterInput(in *crosschaintypes.QueryRateLimiterInputResponse) *ZetacoreClient { + on := _m.On("GetRateLimiterInput", mock.Anything, mock.Anything).Maybe() + if in != nil { + on.Return(in, nil) + } else { + on.Return(nil, errSomethingIsWrong) + } + + return _m +} + +func (_m *ZetacoreClient) WithPendingCctx(chainID int64, cctxs []*crosschaintypes.CrossChainTx) *ZetacoreClient { + totalPending := uint64(len(cctxs)) + + _m.On("ListPendingCCTX", mock.Anything, chainID).Maybe().Return(cctxs, totalPending, nil) + + return _m +} diff --git a/zetaclient/tss/tss_signer.go b/zetaclient/tss/tss_signer.go index e00252db55..dc813eb784 100644 --- a/zetaclient/tss/tss_signer.go +++ b/zetaclient/tss/tss_signer.go @@ -3,6 +3,7 @@ package tss import ( "bytes" + "context" "encoding/base64" "encoding/hex" "fmt" @@ -90,6 +91,7 @@ type TSS struct { // NewTSS creates a new TSS instance func NewTSS( + ctx context.Context, appContext *appcontext.AppContext, peer p2p.AddrList, privkey tmcrypto.PrivKey, @@ -131,7 +133,7 @@ func NewTSS( client.GetLogger().Error().Err(err).Msg("VerifyKeysharesForPubkeys fail") } - keygenRes, err := newTss.ZetacoreClient.GetKeyGen() + keygenRes, err := newTss.ZetacoreClient.GetKeyGen(ctx) if err != nil { return nil, err } @@ -222,6 +224,7 @@ func (tss *TSS) Pubkey() []byte { // digest should be Hashes of some data // NOTE: Specify optionalPubkey to use a different pubkey than the current pubkey set during keygen func (tss *TSS) Sign( + ctx context.Context, digest []byte, height uint64, nonce uint64, @@ -236,7 +239,7 @@ func (tss *TSS) Sign( tssPubkey = optionalPubKey } - // #nosec G701 always in range + // #nosec G115 always in range keysignReq := keysign.NewRequest( tssPubkey, []string{base64.StdEncoding.EncodeToString(H)}, @@ -258,7 +261,7 @@ func (tss *TSS) Sign( if IsEnvFlagEnabled(envFlagPostBlame) { digest := hex.EncodeToString(digest) index := observertypes.GetBlameIndex(chainID, nonce, digest, height) - zetaHash, err := tss.ZetacoreClient.PostBlameData(&ksRes.Blame, chainID, index) + zetaHash, err := tss.ZetacoreClient.PostVoteBlameData(ctx, &ksRes.Blame, chainID, index) if err != nil { log.Error().Err(err).Msg("error sending blame data to core") return [65]byte{}, err @@ -311,13 +314,19 @@ func (tss *TSS) Sign( // SignBatch is hash of some data // digest should be batch of hashes of some data -func (tss *TSS) SignBatch(digests [][]byte, height uint64, nonce uint64, chainID int64) ([][65]byte, error) { +func (tss *TSS) SignBatch( + ctx context.Context, + digests [][]byte, + height uint64, + nonce uint64, + chainID int64, +) ([][65]byte, error) { tssPubkey := tss.CurrentPubkey digestBase64 := make([]string, len(digests)) for i, digest := range digests { digestBase64[i] = base64.StdEncoding.EncodeToString(digest) } - // #nosec G701 always in range + // #nosec G115 always in range keysignReq := keysign.NewRequest(tssPubkey, digestBase64, int64(height), nil, "0.14.0") tss.KeysignsTracker.StartMsgSign() @@ -334,7 +343,7 @@ func (tss *TSS) SignBatch(digests [][]byte, height uint64, nonce uint64, chainID if IsEnvFlagEnabled(envFlagPostBlame) { digest := combineDigests(digestBase64) index := observertypes.GetBlameIndex(chainID, nonce, hex.EncodeToString(digest), height) - zetaHash, err := tss.ZetacoreClient.PostBlameData(&ksRes.Blame, chainID, index) + zetaHash, err := tss.ZetacoreClient.PostVoteBlameData(ctx, &ksRes.Blame, chainID, index) if err != nil { log.Error().Err(err).Msg("error sending blame data to core") return [][65]byte{}, err diff --git a/zetaclient/zetacore/broadcast.go b/zetaclient/zetacore/broadcast.go index 6c1b475476..7096f4f1e8 100644 --- a/zetaclient/zetacore/broadcast.go +++ b/zetaclient/zetacore/broadcast.go @@ -1,18 +1,18 @@ package zetacore import ( + "context" "fmt" "regexp" "strconv" "strings" - rpchttp "github.com/cometbft/cometbft/rpc/client/http" + "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/client" clienttx "github.com/cosmos/cosmos-sdk/client/tx" sdktypes "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/tx/signing" authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/rs/zerolog/log" flag "github.com/spf13/pflag" @@ -22,62 +22,49 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/hsm" ) -// BroadcastInterface defines the signature of the broadcast function used by zetacore transactions -type BroadcastInterface = func(client *Client, gaslimit uint64, authzWrappedMsg sdktypes.Msg, authzSigner authz.Signer) (string, error) +// paying 50% more than the current base gas price to buffer for potential block-by-block +// gas price increase due to EIP1559 feemarket on ZetaChain +var bufferMultiplier = sdktypes.MustNewDecFromStr("1.5") -const ( - // DefaultBaseGasPrice is the default base gas price - DefaultBaseGasPrice = 1_000_000 -) - -var ( - // paying 50% more than the current base gas price to buffer for potential block-by-block - // gas price increase due to EIP1559 feemarket on ZetaChain - bufferMultiplier = sdktypes.MustNewDecFromStr("1.5") - - // Variable function used by transactions to broadcast a message to zetacore. This will create enough flexibility - // in the implementation to allow for more comprehensive unit testing. - zetacoreBroadcast BroadcastInterface = BroadcastToZetaCore -) - -// BroadcastToZetaCore is the default broadcast function used to send transactions to zetacore -func BroadcastToZetaCore( - client *Client, +// Broadcast Broadcasts tx to ZetaChain. Returns txHash and error +func (c *Client) Broadcast( + ctx context.Context, gasLimit uint64, authzWrappedMsg sdktypes.Msg, authzSigner authz.Signer, ) (string, error) { - return client.Broadcast(gasLimit, authzWrappedMsg, authzSigner) -} - -// Broadcast Broadcasts tx to ZetaChain. Returns txHash and error -func (c *Client) Broadcast(gaslimit uint64, authzWrappedMsg sdktypes.Msg, authzSigner authz.Signer) (string, error) { - c.broadcastLock.Lock() - defer c.broadcastLock.Unlock() - var err error - - blockHeight, err := c.GetBlockHeight() + blockHeight, err := c.GetBlockHeight(ctx) if err != nil { - return "", err + return "", errors.Wrap(err, "unable to get block height") } - baseGasPrice, err := c.GetBaseGasPrice() + + baseGasPrice, err := c.GetBaseGasPrice(ctx) if err != nil { - return "", err + return "", errors.Wrap(err, "unable to get base gas price") } + + // shouldn't happen, but just in case if baseGasPrice == 0 { - baseGasPrice = DefaultBaseGasPrice // shouldn't happen, but just in case + baseGasPrice = DefaultBaseGasPrice } + reductionRate := sdktypes.MustNewDecFromStr(ante.GasPriceReductionRate) + // multiply gas price by the system tx reduction rate adjustedBaseGasPrice := sdktypes.NewDec(baseGasPrice).Mul(reductionRate).Mul(bufferMultiplier) + c.mu.Lock() + defer c.mu.Unlock() + if blockHeight > c.blockHeight { c.blockHeight = blockHeight accountNumber, seqNumber, err := c.GetAccountNumberAndSequenceNumber(authzSigner.KeyType) if err != nil { return "", err } + c.accountNumber[authzSigner.KeyType] = accountNumber + if c.seqNumber[authzSigner.KeyType] < seqNumber { c.seqNumber[authzSigner.KeyType] = seqNumber } @@ -85,11 +72,7 @@ func (c *Client) Broadcast(gaslimit uint64, authzWrappedMsg sdktypes.Msg, authzS flags := flag.NewFlagSet("zetaclient", 0) - ctx, err := c.GetContext() - if err != nil { - return "", err - } - factory, err := clienttx.NewFactoryCLI(ctx, flags) + factory, err := clienttx.NewFactoryCLI(c.cosmosClientContext, flags) if err != nil { return "", err } @@ -99,29 +82,32 @@ func (c *Client) Broadcast(gaslimit uint64, authzWrappedMsg sdktypes.Msg, authzS factory = factory.WithSignMode(signing.SignMode_SIGN_MODE_DIRECT) builder, err := factory.BuildUnsignedTx(authzWrappedMsg) if err != nil { - return "", err + return "", errors.Wrap(err, "unable to build unsigned tx") } - builder.SetGasLimit(gaslimit) + builder.SetGasLimit(gasLimit) - // #nosec G701 always in range - fee := sdktypes.NewCoins(sdktypes.NewCoin(config.BaseDenom, - sdktypes.NewInt(int64(gaslimit)).Mul(adjustedBaseGasPrice.Ceil().RoundInt()))) + // #nosec G115 always in range + fee := sdktypes.NewCoins(sdktypes.NewCoin( + config.BaseDenom, + sdktypes.NewInt(int64(gasLimit)).Mul(adjustedBaseGasPrice.Ceil().RoundInt()), + )) builder.SetFeeAmount(fee) - err = c.SignTx(factory, ctx.GetFromName(), builder, true, ctx.TxConfig) + + err = c.SignTx(factory, c.cosmosClientContext.GetFromName(), builder, true, c.cosmosClientContext.TxConfig) if err != nil { - return "", err + return "", errors.Wrap(err, "unable to sign tx") } - txBytes, err := ctx.TxConfig.TxEncoder()(builder.GetTx()) + + txBytes, err := c.cosmosClientContext.TxConfig.TxEncoder()(builder.GetTx()) if err != nil { - return "", err + return "", errors.Wrap(err, "unable to encode tx") } // broadcast to a Tendermint node - commit, err := ctx.BroadcastTxSync(txBytes) + commit, err := c.cosmosClientContext.BroadcastTxSync(txBytes) if err != nil { - c.logger.Error().Err(err).Msgf("fail to broadcast tx %s", err.Error()) - return "", err + return "", errors.Wrap(err, "fail to broadcast tx sync") } // Code will be the tendermint ABICode , it start at 1 , so if it is an error , code will not be zero @@ -156,54 +142,6 @@ func (c *Client) Broadcast(gaslimit uint64, authzWrappedMsg sdktypes.Msg, authzS return commit.TxHash, nil } -// GetContext return a valid context with all relevant values set -func (c *Client) GetContext() (client.Context, error) { - ctx := client.Context{} - addr, err := c.keys.GetAddress() - if err != nil { - c.logger.Error().Err(err).Msg("fail to get address from key") - return ctx, err - } - - // if password is needed, set it as input - password := c.keys.GetHotkeyPassword() - if password != "" { - ctx = ctx.WithInput(strings.NewReader(fmt.Sprintf("%[1]s\n%[1]s\n", password))) - } - - ctx = ctx.WithKeyring(c.keys.GetKeybase()) - ctx = ctx.WithChainID(c.chainID) - ctx = ctx.WithHomeDir(c.cfg.ChainHomeFolder) - ctx = ctx.WithFromName(c.cfg.SignerName) - ctx = ctx.WithFromAddress(addr) - ctx = ctx.WithBroadcastMode("sync") - - ctx = ctx.WithCodec(c.encodingCfg.Codec) - ctx = ctx.WithInterfaceRegistry(c.encodingCfg.InterfaceRegistry) - ctx = ctx.WithTxConfig(c.encodingCfg.TxConfig) - ctx = ctx.WithLegacyAmino(c.encodingCfg.Amino) - ctx = ctx.WithAccountRetriever(authtypes.AccountRetriever{}) - - if c.enableMockSDKClient { - ctx = ctx.WithClient(c.mockSDKClient) - } else { - remote := c.cfg.ChainRPC - if !strings.HasPrefix(c.cfg.ChainHost, "http") { - remote = fmt.Sprintf("tcp://%s", remote) - } - - ctx = ctx.WithNodeURI(remote) - wsClient, err := rpchttp.New(remote, "/websocket") - if err != nil { - return ctx, err - } - - ctx = ctx.WithClient(wsClient) - } - - return ctx, nil -} - // SignTx signs a tx with the given name func (c *Client) SignTx( txf clienttx.Factory, @@ -212,42 +150,48 @@ func (c *Client) SignTx( overwriteSig bool, txConfig client.TxConfig, ) error { - if c.cfg.HsmMode { + if c.config.HsmMode { return hsm.SignWithHSM(txf, name, txBuilder, overwriteSig, txConfig) } + return clienttx.Sign(txf, name, txBuilder, overwriteSig) } // QueryTxResult query the result of a tx func (c *Client) QueryTxResult(hash string) (*sdktypes.TxResponse, error) { - ctx, err := c.GetContext() - if err != nil { - return nil, err - } - return authtx.QueryTx(ctx, hash) + return authtx.QueryTx(c.cosmosClientContext, hash) } // HandleBroadcastError returns whether to retry in a few seconds, and whether to report via AddOutboundTracker // returns (bool retry, bool report) func HandleBroadcastError(err error, nonce, toChain, outboundHash string) (bool, bool) { - if strings.Contains(err.Error(), "nonce too low") { - log.Warn(). - Err(err). - Msgf("nonce too low! this might be a unnecessary key-sign. increase re-try interval and awaits outbound confirmation") + if err == nil { return false, false } - if strings.Contains(err.Error(), "replacement transaction underpriced") { - log.Warn(). - Err(err). - Msgf("Broadcast replacement: nonce %s chain %s outboundHash %s", nonce, toChain, outboundHash) + + msg := err.Error() + evt := log.Warn().Err(err). + Str("broadcast.nonce", nonce). + Str("broadcast.to_chain", toChain). + Str("broadcast.outbound_hash", outboundHash) + + switch { + case strings.Contains(msg, "nonce too low"): + const m = "nonce too low! this might be a unnecessary key-sign. increase retry interval and awaits outbound confirmation" + evt.Msg(m) return false, false - } else if strings.Contains(err.Error(), "already known") { // this is error code from QuickNode - log.Warn().Err(err).Msgf("Broadcast duplicates: nonce %s chain %s outboundHash %s", nonce, toChain, outboundHash) - return false, true // report to tracker, because there's possibilities a successful broadcast gets this error code - } - log.Error(). - Err(err). - Msgf("Broadcast error: nonce %s chain %s outboundHash %s; retrying...", nonce, toChain, outboundHash) - return true, false + case strings.Contains(msg, "replacement transaction underpriced"): + evt.Msg("Broadcast replacement") + return false, false + + case strings.Contains(msg, "already known"): + // report to tracker, because there's possibilities a successful broadcast gets this error code + evt.Msg("Broadcast duplicates") + return false, true + + default: + evt.Msg("Broadcast error. Retrying...") + return true, false + } } diff --git a/zetaclient/zetacore/broadcast_test.go b/zetaclient/zetacore/broadcast_test.go index ba86407811..6acd5c535f 100644 --- a/zetaclient/zetacore/broadcast_test.go +++ b/zetaclient/zetacore/broadcast_test.go @@ -1,6 +1,7 @@ package zetacore import ( + "context" "encoding/hex" "errors" "net" @@ -39,11 +40,14 @@ func TestHandleBroadcastError(t *testing.T) { } func TestBroadcast(t *testing.T) { + ctx := context.Background() + address := types.AccAddress(mocks.TestKeyringPair.PubKey().Address().Bytes()) //Setup server for multiple grpc calls listener, err := net.Listen("tcp", "127.0.0.1:9090") require.NoError(t, err) + server := grpcmock.MockUnstartedServer( grpcmock.RegisterService(crosschaintypes.RegisterQueryServer), grpcmock.RegisterService(feemarkettypes.RegisterQueryServer), @@ -70,14 +74,16 @@ func TestBroadcast(t *testing.T) { )(t) server.Serve() - defer closeMockServer(t, server) + defer server.Close() - client, err := setupZetacoreClient() - require.NoError(t, err) - client.keys = keys.NewKeysWithKeybase(mocks.NewKeyring(), address, testSigner, "") + observerKeys := keys.NewKeysWithKeybase(mocks.NewKeyring(), address, testSigner, "") t.Run("broadcast success", func(t *testing.T) { - client.EnableMockSDKClient(mocks.NewSDKClientWithErr(nil, 0)) + client := setupZetacoreClient(t, + withObserverKeys(observerKeys), + withTendermint(mocks.NewSDKClientWithErr(t, nil, 0)), + ) + blockHash, err := hex.DecodeString(ethBlockHash) require.NoError(t, err) msg := observerTypes.NewMsgVoteBlockHeader( @@ -87,16 +93,21 @@ func TestBroadcast(t *testing.T) { 18495266, getHeaderData(t), ) - authzMsg, authzSigner, err := client.WrapMessageWithAuthz(msg) + authzMsg, authzSigner, err := WrapMessageWithAuthz(msg) require.NoError(t, err) - _, err = BroadcastToZetaCore(client, 10000, authzMsg, authzSigner) + + _, err = client.Broadcast(ctx, 10_000, authzMsg, authzSigner) require.NoError(t, err) }) t.Run("broadcast failed", func(t *testing.T) { - client.EnableMockSDKClient( - mocks.NewSDKClientWithErr(errors.New("account sequence mismatch, expected 5 got 4"), 32), + client := setupZetacoreClient(t, + withObserverKeys(observerKeys), + withTendermint( + mocks.NewSDKClientWithErr(t, errors.New("account sequence mismatch, expected 5 got 4"), 32), + ), ) + blockHash, err := hex.DecodeString(ethBlockHash) require.NoError(t, err) msg := observerTypes.NewMsgVoteBlockHeader( @@ -106,19 +117,10 @@ func TestBroadcast(t *testing.T) { 18495266, getHeaderData(t), ) - authzMsg, authzSigner, err := client.WrapMessageWithAuthz(msg) + authzMsg, authzSigner, err := WrapMessageWithAuthz(msg) require.NoError(t, err) - _, err = BroadcastToZetaCore(client, 10000, authzMsg, authzSigner) + + _, err = client.Broadcast(ctx, 10_000, authzMsg, authzSigner) require.Error(t, err) }) } - -func TestZetacore_GetContext(t *testing.T) { - address := types.AccAddress(mocks.TestKeyringPair.PubKey().Address().Bytes()) - client, err := setupZetacoreClient() - require.NoError(t, err) - client.keys = keys.NewKeysWithKeybase(mocks.NewKeyring(), address, testSigner, "") - - _, err = client.GetContext() - require.NoError(t, err) -} diff --git a/zetaclient/zetacore/client.go b/zetaclient/zetacore/client.go index 53d1d5958c..2874408451 100644 --- a/zetaclient/zetacore/client.go +++ b/zetaclient/zetacore/client.go @@ -1,52 +1,100 @@ -// Package zetacore provides functionalities for interacting with ZetaChain +// Package zetacore provides the client to interact with zetacore node via GRPC. package zetacore import ( + "context" "fmt" + "strings" "sync" "time" "cosmossdk.io/simapp/params" - rpcclient "github.com/cometbft/cometbft/rpc/client" + rpchttp "github.com/cometbft/cometbft/rpc/client/http" + cosmosclient "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + feemarkettypes "github.com/evmos/ethermint/x/feemarket/types" "github.com/pkg/errors" "github.com/rs/zerolog" - "github.com/rs/zerolog/log" "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" "github.com/zeta-chain/zetacore/app" "github.com/zeta-chain/zetacore/pkg/authz" "github.com/zeta-chain/zetacore/pkg/chains" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" + lightclienttypes "github.com/zeta-chain/zetacore/x/lightclient/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" "github.com/zeta-chain/zetacore/zetaclient/config" - "github.com/zeta-chain/zetacore/zetaclient/context" + zctx "github.com/zeta-chain/zetacore/zetaclient/context" keyinterfaces "github.com/zeta-chain/zetacore/zetaclient/keys/interfaces" - "github.com/zeta-chain/zetacore/zetaclient/metrics" ) var _ interfaces.ZetacoreClient = &Client{} // Client is the client to send tx to zetacore type Client struct { - logger zerolog.Logger + logger zerolog.Logger + config config.ClientConfiguration + + client clients + cosmosClientContext cosmosclient.Context + blockHeight int64 accountNumber map[authz.KeyType]uint64 seqNumber map[authz.KeyType]uint64 - grpcConn *grpc.ClientConn - cfg config.ClientConfiguration - encodingCfg params.EncodingConfig - keys keyinterfaces.ObserverKeys - broadcastLock *sync.RWMutex - chainID string - chain chains.Chain - stop chan struct{} - pause chan struct{} - Telemetry *metrics.TelemetryServer - - // enableMockSDKClient is a flag that determines whether the mock cosmos sdk client should be used, primarily for - // unit testing - enableMockSDKClient bool - mockSDKClient rpcclient.Client + + encodingCfg params.EncodingConfig + keys keyinterfaces.ObserverKeys + chainID string + chain chains.Chain + stop chan struct{} + onBeforeStopCallback []func() + + mu sync.RWMutex +} + +type clients struct { + observer observertypes.QueryClient + light lightclienttypes.QueryClient + crosschain crosschaintypes.QueryClient + bank banktypes.QueryClient + upgrade upgradetypes.QueryClient + fees feemarkettypes.QueryClient + authority authoritytypes.QueryClient + tendermint tmservice.ServiceClient +} + +var unsecureGRPC = grpc.WithTransportCredentials(insecure.NewCredentials()) + +type constructOpts struct { + customTendermint bool + tendermintClient cosmosclient.TendermintRPC + + customAccountRetriever bool + accountRetriever cosmosclient.AccountRetriever +} + +type Opt func(cfg *constructOpts) + +// WithTendermintClient sets custom tendermint client +func WithTendermintClient(client cosmosclient.TendermintRPC) Opt { + return func(c *constructOpts) { + c.customTendermint = true + c.tendermintClient = client + } +} + +// WithCustomAccountRetriever sets custom tendermint client +func WithCustomAccountRetriever(ac cosmosclient.AccountRetriever) Opt { + return func(c *constructOpts) { + c.customAccountRetriever = true + c.accountRetriever = ac + } } // NewClient create a new instance of Client @@ -56,26 +104,36 @@ func NewClient( signerName string, chainID string, hsmMode bool, - telemetry *metrics.TelemetryServer, + logger zerolog.Logger, + opts ...Opt, ) (*Client, error) { - // main module logger - logger := log.With().Str("module", "ZetacoreClient").Logger() + var constructOptions constructOpts + for _, opt := range opts { + opt(&constructOptions) + } + + zetaChain, err := chains.ZetaChainFromCosmosChainID(chainID) + if err != nil { + return nil, errors.Wrapf(err, "invalid chain id %q", chainID) + } + + log := logger.With().Str("module", "zetacoreClient").Logger() + cfg := config.ClientConfiguration{ - ChainHost: fmt.Sprintf("%s:1317", chainIP), + ChainHost: cosmosREST(chainIP), SignerName: signerName, SignerPasswd: "password", - ChainRPC: fmt.Sprintf("%s:26657", chainIP), + ChainRPC: tendermintRPC(chainIP), HsmMode: hsmMode, } - grpcConn, err := grpc.Dial( - fmt.Sprintf("%s:9090", chainIP), - grpc.WithInsecure(), - ) + encodingCfg := app.MakeEncodingConfig() + + grpcConn, err := grpc.Dial(cosmosGRPC(chainIP), unsecureGRPC) if err != nil { - logger.Error().Err(err).Msg("grpc dial fail") - return nil, err + return nil, errors.Wrap(err, "grpc dial fail") } + accountsMap := make(map[authz.KeyType]uint64) seqMap := make(map[authz.KeyType]uint64) for _, keyType := range authz.GetAllKeyTypes() { @@ -83,27 +141,110 @@ func NewClient( seqMap[keyType] = 0 } - zetaChain, err := chains.ZetaChainFromCosmosChainID(chainID) + cosmosContext, err := buildCosmosClientContext(chainID, keys, cfg, encodingCfg, constructOptions) if err != nil { - return nil, fmt.Errorf("invalid chain id %s, %w", chainID, err) + return nil, errors.Wrap(err, "unable to build cosmos client context") } return &Client{ - logger: logger, - grpcConn: grpcConn, - accountNumber: accountsMap, - seqNumber: seqMap, - cfg: cfg, - encodingCfg: app.MakeEncodingConfig(), - keys: keys, - broadcastLock: &sync.RWMutex{}, - stop: make(chan struct{}), - chainID: chainID, - chain: zetaChain, - pause: make(chan struct{}), - Telemetry: telemetry, - enableMockSDKClient: false, - mockSDKClient: nil, + logger: log, + config: cfg, + + cosmosClientContext: cosmosContext, + client: clients{ + observer: observertypes.NewQueryClient(grpcConn), + light: lightclienttypes.NewQueryClient(grpcConn), + crosschain: crosschaintypes.NewQueryClient(grpcConn), + bank: banktypes.NewQueryClient(grpcConn), + upgrade: upgradetypes.NewQueryClient(grpcConn), + fees: feemarkettypes.NewQueryClient(grpcConn), + authority: authoritytypes.NewQueryClient(grpcConn), + tendermint: tmservice.NewServiceClient(grpcConn), + }, + + accountNumber: accountsMap, + seqNumber: seqMap, + + encodingCfg: encodingCfg, + keys: keys, + stop: make(chan struct{}), + chainID: chainID, + chain: zetaChain, + }, nil +} + +// buildCosmosClientContext constructs a valid context with all relevant values set +func buildCosmosClientContext( + chainID string, + keys keyinterfaces.ObserverKeys, + config config.ClientConfiguration, + encodingConfig params.EncodingConfig, + opts constructOpts, +) (cosmosclient.Context, error) { + if keys == nil { + return cosmosclient.Context{}, errors.New("client key are not set") + } + + addr, err := keys.GetAddress() + if err != nil { + return cosmosclient.Context{}, errors.Wrap(err, "fail to get address from key") + } + + var ( + input = strings.NewReader("") + client cosmosclient.TendermintRPC + nodeURI string + ) + + // if password is needed, set it as input + password := keys.GetHotkeyPassword() + if password != "" { + input = strings.NewReader(fmt.Sprintf("%[1]s\n%[1]s\n", password)) + } + + // note that in rare cases, this might give FALSE positive + // (google "golang nil interface comparison") + client = opts.tendermintClient + if !opts.customTendermint { + remote := config.ChainRPC + if !strings.HasPrefix(config.ChainHost, "http") { + remote = fmt.Sprintf("tcp://%s", remote) + } + + wsClient, err := rpchttp.New(remote, "/websocket") + if err != nil { + return cosmosclient.Context{}, err + } + + client = wsClient + nodeURI = remote + } + + var accountRetriever cosmosclient.AccountRetriever + if opts.customAccountRetriever { + accountRetriever = opts.accountRetriever + } else { + accountRetriever = authtypes.AccountRetriever{} + } + + return cosmosclient.Context{ + Client: client, + NodeURI: nodeURI, + FromAddress: addr, + ChainID: chainID, + Keyring: keys.GetKeybase(), + BroadcastMode: "sync", + HomeDir: config.ChainHomeFolder, + FromName: config.SignerName, + + AccountRetriever: accountRetriever, + + Codec: encodingConfig.Codec, + InterfaceRegistry: encodingConfig.InterfaceRegistry, + TxConfig: encodingConfig.TxConfig, + LegacyAmino: encodingConfig.Amino, + + Input: input, }, nil } @@ -134,38 +275,45 @@ func (c *Client) GetKeys() keyinterfaces.ObserverKeys { return c.keys } +// OnBeforeStop adds a callback to be called before the client stops. +func (c *Client) OnBeforeStop(callback func()) { + c.onBeforeStopCallback = append(c.onBeforeStopCallback, callback) +} + +// Stop stops the client and optionally calls the onBeforeStop callbacks. func (c *Client) Stop() { - c.logger.Info().Msgf("zetacore client is stopping") - close(c.stop) // this notifies all configupdater to stop + c.logger.Info().Msgf("Stopping zetacore client") + + for i := len(c.onBeforeStopCallback) - 1; i >= 0; i-- { + c.logger.Info().Int("callback.index", i).Msgf("calling onBeforeStopCallback") + c.onBeforeStopCallback[i]() + } + + close(c.stop) } // GetAccountNumberAndSequenceNumber We do not use multiple KeyType for now , but this can be optionally used in the future to seprate TSS signer from Zetaclient GRantee func (c *Client) GetAccountNumberAndSequenceNumber(_ authz.KeyType) (uint64, uint64, error) { - ctx, err := c.GetContext() - if err != nil { - return 0, 0, err - } address, err := c.keys.GetAddress() if err != nil { return 0, 0, err } - return ctx.AccountRetriever.GetAccountNumberSequence(ctx, address) + return c.cosmosClientContext.AccountRetriever.GetAccountNumberSequence(c.cosmosClientContext, address) } // SetAccountNumber sets the account number and sequence number for the given keyType +// todo remove method and make it part of the client constructor. func (c *Client) SetAccountNumber(keyType authz.KeyType) error { - ctx, err := c.GetContext() - if err != nil { - return errors.Wrap(err, "fail to get context") - } address, err := c.keys.GetAddress() if err != nil { return errors.Wrap(err, "fail to get address") } - accN, seq, err := ctx.AccountRetriever.GetAccountNumberSequence(ctx, address) + + accN, seq, err := c.cosmosClientContext.AccountRetriever.GetAccountNumberSequence(c.cosmosClientContext, address) if err != nil { return errors.Wrap(err, "fail to get account number and sequence number") } + c.accountNumber[keyType] = accN c.seqNumber[keyType] = seq @@ -173,10 +321,10 @@ func (c *Client) SetAccountNumber(keyType authz.KeyType) error { } // WaitForZetacoreToCreateBlocks waits for zetacore to create blocks -func (c *Client) WaitForZetacoreToCreateBlocks() error { +func (c *Client) WaitForZetacoreToCreateBlocks(ctx context.Context) error { retryCount := 0 for { - block, err := c.GetLatestZetaBlock() + block, err := c.GetLatestZetaBlock(ctx) if err == nil && block.Header.Height > 1 { c.logger.Info().Msgf("Zetacore height: %d", block.Header.Height) break @@ -193,29 +341,40 @@ func (c *Client) WaitForZetacoreToCreateBlocks() error { // UpdateZetacoreContext updates zetacore context // zetacore stores zetacore context for all clients -func (c *Client) UpdateZetacoreContext(coreContext *context.AppContext, init bool, sampledLogger zerolog.Logger) error { - bn, err := c.GetBlockHeight() +func (c *Client) UpdateZetacoreContext( + ctx context.Context, + appContext *zctx.AppContext, + init bool, + sampledLogger zerolog.Logger, +) error { + bn, err := c.GetBlockHeight(ctx) if err != nil { return fmt.Errorf("failed to get zetablock height: %w", err) } - plan, err := c.GetUpgradePlan() + + plan, err := c.GetUpgradePlan(ctx) if err != nil { - // if there is no active upgrade plan, plan will be nil, err will be nil as well. return fmt.Errorf("failed to get upgrade plan: %w", err) } - if plan != nil && bn == plan.Height-1 { // stop zetaclients; notify operator to upgrade and restart - c.logger.Warn(). - Msgf("Active upgrade plan detected and upgrade height reached: %s at height %d; ZetaClient is stopped;"+ - "please kill this process, replace zetaclientd binary with upgraded version, and restart zetaclientd", plan.Name, plan.Height) - c.pause <- struct{}{} // notify Orchestrator to stop Observers, Signers, and Orchestrator itself + + // Stop client and notify dependant services to stop (Orchestrator, Observers, and Signers) + if plan != nil && bn == plan.Height-1 { + c.logger.Warn().Msgf( + "Active upgrade plan detected and upgrade height reached: %s at height %d; Stopping ZetaClient;"+ + " please kill this process, replace zetaclientd binary with upgraded version, and restart zetaclientd", + plan.Name, + plan.Height, + ) + + c.Stop() } - additionalChains, err := c.GetAdditionalChains() + additionalChains, err := c.GetAdditionalChains(ctx) if err != nil { return fmt.Errorf("failed to additional chains: %w", err) } - chainParams, err := c.GetChainParams() + chainParams, err := c.GetChainParams(ctx) if err != nil { return fmt.Errorf("failed to get chain params: %w", err) } @@ -237,40 +396,42 @@ func (c *Client) UpdateZetacoreContext(coreContext *context.AppContext, init boo } } - supportedChains, err := c.GetSupportedChains() + supportedChains, err := c.GetSupportedChains(ctx) if err != nil { return fmt.Errorf("failed to get supported chains: %w", err) } + newChains := make([]chains.Chain, len(supportedChains)) for i, chain := range supportedChains { newChains[i] = chain } - keyGen, err := c.GetKeyGen() + + keyGen, err := c.GetKeyGen(ctx) if err != nil { c.logger.Info().Msg("Unable to fetch keygen from zetacore") return fmt.Errorf("failed to get keygen: %w", err) } - tss, err := c.GetCurrentTss() + tss, err := c.GetCurrentTSS(ctx) if err != nil { c.logger.Info().Err(err).Msg("Unable to fetch TSS from zetacore") return fmt.Errorf("failed to get current tss: %w", err) } tssPubKey := tss.GetTssPubkey() - crosschainFlags, err := c.GetCrosschainFlags() + crosschainFlags, err := c.GetCrosschainFlags(ctx) if err != nil { c.logger.Info().Msg("Unable to fetch cross-chain flags from zetacore") return fmt.Errorf("failed to get crosschain flags: %w", err) } - blockHeaderEnabledChains, err := c.GetBlockHeaderEnabledChains() + blockHeaderEnabledChains, err := c.GetBlockHeaderEnabledChains(ctx) if err != nil { c.logger.Info().Msg("Unable to fetch block header enabled chains from zetacore") return err } - coreContext.Update( + appContext.Update( keyGen, newChains, newEVMParams, @@ -285,19 +446,14 @@ func (c *Client) UpdateZetacoreContext(coreContext *context.AppContext, init boo return nil } -// Pause pauses the client -func (c *Client) Pause() { - <-c.pause +func cosmosREST(host string) string { + return fmt.Sprintf("%s:1317", host) } -// Unpause unpauses the client -func (c *Client) Unpause() { - c.pause <- struct{}{} +func cosmosGRPC(host string) string { + return fmt.Sprintf("%s:9090", host) } -// EnableMockSDKClient enables the mock cosmos sdk client -// TODO(revamp): move this to a test package -func (c *Client) EnableMockSDKClient(client rpcclient.Client) { - c.mockSDKClient = client - c.enableMockSDKClient = true +func tendermintRPC(host string) string { + return fmt.Sprintf("%s:26657", host) } diff --git a/zetaclient/zetacore/client_monitor.go b/zetaclient/zetacore/client_monitor.go new file mode 100644 index 0000000000..6c124ced48 --- /dev/null +++ b/zetaclient/zetacore/client_monitor.go @@ -0,0 +1,182 @@ +package zetacore + +import ( + "context" + "strings" + "time" + + "github.com/cenkalti/backoff/v4" + "github.com/pkg/errors" + + "github.com/zeta-chain/zetacore/pkg/retry" + "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +// MonitorVoteOutboundResult monitors the result of a vote outbound tx +// retryGasLimit is the gas limit used to resend the tx if it fails because of insufficient gas +// if retryGasLimit is 0, the tx is not resent +func (c *Client) MonitorVoteOutboundResult( + ctx context.Context, + zetaTxHash string, + retryGasLimit uint64, + msg *types.MsgVoteOutbound, +) error { + defer func() { + if r := recover(); r != nil { + c.logger.Error(). + Interface("panic", r). + Str("outbound.hash", zetaTxHash). + Msg("monitorVoteOutboundResult: recovered from panic") + } + }() + + call := func() error { + return retry.Retry(c.monitorVoteOutboundResult(ctx, zetaTxHash, retryGasLimit, msg)) + } + + err := retryWithBackoff(call, monitorRetryCount, monitorInterval/2, monitorInterval) + if err != nil { + c.logger.Error().Err(err). + Str("outbound.hash", zetaTxHash). + Msg("monitorVoteOutboundResult: unable to query tx result") + + return err + } + + return nil +} + +func (c *Client) monitorVoteOutboundResult( + ctx context.Context, + zetaTxHash string, + retryGasLimit uint64, + msg *types.MsgVoteOutbound, +) error { + // query tx result from ZetaChain + txResult, err := c.QueryTxResult(zetaTxHash) + if err != nil { + return errors.Wrap(err, "failed to query tx result") + } + + logFields := map[string]any{ + "outbound.hash": zetaTxHash, + "outbound.raw_log": txResult.RawLog, + } + + switch { + case strings.Contains(txResult.RawLog, "failed to execute message"): + // the inbound vote tx shouldn't fail to execute + // this shouldn't happen + c.logger.Error().Fields(logFields).Msg("monitorVoteOutboundResult: failed to execute vote") + case strings.Contains(txResult.RawLog, "out of gas"): + // if the tx fails with an out of gas error, resend the tx with more gas if retryGasLimit > 0 + c.logger.Debug().Fields(logFields).Msg("monitorVoteOutboundResult: out of gas") + + if retryGasLimit > 0 { + // new retryGasLimit set to 0 to prevent reentering this function + if _, _, err := c.PostVoteOutbound(ctx, retryGasLimit, 0, msg); err != nil { + c.logger.Error().Err(err).Fields(logFields).Msg("monitorVoteOutboundResult: failed to resend tx") + } else { + c.logger.Info().Fields(logFields).Msg("monitorVoteOutboundResult: successfully resent tx") + } + } + default: + c.logger.Debug().Fields(logFields).Msg("monitorVoteOutboundResult: successful") + } + + return nil +} + +// MonitorVoteInboundResult monitors the result of a vote inbound tx +// retryGasLimit is the gas limit used to resend the tx if it fails because of insufficient gas +// if retryGasLimit is 0, the tx is not resent +func (c *Client) MonitorVoteInboundResult( + ctx context.Context, + zetaTxHash string, + retryGasLimit uint64, + msg *types.MsgVoteInbound, +) error { + defer func() { + if r := recover(); r != nil { + c.logger.Error(). + Interface("panic", r). + Str("inbound.hash", zetaTxHash). + Msg("monitorVoteInboundResult: recovered from panic") + } + }() + + call := func() error { + return retry.Retry(c.monitorVoteInboundResult(ctx, zetaTxHash, retryGasLimit, msg)) + } + + err := retryWithBackoff(call, monitorRetryCount, monitorInterval/2, monitorInterval) + if err != nil { + c.logger.Error().Err(err). + Str("inbound.hash", zetaTxHash). + Msg("monitorVoteInboundResult: unable to query tx result") + + return err + } + + return nil +} + +func (c *Client) monitorVoteInboundResult( + ctx context.Context, + zetaTxHash string, + retryGasLimit uint64, + msg *types.MsgVoteInbound, +) error { + // query tx result from ZetaChain + txResult, err := c.QueryTxResult(zetaTxHash) + if err != nil { + return errors.Wrap(err, "failed to query tx result") + } + + logFields := map[string]any{ + "inbound.hash": zetaTxHash, + "inbound.raw_log": txResult.RawLog, + } + + switch { + case strings.Contains(txResult.RawLog, "failed to execute message"): + // the inbound vote tx shouldn't fail to execute + // this shouldn't happen + c.logger.Error().Fields(logFields).Msg("monitorVoteInboundResult: failed to execute vote") + + case strings.Contains(txResult.RawLog, "out of gas"): + // if the tx fails with an out of gas error, resend the tx with more gas if retryGasLimit > 0 + c.logger.Debug().Fields(logFields).Msg("monitorVoteInboundResult: out of gas") + + if retryGasLimit > 0 { + // new retryGasLimit set to 0 to prevent reentering this function + if _, _, err := c.PostVoteInbound(ctx, retryGasLimit, 0, msg); err != nil { + c.logger.Error().Err(err).Fields(logFields).Msg("monitorVoteInboundResult: failed to resend tx") + } else { + c.logger.Info().Fields(logFields).Msg("monitorVoteInboundResult: successfully resent tx") + } + } + + default: + c.logger.Debug().Fields(logFields).Msgf("monitorVoteInboundResult: successful") + } + + return nil +} + +func retryWithBackoff(call func() error, attempts int, minInternal, maxInterval time.Duration) error { + if attempts < 1 { + return errors.New("attempts must be positive") + } + + bo := backoff.WithMaxRetries( + backoff.NewExponentialBackOff( + backoff.WithInitialInterval(minInternal), + backoff.WithMaxInterval(maxInterval), + ), + // #nosec G115 always positive + uint64(attempts), + ) + + return retry.DoWithBackoff(call, bo) +} diff --git a/zetaclient/zetacore/client_query_authority.go b/zetaclient/zetacore/client_query_authority.go new file mode 100644 index 0000000000..044a3d47ee --- /dev/null +++ b/zetaclient/zetacore/client_query_authority.go @@ -0,0 +1,18 @@ +package zetacore + +import ( + "context" + + "github.com/zeta-chain/zetacore/pkg/chains" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" +) + +// GetAdditionalChains returns the additional chains +func (c *Client) GetAdditionalChains(ctx context.Context) ([]chains.Chain, error) { + resp, err := c.client.authority.ChainInfo(ctx, &authoritytypes.QueryGetChainInfoRequest{}) + if err != nil { + return nil, err + } + + return resp.GetChainInfo().Chains, nil +} diff --git a/zetaclient/zetacore/client_query_cosmos.go b/zetaclient/zetacore/client_query_cosmos.go new file mode 100644 index 0000000000..61d690d54a --- /dev/null +++ b/zetaclient/zetacore/client_query_cosmos.go @@ -0,0 +1,88 @@ +package zetacore + +import ( + "context" + "fmt" + + sdkmath "cosmossdk.io/math" + tmhttp "github.com/cometbft/cometbft/rpc/client/http" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + "github.com/pkg/errors" + + "github.com/zeta-chain/zetacore/cmd/zetacored/config" +) + +// GetGenesisSupply returns the genesis supply. +// NOTE that this method is brittle as it uses STATEFUL connection +func (c *Client) GetGenesisSupply(ctx context.Context) (sdkmath.Int, error) { + tmURL := fmt.Sprintf("http://%s", c.config.ChainRPC) + + s, err := tmhttp.New(tmURL, "/websocket") + if err != nil { + return sdkmath.ZeroInt(), errors.Wrap(err, "failed to create tm client") + } + + // nolint:errcheck + defer s.Stop() + + res, err := s.Genesis(ctx) + if err != nil { + return sdkmath.ZeroInt(), errors.Wrap(err, "failed to get genesis") + } + + appState, err := genutiltypes.GenesisStateFromGenDoc(*res.Genesis) + if err != nil { + return sdkmath.ZeroInt(), errors.Wrap(err, "failed to get app state") + } + + bankstate := banktypes.GetGenesisStateFromAppState(c.encodingCfg.Codec, appState) + + return bankstate.Supply.AmountOf(config.BaseDenom), nil +} + +// GetUpgradePlan returns the current upgrade plan. +// if there is no active upgrade plan, plan will be nil, err will be nil as well. +func (c *Client) GetUpgradePlan(ctx context.Context) (*upgradetypes.Plan, error) { + in := &upgradetypes.QueryCurrentPlanRequest{} + + resp, err := c.client.upgrade.CurrentPlan(ctx, in) + if err != nil { + return nil, errors.Wrap(err, "failed to get current upgrade plan") + } + + return resp.Plan, nil +} + +// GetZetaTokenSupplyOnNode returns the zeta token supply on the node +func (c *Client) GetZetaTokenSupplyOnNode(ctx context.Context) (sdkmath.Int, error) { + in := &banktypes.QuerySupplyOfRequest{Denom: config.BaseDenom} + + resp, err := c.client.bank.SupplyOf(ctx, in) + if err != nil { + return sdkmath.ZeroInt(), errors.Wrap(err, "failed to get zeta token supply") + } + + return resp.GetAmount().Amount, nil +} + +// GetZetaHotKeyBalance returns the zeta hot key balance +func (c *Client) GetZetaHotKeyBalance(ctx context.Context) (sdkmath.Int, error) { + address, err := c.keys.GetAddress() + if err != nil { + return sdkmath.ZeroInt(), errors.Wrap(err, "failed to get address") + } + + in := &banktypes.QueryBalanceRequest{ + Address: address.String(), + Denom: config.BaseDenom, + } + + resp, err := c.client.bank.Balance(ctx, in) + if err != nil { + return sdkmath.ZeroInt(), errors.Wrap(err, "failed to get zeta hot key balance") + } + + return resp.Balance.Amount, nil +} diff --git a/zetaclient/zetacore/client_query_crosschain.go b/zetaclient/zetacore/client_query_crosschain.go new file mode 100644 index 0000000000..262b766a6b --- /dev/null +++ b/zetaclient/zetacore/client_query_crosschain.go @@ -0,0 +1,194 @@ +package zetacore + +import ( + "context" + "sort" + + "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/types/query" + "google.golang.org/grpc" + + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/x/crosschain/types" + "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" +) + +// 32MB +var maxSizeOption = grpc.MaxCallRecvMsgSize(32 * 1024 * 1024) + +// GetLastBlockHeight returns the zetachain block height +func (c *Client) GetLastBlockHeight(ctx context.Context) (uint64, error) { + resp, err := c.client.crosschain.LastBlockHeight(ctx, &types.QueryGetLastBlockHeightRequest{}) + if err != nil { + return 0, errors.Wrap(err, "failed to get block height") + } + + return resp.GetLastBlockHeight().LastInboundHeight, nil +} + +// GetBlockHeight returns the zetachain block height +func (c *Client) GetBlockHeight(ctx context.Context) (int64, error) { + resp, err := c.client.crosschain.LastZetaHeight(ctx, &types.QueryLastZetaHeightRequest{}) + if err != nil { + return 0, err + } + + return resp.Height, nil +} + +// GetAbortedZetaAmount returns the amount of zeta that has been aborted +func (c *Client) GetAbortedZetaAmount(ctx context.Context) (string, error) { + resp, err := c.client.crosschain.ZetaAccounting(ctx, &types.QueryZetaAccountingRequest{}) + if err != nil { + return "", errors.Wrap(err, "failed to get aborted zeta amount") + } + + return resp.AbortedZetaAmount, nil +} + +// GetRateLimiterFlags returns the rate limiter flags +func (c *Client) GetRateLimiterFlags(ctx context.Context) (types.RateLimiterFlags, error) { + resp, err := c.client.crosschain.RateLimiterFlags(ctx, &types.QueryRateLimiterFlagsRequest{}) + if err != nil { + return types.RateLimiterFlags{}, errors.Wrap(err, "failed to get rate limiter flags") + } + + return resp.RateLimiterFlags, nil +} + +// GetRateLimiterInput returns input data for the rate limit checker +func (c *Client) GetRateLimiterInput(ctx context.Context, window int64) (*types.QueryRateLimiterInputResponse, error) { + in := &types.QueryRateLimiterInputRequest{Window: window} + + resp, err := c.client.crosschain.RateLimiterInput(ctx, in, maxSizeOption) + if err != nil { + return nil, errors.Wrap(err, "failed to get rate limiter input") + } + + return resp, nil +} + +// GetAllCctx returns all cross chain transactions +func (c *Client) GetAllCctx(ctx context.Context) ([]*types.CrossChainTx, error) { + resp, err := c.client.crosschain.CctxAll(ctx, &types.QueryAllCctxRequest{}) + if err != nil { + return nil, errors.Wrap(err, "failed to get all cross chain transactions") + } + + return resp.CrossChainTx, nil +} + +func (c *Client) GetCctxByHash(ctx context.Context, sendHash string) (*types.CrossChainTx, error) { + in := &types.QueryGetCctxRequest{Index: sendHash} + resp, err := c.client.crosschain.Cctx(ctx, in) + if err != nil { + return nil, errors.Wrap(err, "failed to get cctx by hash") + } + + return resp.CrossChainTx, nil +} + +// GetCctxByNonce returns a cross chain transaction by nonce +func (c *Client) GetCctxByNonce(ctx context.Context, chainID int64, nonce uint64) (*types.CrossChainTx, error) { + resp, err := c.client.crosschain.CctxByNonce(ctx, &types.QueryGetCctxByNonceRequest{ + ChainID: chainID, + Nonce: nonce, + }) + if err != nil { + return nil, errors.Wrap(err, "failed to get cctx by nonce") + } + + return resp.CrossChainTx, nil +} + +// ListPendingCCTXWithinRateLimit returns a list of pending cctxs that do not exceed the outbound rate limit +// - The max size of the list is crosschainkeeper.MaxPendingCctxs +// - The returned `rateLimitExceeded` flag indicates if the rate limit is exceeded or not +func (c *Client) ListPendingCCTXWithinRateLimit( + ctx context.Context, +) (*types.QueryListPendingCctxWithinRateLimitResponse, error) { + in := &types.QueryListPendingCctxWithinRateLimitRequest{} + + resp, err := c.client.crosschain.ListPendingCctxWithinRateLimit(ctx, in, maxSizeOption) + if err != nil { + return nil, errors.Wrap(err, "failed to get pending cctxs within rate limit") + } + + return resp, nil +} + +// ListPendingCCTX returns a list of pending cctxs for a given chainID +// - The max size of the list is crosschainkeeper.MaxPendingCctxs +func (c *Client) ListPendingCCTX(ctx context.Context, chainID int64) ([]*types.CrossChainTx, uint64, error) { + in := &types.QueryListPendingCctxRequest{ChainId: chainID} + + resp, err := c.client.crosschain.ListPendingCctx(ctx, in, maxSizeOption) + if err != nil { + return nil, 0, errors.Wrap(err, "failed to get pending cctxs") + } + + return resp.CrossChainTx, resp.TotalPending, nil +} + +// GetOutboundTracker returns the outbound tracker for a chain and nonce +func (c *Client) GetOutboundTracker( + ctx context.Context, + chain chains.Chain, + nonce uint64, +) (*types.OutboundTracker, error) { + in := &types.QueryGetOutboundTrackerRequest{ChainID: chain.ChainId, Nonce: nonce} + + resp, err := c.client.crosschain.OutboundTracker(ctx, in) + if err != nil { + return nil, err + } + + return &resp.OutboundTracker, nil +} + +// GetInboundTrackersForChain returns the inbound trackers for a chain +func (c *Client) GetInboundTrackersForChain(ctx context.Context, chainID int64) ([]types.InboundTracker, error) { + in := &types.QueryAllInboundTrackerByChainRequest{ChainId: chainID} + + resp, err := c.client.crosschain.InboundTrackerAllByChain(ctx, in) + if err != nil { + return nil, err + } + + return resp.InboundTracker, nil +} + +// GetAllOutboundTrackerByChain returns all outbound trackers for a chain +func (c *Client) GetAllOutboundTrackerByChain( + ctx context.Context, + chainID int64, + order interfaces.Order, +) ([]types.OutboundTracker, error) { + in := &types.QueryAllOutboundTrackerByChainRequest{ + Chain: chainID, + Pagination: &query.PageRequest{ + Key: nil, + Offset: 0, + Limit: 2000, + CountTotal: false, + Reverse: false, + }, + } + + resp, err := c.client.crosschain.OutboundTrackerAllByChain(ctx, in) + if err != nil { + return nil, errors.Wrap(err, "failed to get all outbound trackers") + } + + if order == interfaces.Ascending { + sort.SliceStable(resp.OutboundTracker, func(i, j int) bool { + return resp.OutboundTracker[i].Nonce < resp.OutboundTracker[j].Nonce + }) + } else if order == interfaces.Descending { + sort.SliceStable(resp.OutboundTracker, func(i, j int) bool { + return resp.OutboundTracker[i].Nonce > resp.OutboundTracker[j].Nonce + }) + } + + return resp.OutboundTracker, nil +} diff --git a/zetaclient/zetacore/client_query_ethermint.go b/zetaclient/zetacore/client_query_ethermint.go new file mode 100644 index 0000000000..edb8987360 --- /dev/null +++ b/zetaclient/zetacore/client_query_ethermint.go @@ -0,0 +1,23 @@ +package zetacore + +import ( + "context" + "fmt" + + "cosmossdk.io/errors" + feemarkettypes "github.com/evmos/ethermint/x/feemarket/types" +) + +// GetBaseGasPrice returns the base gas price +func (c *Client) GetBaseGasPrice(ctx context.Context) (int64, error) { + resp, err := c.client.fees.Params(ctx, &feemarkettypes.QueryParamsRequest{}) + if err != nil { + return 0, errors.Wrap(err, "failed to get base gas price") + } + + if resp.Params.BaseFee.IsNil() { + return 0, fmt.Errorf("base fee is nil") + } + + return resp.Params.BaseFee.Int64(), nil +} diff --git a/zetaclient/zetacore/client_query_lightclient.go b/zetaclient/zetacore/client_query_lightclient.go new file mode 100644 index 0000000000..f5999a5d2a --- /dev/null +++ b/zetaclient/zetacore/client_query_lightclient.go @@ -0,0 +1,57 @@ +package zetacore + +import ( + "context" + + "cosmossdk.io/errors" + + "github.com/zeta-chain/zetacore/pkg/proofs" + "github.com/zeta-chain/zetacore/x/lightclient/types" +) + +// GetBlockHeaderEnabledChains returns the enabled chains for block headers +func (c *Client) GetBlockHeaderEnabledChains(ctx context.Context) ([]types.HeaderSupportedChain, error) { + resp, err := c.client.light.HeaderEnabledChains(ctx, &types.QueryHeaderEnabledChainsRequest{}) + if err != nil { + return []types.HeaderSupportedChain{}, err + } + + return resp.HeaderEnabledChains, nil +} + +// GetBlockHeaderChainState returns the block header chain state +func (c *Client) GetBlockHeaderChainState(ctx context.Context, chainID int64) (*types.ChainState, error) { + in := &types.QueryGetChainStateRequest{ChainId: chainID} + + resp, err := c.client.light.ChainState(ctx, in) + if err != nil { + return nil, errors.Wrap(err, "failed to get chain state") + } + + return resp.ChainState, nil +} + +// Prove returns whether a proof is valid +func (c *Client) Prove( + ctx context.Context, + blockHash string, + txHash string, + txIndex int64, + proof *proofs.Proof, + chainID int64, +) (bool, error) { + in := &types.QueryProveRequest{ + BlockHash: blockHash, + TxIndex: txIndex, + Proof: proof, + ChainId: chainID, + TxHash: txHash, + } + + resp, err := c.client.light.Prove(ctx, in) + if err != nil { + return false, errors.Wrap(err, "failed to prove") + } + + return resp.Valid, nil +} diff --git a/zetaclient/zetacore/client_query_observer.go b/zetaclient/zetacore/client_query_observer.go new file mode 100644 index 0000000000..da4fcbad7a --- /dev/null +++ b/zetaclient/zetacore/client_query_observer.go @@ -0,0 +1,215 @@ +package zetacore + +import ( + "context" + + "cosmossdk.io/errors" + + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/retry" + "github.com/zeta-chain/zetacore/x/observer/types" +) + +// GetCrosschainFlags returns the crosschain flags +func (c *Client) GetCrosschainFlags(ctx context.Context) (types.CrosschainFlags, error) { + resp, err := c.client.observer.CrosschainFlags(ctx, &types.QueryGetCrosschainFlagsRequest{}) + if err != nil { + return types.CrosschainFlags{}, err + } + + return resp.CrosschainFlags, nil +} + +// GetSupportedChains returns the supported chains +func (c *Client) GetSupportedChains(ctx context.Context) ([]chains.Chain, error) { + resp, err := c.client.observer.SupportedChains(ctx, &types.QuerySupportedChains{}) + if err != nil { + return nil, errors.Wrap(err, "failed to get supported chains") + } + + return resp.GetChains(), nil +} + +// GetChainParams returns all the chain params +func (c *Client) GetChainParams(ctx context.Context) ([]*types.ChainParams, error) { + in := &types.QueryGetChainParamsRequest{} + + resp, err := retry.DoTypedWithRetry(func() (*types.QueryGetChainParamsResponse, error) { + return c.client.observer.GetChainParams(ctx, in) + }) + + if err != nil { + return nil, errors.Wrap(err, "failed to get chain params") + } + + return resp.ChainParams.ChainParams, nil +} + +// GetChainParamsForChainID returns the chain params for a given chain ID +func (c *Client) GetChainParamsForChainID( + ctx context.Context, + externalChainID int64, +) (*types.ChainParams, error) { + in := &types.QueryGetChainParamsForChainRequest{ChainId: externalChainID} + + resp, err := c.client.observer.GetChainParamsForChain(ctx, in) + if err != nil { + return &types.ChainParams{}, err + } + + return resp.ChainParams, nil +} + +// GetObserverList returns the list of observers +func (c *Client) GetObserverList(ctx context.Context) ([]string, error) { + in := &types.QueryObserverSet{} + + resp, err := retry.DoTypedWithRetry(func() (*types.QueryObserverSetResponse, error) { + return c.client.observer.ObserverSet(ctx, in) + }) + + if err != nil { + return nil, errors.Wrap(err, "failed to get observer list") + } + + return resp.Observers, nil +} + +// GetBallotByID returns a ballot by ID +func (c *Client) GetBallotByID(ctx context.Context, id string) (*types.QueryBallotByIdentifierResponse, error) { + in := &types.QueryBallotByIdentifierRequest{BallotIdentifier: id} + + return c.client.observer.BallotByIdentifier(ctx, in) +} + +// GetNonceByChain returns the nonce by chain +func (c *Client) GetNonceByChain(ctx context.Context, chain chains.Chain) (types.ChainNonces, error) { + in := &types.QueryGetChainNoncesRequest{Index: chain.ChainName.String()} + + resp, err := c.client.observer.ChainNonces(ctx, in) + if err != nil { + return types.ChainNonces{}, errors.Wrap(err, "failed to get nonce by chain") + } + + return resp.ChainNonces, nil +} + +// GetKeyGen returns the keygen +func (c *Client) GetKeyGen(ctx context.Context) (*types.Keygen, error) { + in := &types.QueryGetKeygenRequest{} + + resp, err := retry.DoTypedWithRetry(func() (*types.QueryGetKeygenResponse, error) { + return c.client.observer.Keygen(ctx, in) + }) + + if err != nil { + return nil, errors.Wrap(err, "failed to get keygen") + } + + return resp.GetKeygen(), nil +} + +// GetAllNodeAccounts returns all node accounts +func (c *Client) GetAllNodeAccounts(ctx context.Context) ([]*types.NodeAccount, error) { + resp, err := c.client.observer.NodeAccountAll(ctx, &types.QueryAllNodeAccountRequest{}) + if err != nil { + return nil, errors.Wrap(err, "failed to get all node accounts") + } + + c.logger.Debug().Int("node_account.len", len(resp.NodeAccount)).Msg("GetAllNodeAccounts: OK") + + return resp.NodeAccount, nil +} + +// GetBallot returns a ballot by ID +func (c *Client) GetBallot( + ctx context.Context, + ballotIdentifier string, +) (*types.QueryBallotByIdentifierResponse, error) { + in := &types.QueryBallotByIdentifierRequest{BallotIdentifier: ballotIdentifier} + + resp, err := c.client.observer.BallotByIdentifier(ctx, in) + if err != nil { + return nil, errors.Wrap(err, "failed to get ballot") + } + + return resp, nil +} + +// GetCurrentTSS returns the current TSS +func (c *Client) GetCurrentTSS(ctx context.Context) (types.TSS, error) { + resp, err := c.client.observer.TSS(ctx, &types.QueryGetTSSRequest{}) + if err != nil { + return types.TSS{}, errors.Wrap(err, "failed to get current tss") + } + + return resp.TSS, nil +} + +// GetEVMTSSAddress returns the EVM TSS address. +func (c *Client) GetEVMTSSAddress(ctx context.Context) (string, error) { + resp, err := c.client.observer.GetTssAddress(ctx, &types.QueryGetTssAddressRequest{}) + if err != nil { + return "", errors.Wrap(err, "failed to get eth tss address") + } + + return resp.Eth, nil +} + +// GetBTCTSSAddress returns the BTC TSS address +func (c *Client) GetBTCTSSAddress(ctx context.Context, chainID int64) (string, error) { + in := &types.QueryGetTssAddressRequest{BitcoinChainId: chainID} + + resp, err := c.client.observer.GetTssAddress(ctx, in) + if err != nil { + return "", errors.Wrap(err, "failed to get btc tss address") + } + return resp.Btc, nil +} + +// GetTSSHistory returns the TSS history +func (c *Client) GetTSSHistory(ctx context.Context) ([]types.TSS, error) { + resp, err := c.client.observer.TssHistory(ctx, &types.QueryTssHistoryRequest{}) + if err != nil { + return nil, errors.Wrap(err, "failed to get tss history") + } + + return resp.TssList, nil +} + +// GetPendingNonces returns the pending nonces +func (c *Client) GetPendingNonces(ctx context.Context) (*types.QueryAllPendingNoncesResponse, error) { + resp, err := c.client.observer.PendingNoncesAll(ctx, &types.QueryAllPendingNoncesRequest{}) + if err != nil { + return nil, errors.Wrap(err, "failed to get pending nonces") + } + + return resp, nil +} + +// GetPendingNoncesByChain returns the pending nonces for a chain and current tss address +func (c *Client) GetPendingNoncesByChain(ctx context.Context, chainID int64) (types.PendingNonces, error) { + in := &types.QueryPendingNoncesByChainRequest{ChainId: chainID} + + resp, err := c.client.observer.PendingNoncesByChain(ctx, in) + if err != nil { + return types.PendingNonces{}, errors.Wrap(err, "failed to get pending nonces by chain") + } + + return resp.PendingNonces, nil +} + +// HasVoted returns whether an observer has voted +func (c *Client) HasVoted(ctx context.Context, ballotIndex string, voterAddress string) (bool, error) { + in := &types.QueryHasVotedRequest{ + BallotIdentifier: ballotIndex, + VoterAddress: voterAddress, + } + + resp, err := c.client.observer.HasVoted(ctx, in) + if err != nil { + return false, errors.Wrap(err, "failed to check if observer has voted") + } + + return resp.HasVoted, nil +} diff --git a/zetaclient/zetacore/client_query_tendermint.go b/zetaclient/zetacore/client_query_tendermint.go new file mode 100644 index 0000000000..adb38d0bab --- /dev/null +++ b/zetaclient/zetacore/client_query_tendermint.go @@ -0,0 +1,35 @@ +package zetacore + +import ( + "context" + + "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" + + "github.com/zeta-chain/zetacore/pkg/retry" +) + +// GetLatestZetaBlock returns the latest zeta block +func (c *Client) GetLatestZetaBlock(ctx context.Context) (*tmservice.Block, error) { + res, err := c.client.tendermint.GetLatestBlock(ctx, &tmservice.GetLatestBlockRequest{}) + if err != nil { + return nil, errors.Wrap(err, "failed to get latest zeta block") + } + + return res.SdkBlock, nil +} + +// GetNodeInfo returns the node info +func (c *Client) GetNodeInfo(ctx context.Context) (*tmservice.GetNodeInfoResponse, error) { + var err error + + res, err := retry.DoTypedWithRetry(func() (*tmservice.GetNodeInfoResponse, error) { + return c.client.tendermint.GetNodeInfo(ctx, &tmservice.GetNodeInfoRequest{}) + }) + + if err != nil { + return nil, errors.Wrap(err, "failed to get node info") + } + + return res, nil +} diff --git a/zetaclient/zetacore/query_test.go b/zetaclient/zetacore/client_query_test.go similarity index 57% rename from zetaclient/zetacore/query_test.go rename to zetaclient/zetacore/client_query_test.go index baba533cfa..35dcc43eec 100644 --- a/zetaclient/zetacore/query_test.go +++ b/zetaclient/zetacore/client_query_test.go @@ -1,18 +1,27 @@ package zetacore import ( - authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + "context" "net" "testing" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + + abci "github.com/cometbft/cometbft/abci/types" tmtypes "github.com/cometbft/cometbft/proto/tendermint/types" + cosmosclient "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" + "github.com/cosmos/cosmos-sdk/testutil/mock" "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" feemarkettypes "github.com/evmos/ethermint/x/feemarket/types" + "github.com/golang/mock/gomock" + "github.com/rs/zerolog" "github.com/stretchr/testify/require" + keyinterfaces "github.com/zeta-chain/zetacore/zetaclient/keys/interfaces" "go.nhat.io/grpcmock" "go.nhat.io/grpcmock/planner" @@ -20,50 +29,147 @@ import ( "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/testutil/sample" - crosschainTypes "github.com/zeta-chain/zetacore/x/crosschain/types" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" lightclienttypes "github.com/zeta-chain/zetacore/x/lightclient/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" "github.com/zeta-chain/zetacore/zetaclient/keys" - "github.com/zeta-chain/zetacore/zetaclient/metrics" "github.com/zeta-chain/zetacore/zetaclient/testutils/mocks" ) -func setupMockServer(t *testing.T, serviceFunc any, method string, input any, expectedOutput any) *grpcmock.Server { +const skipMethod = "skip" + +// setupMockServer setup mock zetacore GRPC server +func setupMockServer( + t *testing.T, + serviceFunc any, method string, input any, expectedOutput any, + extra ...grpcmock.ServerOption, +) *grpcmock.Server { listener, err := net.Listen("tcp", "127.0.0.1:9090") require.NoError(t, err) - server := grpcmock.MockUnstartedServer( + opts := []grpcmock.ServerOption{ grpcmock.RegisterService(serviceFunc), grpcmock.WithPlanner(planner.FirstMatch()), grpcmock.WithListener(listener), - func(s *grpcmock.Server) { + } + + opts = append(opts, extra...) + + if method != skipMethod { + opts = append(opts, func(s *grpcmock.Server) { s.ExpectUnary(method). UnlimitedTimes(). WithPayload(input). Return(expectedOutput) - }, - )(t) + }) + } + + server := grpcmock.MockUnstartedServer(opts...)(t) + + server.Serve() + + t.Cleanup(func() { + require.NoError(t, server.Close()) + }) return server } -func closeMockServer(t *testing.T, server *grpcmock.Server) { - err := server.Close() - require.NoError(t, err) +func withDummyServer(zetaBlockHeight int64) []grpcmock.ServerOption { + return []grpcmock.ServerOption{ + grpcmock.RegisterService(crosschaintypes.RegisterQueryServer), + grpcmock.RegisterService(crosschaintypes.RegisterMsgServer), + grpcmock.RegisterService(feemarkettypes.RegisterQueryServer), + grpcmock.RegisterService(authtypes.RegisterQueryServer), + grpcmock.RegisterService(abci.RegisterABCIApplicationServer), + func(s *grpcmock.Server) { + // Block Height + s.ExpectUnary("/zetachain.zetacore.crosschain.Query/LastZetaHeight"). + UnlimitedTimes(). + Return(crosschaintypes.QueryLastZetaHeightResponse{Height: zetaBlockHeight}) + + // London Base Fee + s.ExpectUnary("/ethermint.feemarket.v1.Query/Params"). + UnlimitedTimes(). + Return(feemarkettypes.QueryParamsResponse{ + Params: feemarkettypes.Params{BaseFee: types.NewInt(100)}, + }) + }, + } } -func setupZetacoreClient() (*Client, error) { - return NewClient( - &keys.Keys{}, - "127.0.0.1", - testSigner, - "zetachain_7000-1", +type clientTestConfig struct { + keys keyinterfaces.ObserverKeys + opts []Opt +} + +type clientTestOpt func(*clientTestConfig) + +func withObserverKeys(keys keyinterfaces.ObserverKeys) clientTestOpt { + return func(cfg *clientTestConfig) { cfg.keys = keys } +} + +func withDefaultObserverKeys() clientTestOpt { + var ( + key = mocks.TestKeyringPair + address = types.AccAddress(key.PubKey().Address().Bytes()) + keyRing = mocks.NewKeyring() + ) + + return withObserverKeys(keys.NewKeysWithKeybase(keyRing, address, testSigner, "")) +} + +func withTendermint(client cosmosclient.TendermintRPC) clientTestOpt { + return func(cfg *clientTestConfig) { cfg.opts = append(cfg.opts, WithTendermintClient(client)) } +} + +func withAccountRetriever(t *testing.T, accNum uint64, accSeq uint64) clientTestOpt { + ctrl := gomock.NewController(t) + ac := mock.NewMockAccountRetriever(ctrl) + ac.EXPECT(). + GetAccountNumberSequence(gomock.Any(), gomock.Any()). + AnyTimes(). + Return(accNum, accSeq, nil) + + return func(cfg *clientTestConfig) { + cfg.opts = append(cfg.opts, WithCustomAccountRetriever(ac)) + } +} + +func setupZetacoreClient(t *testing.T, opts ...clientTestOpt) *Client { + const ( + chainIP = "127.0.0.1" + signer = testSigner + chainID = "zetachain_7000-1" + ) + + var cfg clientTestConfig + for _, opt := range opts { + opt(&cfg) + } + + if cfg.keys == nil { + cfg.keys = &keys.Keys{} + } + + c, err := NewClient( + cfg.keys, + chainIP, signer, + chainID, false, - &metrics.TelemetryServer{}) + zerolog.Nop(), + cfg.opts..., + ) + + require.NoError(t, err) + + return c } func TestZetacore_GetBallot(t *testing.T) { + ctx := context.Background() + expectedOutput := observertypes.QueryBallotByIdentifierResponse{ BallotIdentifier: "123", Voters: nil, @@ -72,19 +178,18 @@ func TestZetacore_GetBallot(t *testing.T) { } input := observertypes.QueryBallotByIdentifierRequest{BallotIdentifier: "123"} method := "/zetachain.zetacore.observer.Query/BallotByIdentifier" - server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetBallotByID("123") + resp, err := client.GetBallotByID(ctx, "123") require.NoError(t, err) require.Equal(t, expectedOutput, *resp) } func TestZetacore_GetCrosschainFlags(t *testing.T) { + ctx := context.Background() + expectedOutput := observertypes.QueryGetCrosschainFlagsResponse{CrosschainFlags: observertypes.CrosschainFlags{ IsInboundEnabled: true, IsOutboundEnabled: false, @@ -92,42 +197,40 @@ func TestZetacore_GetCrosschainFlags(t *testing.T) { }} input := observertypes.QueryGetCrosschainFlagsRequest{} method := "/zetachain.zetacore.observer.Query/CrosschainFlags" - server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetCrosschainFlags() + resp, err := client.GetCrosschainFlags(ctx) require.NoError(t, err) require.Equal(t, expectedOutput.CrosschainFlags, resp) } func TestZetacore_GetRateLimiterFlags(t *testing.T) { + ctx := context.Background() + // create sample flags rateLimiterFlags := sample.RateLimiterFlags() - expectedOutput := crosschainTypes.QueryRateLimiterFlagsResponse{ + expectedOutput := crosschaintypes.QueryRateLimiterFlagsResponse{ RateLimiterFlags: rateLimiterFlags, } // setup mock server - input := crosschainTypes.QueryRateLimiterFlagsRequest{} + input := crosschaintypes.QueryRateLimiterFlagsRequest{} method := "/zetachain.zetacore.crosschain.Query/RateLimiterFlags" - server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, crosschaintypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) // query - resp, err := client.GetRateLimiterFlags() + resp, err := client.GetRateLimiterFlags(ctx) require.NoError(t, err) require.Equal(t, expectedOutput.RateLimiterFlags, resp) } func TestZetacore_HeaderEnabledChains(t *testing.T) { + ctx := context.Background() + expectedOutput := lightclienttypes.QueryHeaderEnabledChainsResponse{ HeaderEnabledChains: []lightclienttypes.HeaderSupportedChain{ { @@ -142,19 +245,18 @@ func TestZetacore_HeaderEnabledChains(t *testing.T) { } input := lightclienttypes.QueryHeaderEnabledChainsRequest{} method := "/zetachain.zetacore.lightclient.Query/HeaderEnabledChains" - server := setupMockServer(t, lightclienttypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, lightclienttypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetBlockHeaderEnabledChains() + resp, err := client.GetBlockHeaderEnabledChains(ctx) require.NoError(t, err) require.Equal(t, expectedOutput.HeaderEnabledChains, resp) } func TestZetacore_GetChainParamsForChainID(t *testing.T) { + ctx := context.Background() + expectedOutput := observertypes.QueryGetChainParamsForChainResponse{ChainParams: &observertypes.ChainParams{ ChainId: 123, BallotThreshold: types.ZeroDec(), @@ -162,19 +264,18 @@ func TestZetacore_GetChainParamsForChainID(t *testing.T) { }} input := observertypes.QueryGetChainParamsForChainRequest{ChainId: 123} method := "/zetachain.zetacore.observer.Query/GetChainParamsForChain" - server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetChainParamsForChainID(123) + resp, err := client.GetChainParamsForChainID(ctx, 123) require.NoError(t, err) require.Equal(t, expectedOutput.ChainParams, resp) } func TestZetacore_GetChainParams(t *testing.T) { + ctx := context.Background() + expectedOutput := observertypes.QueryGetChainParamsResponse{ChainParams: &observertypes.ChainParamsList{ ChainParams: []*observertypes.ChainParams{ { @@ -186,19 +287,18 @@ func TestZetacore_GetChainParams(t *testing.T) { }} input := observertypes.QueryGetChainParamsRequest{} method := "/zetachain.zetacore.observer.Query/GetChainParams" - server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetChainParams() + resp, err := client.GetChainParams(ctx) require.NoError(t, err) require.Equal(t, expectedOutput.ChainParams.ChainParams, resp) } func TestZetacore_GetUpgradePlan(t *testing.T) { + ctx := context.Background() + expectedOutput := upgradetypes.QueryCurrentPlanResponse{ Plan: &upgradetypes.Plan{ Name: "big upgrade", @@ -207,83 +307,79 @@ func TestZetacore_GetUpgradePlan(t *testing.T) { } input := upgradetypes.QueryCurrentPlanRequest{} method := "/cosmos.upgrade.v1beta1.Query/CurrentPlan" - server := setupMockServer(t, upgradetypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, upgradetypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetUpgradePlan() + resp, err := client.GetUpgradePlan(ctx) require.NoError(t, err) require.Equal(t, expectedOutput.Plan, resp) } func TestZetacore_GetAllCctx(t *testing.T) { - expectedOutput := crosschainTypes.QueryAllCctxResponse{ - CrossChainTx: []*crosschainTypes.CrossChainTx{ + ctx := context.Background() + + expectedOutput := crosschaintypes.QueryAllCctxResponse{ + CrossChainTx: []*crosschaintypes.CrossChainTx{ { Index: "cross-chain4456", }, }, Pagination: nil, } - input := crosschainTypes.QueryAllCctxRequest{} + input := crosschaintypes.QueryAllCctxRequest{} method := "/zetachain.zetacore.crosschain.Query/CctxAll" - server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, crosschaintypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetAllCctx() + resp, err := client.GetAllCctx(ctx) require.NoError(t, err) require.Equal(t, expectedOutput.CrossChainTx, resp) } func TestZetacore_GetCctxByHash(t *testing.T) { - expectedOutput := crosschainTypes.QueryGetCctxResponse{CrossChainTx: &crosschainTypes.CrossChainTx{ + ctx := context.Background() + + expectedOutput := crosschaintypes.QueryGetCctxResponse{CrossChainTx: &crosschaintypes.CrossChainTx{ Index: "9c8d02b6956b9c78ecb6090a8160faaa48e7aecfd0026fcdf533721d861436a3", }} - input := crosschainTypes.QueryGetCctxRequest{ + input := crosschaintypes.QueryGetCctxRequest{ Index: "9c8d02b6956b9c78ecb6090a8160faaa48e7aecfd0026fcdf533721d861436a3", } method := "/zetachain.zetacore.crosschain.Query/Cctx" - server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, crosschaintypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetCctxByHash("9c8d02b6956b9c78ecb6090a8160faaa48e7aecfd0026fcdf533721d861436a3") + resp, err := client.GetCctxByHash(ctx, "9c8d02b6956b9c78ecb6090a8160faaa48e7aecfd0026fcdf533721d861436a3") require.NoError(t, err) require.Equal(t, expectedOutput.CrossChainTx, resp) } func TestZetacore_GetCctxByNonce(t *testing.T) { - expectedOutput := crosschainTypes.QueryGetCctxResponse{CrossChainTx: &crosschainTypes.CrossChainTx{ + ctx := context.Background() + + expectedOutput := crosschaintypes.QueryGetCctxResponse{CrossChainTx: &crosschaintypes.CrossChainTx{ Index: "9c8d02b6956b9c78ecb6090a8160faaa48e7aecfd0026fcdf533721d861436a3", }} - input := crosschainTypes.QueryGetCctxByNonceRequest{ + input := crosschaintypes.QueryGetCctxByNonceRequest{ ChainID: 7000, Nonce: 55, } method := "/zetachain.zetacore.crosschain.Query/CctxByNonce" - server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, crosschaintypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetCctxByNonce(7000, 55) + resp, err := client.GetCctxByNonce(ctx, 7000, 55) require.NoError(t, err) require.Equal(t, expectedOutput.CrossChainTx, resp) } func TestZetacore_GetObserverList(t *testing.T) { + ctx := context.Background() + expectedOutput := observertypes.QueryObserverSetResponse{ Observers: []string{ "zeta19jr7nl82lrktge35f52x9g5y5prmvchmk40zhg", @@ -293,78 +389,72 @@ func TestZetacore_GetObserverList(t *testing.T) { } input := observertypes.QueryObserverSet{} method := "/zetachain.zetacore.observer.Query/ObserverSet" - server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetObserverList() + resp, err := client.GetObserverList(ctx) require.NoError(t, err) require.Equal(t, expectedOutput.Observers, resp) } func TestZetacore_GetRateLimiterInput(t *testing.T) { - expectedOutput := crosschainTypes.QueryRateLimiterInputResponse{ + ctx := context.Background() + + expectedOutput := &crosschaintypes.QueryRateLimiterInputResponse{ Height: 10, - CctxsMissed: []*crosschainTypes.CrossChainTx{sample.CrossChainTx(t, "1-1")}, - CctxsPending: []*crosschainTypes.CrossChainTx{sample.CrossChainTx(t, "1-2")}, + CctxsMissed: []*crosschaintypes.CrossChainTx{sample.CrossChainTx(t, "1-1")}, + CctxsPending: []*crosschaintypes.CrossChainTx{sample.CrossChainTx(t, "1-2")}, TotalPending: 1, PastCctxsValue: "123456", PendingCctxsValue: "1234", LowestPendingCctxHeight: 2, } - input := crosschainTypes.QueryRateLimiterInputRequest{Window: 10} + input := crosschaintypes.QueryRateLimiterInputRequest{Window: 10} method := "/zetachain.zetacore.crosschain.Query/RateLimiterInput" - server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, crosschaintypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetRateLimiterInput(10) + resp, err := client.GetRateLimiterInput(ctx, 10) require.NoError(t, err) require.Equal(t, expectedOutput, resp) } func TestZetacore_ListPendingCctx(t *testing.T) { - expectedOutput := crosschainTypes.QueryListPendingCctxResponse{ - CrossChainTx: []*crosschainTypes.CrossChainTx{ + ctx := context.Background() + + expectedOutput := crosschaintypes.QueryListPendingCctxResponse{ + CrossChainTx: []*crosschaintypes.CrossChainTx{ { Index: "cross-chain4456", }, }, TotalPending: 1, } - input := crosschainTypes.QueryListPendingCctxRequest{ChainId: 7000} + input := crosschaintypes.QueryListPendingCctxRequest{ChainId: 7000} method := "/zetachain.zetacore.crosschain.Query/ListPendingCctx" - server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, crosschaintypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, totalPending, err := client.ListPendingCctx(7000) + resp, totalPending, err := client.ListPendingCCTX(ctx, 7000) require.NoError(t, err) require.Equal(t, expectedOutput.CrossChainTx, resp) require.Equal(t, expectedOutput.TotalPending, totalPending) } func TestZetacore_GetAbortedZetaAmount(t *testing.T) { - expectedOutput := crosschainTypes.QueryZetaAccountingResponse{AbortedZetaAmount: "1080999"} - input := crosschainTypes.QueryZetaAccountingRequest{} + ctx := context.Background() + + expectedOutput := crosschaintypes.QueryZetaAccountingResponse{AbortedZetaAmount: "1080999"} + input := crosschaintypes.QueryZetaAccountingRequest{} method := "/zetachain.zetacore.crosschain.Query/ZetaAccounting" - server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, crosschaintypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetAbortedZetaAmount() + resp, err := client.GetAbortedZetaAmount(ctx) require.NoError(t, err) require.Equal(t, expectedOutput.AbortedZetaAmount, resp) } @@ -374,6 +464,8 @@ func TestZetacore_GetGenesisSupply(t *testing.T) { } func TestZetacore_GetZetaTokenSupplyOnNode(t *testing.T) { + ctx := context.Background() + expectedOutput := banktypes.QuerySupplyOfResponse{ Amount: types.Coin{ Denom: config.BaseDenom, @@ -381,46 +473,38 @@ func TestZetacore_GetZetaTokenSupplyOnNode(t *testing.T) { }} input := banktypes.QuerySupplyOfRequest{Denom: config.BaseDenom} method := "/cosmos.bank.v1beta1.Query/SupplyOf" - server := setupMockServer(t, banktypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, banktypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetZetaTokenSupplyOnNode() + resp, err := client.GetZetaTokenSupplyOnNode(ctx) require.NoError(t, err) require.Equal(t, expectedOutput.GetAmount().Amount, resp) } -func TestZetacore_GetLastBlockHeight(t *testing.T) { - expectedOutput := crosschainTypes.QueryAllLastBlockHeightResponse{ - LastBlockHeight: []*crosschainTypes.LastBlockHeight{ - { - Index: "test12345", - Chain: "7000", - LastOutboundHeight: 32345, - LastInboundHeight: 23623, - }, - }, - } - input := crosschainTypes.QueryAllLastBlockHeightRequest{} - method := "/zetachain.zetacore.crosschain.Query/LastBlockHeightAll" - server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) +func TestZetacore_GetBlockHeight(t *testing.T) { + ctx := context.Background() - client, err := setupZetacoreClient() - require.NoError(t, err) + method := "/zetachain.zetacore.crosschain.Query/LastZetaHeight" + input := &crosschaintypes.QueryLastZetaHeightRequest{} + output := &crosschaintypes.QueryLastZetaHeightResponse{Height: 12345} + + setupMockServer(t, crosschaintypes.RegisterQueryServer, method, input, output) + + client := setupZetacoreClient(t, + withDefaultObserverKeys(), + ) t.Run("last block height", func(t *testing.T) { - resp, err := client.GetLastBlockHeight() + height, err := client.GetBlockHeight(ctx) require.NoError(t, err) - require.Equal(t, expectedOutput.LastBlockHeight, resp) + require.Equal(t, int64(12345), height) }) } func TestZetacore_GetLatestZetaBlock(t *testing.T) { + ctx := context.Background() + expectedOutput := tmservice.GetLatestBlockResponse{ SdkBlock: &tmservice.Block{ Header: tmservice.Header{}, @@ -431,56 +515,36 @@ func TestZetacore_GetLatestZetaBlock(t *testing.T) { } input := tmservice.GetLatestBlockRequest{} method := "/cosmos.base.tendermint.v1beta1.Service/GetLatestBlock" - server := setupMockServer(t, tmservice.RegisterServiceServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, tmservice.RegisterServiceServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetLatestZetaBlock() + resp, err := client.GetLatestZetaBlock(ctx) require.NoError(t, err) require.Equal(t, expectedOutput.SdkBlock, resp) } func TestZetacore_GetNodeInfo(t *testing.T) { + ctx := context.Background() + expectedOutput := tmservice.GetNodeInfoResponse{ DefaultNodeInfo: nil, ApplicationVersion: &tmservice.VersionInfo{}, } input := tmservice.GetNodeInfoRequest{} method := "/cosmos.base.tendermint.v1beta1.Service/GetNodeInfo" - server := setupMockServer(t, tmservice.RegisterServiceServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, tmservice.RegisterServiceServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetNodeInfo() + resp, err := client.GetNodeInfo(ctx) require.NoError(t, err) require.Equal(t, expectedOutput, *resp) } -func TestZetacore_GetZetaBlockHeight(t *testing.T) { - expectedOutput := crosschainTypes.QueryLastZetaHeightResponse{Height: 12345} - input := crosschainTypes.QueryLastZetaHeightRequest{} - method := "/zetachain.zetacore.crosschain.Query/LastZetaHeight" - server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) - - client, err := setupZetacoreClient() - require.NoError(t, err) - - t.Run("get zeta block height success", func(t *testing.T) { - resp, err := client.GetBlockHeight() - require.NoError(t, err) - require.Equal(t, expectedOutput.Height, resp) - }) -} - func TestZetacore_GetBaseGasPrice(t *testing.T) { + ctx := context.Background() + expectedOutput := feemarkettypes.QueryParamsResponse{ Params: feemarkettypes.Params{ BaseFee: types.NewInt(23455), @@ -488,19 +552,18 @@ func TestZetacore_GetBaseGasPrice(t *testing.T) { } input := feemarkettypes.QueryParamsRequest{} method := "/ethermint.feemarket.v1.Query/Params" - server := setupMockServer(t, feemarkettypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, feemarkettypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetBaseGasPrice() + resp, err := client.GetBaseGasPrice(ctx) require.NoError(t, err) require.Equal(t, expectedOutput.Params.BaseFee.Int64(), resp) } func TestZetacore_GetNonceByChain(t *testing.T) { + ctx := context.Background() + chain := chains.BscMainnet expectedOutput := observertypes.QueryGetChainNoncesResponse{ ChainNonces: observertypes.ChainNonces{ @@ -514,19 +577,18 @@ func TestZetacore_GetNonceByChain(t *testing.T) { } input := observertypes.QueryGetChainNoncesRequest{Index: chain.ChainName.String()} method := "/zetachain.zetacore.observer.Query/ChainNonces" - server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetNonceByChain(chain) + resp, err := client.GetNonceByChain(ctx, chain) require.NoError(t, err) require.Equal(t, expectedOutput.ChainNonces, resp) } func TestZetacore_GetAllNodeAccounts(t *testing.T) { + ctx := context.Background() + expectedOutput := observertypes.QueryAllNodeAccountResponse{ NodeAccount: []*observertypes.NodeAccount{ { @@ -539,19 +601,18 @@ func TestZetacore_GetAllNodeAccounts(t *testing.T) { } input := observertypes.QueryAllNodeAccountRequest{} method := "/zetachain.zetacore.observer.Query/NodeAccountAll" - server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetAllNodeAccounts() + resp, err := client.GetAllNodeAccounts(ctx) require.NoError(t, err) require.Equal(t, expectedOutput.NodeAccount, resp) } func TestZetacore_GetKeyGen(t *testing.T) { + ctx := context.Background() + expectedOutput := observertypes.QueryGetKeygenResponse{ Keygen: &observertypes.Keygen{ Status: observertypes.KeygenStatus_KeyGenSuccess, @@ -560,40 +621,38 @@ func TestZetacore_GetKeyGen(t *testing.T) { }} input := observertypes.QueryGetKeygenRequest{} method := "/zetachain.zetacore.observer.Query/Keygen" - server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetKeyGen() + resp, err := client.GetKeyGen(ctx) require.NoError(t, err) require.Equal(t, expectedOutput.Keygen, resp) } func TestZetacore_GetBallotByID(t *testing.T) { + ctx := context.Background() + expectedOutput := observertypes.QueryBallotByIdentifierResponse{ BallotIdentifier: "ballot1235", } input := observertypes.QueryBallotByIdentifierRequest{BallotIdentifier: "ballot1235"} method := "/zetachain.zetacore.observer.Query/BallotByIdentifier" - server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetBallot("ballot1235") + resp, err := client.GetBallot(ctx, "ballot1235") require.NoError(t, err) require.Equal(t, expectedOutput, *resp) } func TestZetacore_GetInboundTrackersForChain(t *testing.T) { + ctx := context.Background() + chainID := chains.BscMainnet.ChainId - expectedOutput := crosschainTypes.QueryAllInboundTrackerByChainResponse{ - InboundTracker: []crosschainTypes.InboundTracker{ + expectedOutput := crosschaintypes.QueryAllInboundTrackerByChainResponse{ + InboundTracker: []crosschaintypes.InboundTracker{ { ChainId: chainID, TxHash: "DC76A6DCCC3AA62E89E69042ADC44557C50D59E4D3210C37D78DC8AE49B3B27F", @@ -601,21 +660,20 @@ func TestZetacore_GetInboundTrackersForChain(t *testing.T) { }, }, } - input := crosschainTypes.QueryAllInboundTrackerByChainRequest{ChainId: chainID} + input := crosschaintypes.QueryAllInboundTrackerByChainRequest{ChainId: chainID} method := "/zetachain.zetacore.crosschain.Query/InboundTrackerAllByChain" - server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, crosschaintypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetInboundTrackersForChain(chainID) + resp, err := client.GetInboundTrackersForChain(ctx, chainID) require.NoError(t, err) require.Equal(t, expectedOutput.InboundTracker, resp) } func TestZetacore_GetCurrentTss(t *testing.T) { + ctx := context.Background() + expectedOutput := observertypes.QueryGetTSSResponse{ TSS: observertypes.TSS{ TssPubkey: "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", @@ -627,57 +685,54 @@ func TestZetacore_GetCurrentTss(t *testing.T) { } input := observertypes.QueryGetTSSRequest{} method := "/zetachain.zetacore.observer.Query/TSS" - server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetCurrentTss() + resp, err := client.GetCurrentTSS(ctx) require.NoError(t, err) require.Equal(t, expectedOutput.TSS, resp) } func TestZetacore_GetEthTssAddress(t *testing.T) { + ctx := context.Background() + expectedOutput := observertypes.QueryGetTssAddressResponse{ Eth: "0x70e967acfcc17c3941e87562161406d41676fd83", Btc: "bc1qm24wp577nk8aacckv8np465z3dvmu7ry45el6y", } input := observertypes.QueryGetTssAddressRequest{} method := "/zetachain.zetacore.observer.Query/GetTssAddress" - server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetEthTssAddress() + resp, err := client.GetEVMTSSAddress(ctx) require.NoError(t, err) require.Equal(t, expectedOutput.Eth, resp) } func TestZetacore_GetBtcTssAddress(t *testing.T) { + ctx := context.Background() + expectedOutput := observertypes.QueryGetTssAddressResponse{ Eth: "0x70e967acfcc17c3941e87562161406d41676fd83", Btc: "bc1qm24wp577nk8aacckv8np465z3dvmu7ry45el6y", } input := observertypes.QueryGetTssAddressRequest{BitcoinChainId: 8332} method := "/zetachain.zetacore.observer.Query/GetTssAddress" - server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetBtcTssAddress(8332) + resp, err := client.GetBTCTSSAddress(ctx, 8332) require.NoError(t, err) require.Equal(t, expectedOutput.Btc, resp) } func TestZetacore_GetTssHistory(t *testing.T) { + ctx := context.Background() + expectedOutput := observertypes.QueryTssHistoryResponse{ TssList: []observertypes.TSS{ { @@ -691,49 +746,46 @@ func TestZetacore_GetTssHistory(t *testing.T) { } input := observertypes.QueryTssHistoryRequest{} method := "/zetachain.zetacore.observer.Query/TssHistory" - server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetTssHistory() + resp, err := client.GetTSSHistory(ctx) require.NoError(t, err) require.Equal(t, expectedOutput.TssList, resp) } func TestZetacore_GetOutboundTracker(t *testing.T) { chain := chains.BscMainnet - expectedOutput := crosschainTypes.QueryGetOutboundTrackerResponse{ - OutboundTracker: crosschainTypes.OutboundTracker{ + expectedOutput := crosschaintypes.QueryGetOutboundTrackerResponse{ + OutboundTracker: crosschaintypes.OutboundTracker{ Index: "tracker12345", ChainId: chain.ChainId, Nonce: 456, HashList: nil, }, } - input := crosschainTypes.QueryGetOutboundTrackerRequest{ + input := crosschaintypes.QueryGetOutboundTrackerRequest{ ChainID: chain.ChainId, Nonce: 456, } method := "/zetachain.zetacore.crosschain.Query/OutboundTracker" - server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, crosschaintypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetOutboundTracker(chain, 456) + ctx := context.Background() + resp, err := client.GetOutboundTracker(ctx, chain, 456) require.NoError(t, err) require.Equal(t, expectedOutput.OutboundTracker, *resp) } func TestZetacore_GetAllOutboundTrackerByChain(t *testing.T) { + ctx := context.Background() + chain := chains.BscMainnet - expectedOutput := crosschainTypes.QueryAllOutboundTrackerByChainResponse{ - OutboundTracker: []crosschainTypes.OutboundTracker{ + expectedOutput := crosschaintypes.QueryAllOutboundTrackerByChainResponse{ + OutboundTracker: []crosschaintypes.OutboundTracker{ { Index: "tracker23456", ChainId: chain.ChainId, @@ -742,7 +794,7 @@ func TestZetacore_GetAllOutboundTrackerByChain(t *testing.T) { }, }, } - input := crosschainTypes.QueryAllOutboundTrackerByChainRequest{ + input := crosschaintypes.QueryAllOutboundTrackerByChainRequest{ Chain: chain.ChainId, Pagination: &query.PageRequest{ Key: nil, @@ -753,23 +805,22 @@ func TestZetacore_GetAllOutboundTrackerByChain(t *testing.T) { }, } method := "/zetachain.zetacore.crosschain.Query/OutboundTrackerAllByChain" - server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, crosschaintypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetAllOutboundTrackerByChain(chain.ChainId, interfaces.Ascending) + resp, err := client.GetAllOutboundTrackerByChain(ctx, chain.ChainId, interfaces.Ascending) require.NoError(t, err) require.Equal(t, expectedOutput.OutboundTracker, resp) - resp, err = client.GetAllOutboundTrackerByChain(chain.ChainId, interfaces.Descending) + resp, err = client.GetAllOutboundTrackerByChain(ctx, chain.ChainId, interfaces.Descending) require.NoError(t, err) require.Equal(t, expectedOutput.OutboundTracker, resp) } func TestZetacore_GetPendingNoncesByChain(t *testing.T) { + ctx := context.Background() + expectedOutput := observertypes.QueryPendingNoncesByChainResponse{ PendingNonces: observertypes.PendingNonces{ NonceLow: 0, @@ -780,19 +831,18 @@ func TestZetacore_GetPendingNoncesByChain(t *testing.T) { } input := observertypes.QueryPendingNoncesByChainRequest{ChainId: chains.Ethereum.ChainId} method := "/zetachain.zetacore.observer.Query/PendingNoncesByChain" - server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetPendingNoncesByChain(chains.Ethereum.ChainId) + resp, err := client.GetPendingNoncesByChain(ctx, chains.Ethereum.ChainId) require.NoError(t, err) require.Equal(t, expectedOutput.PendingNonces, resp) } func TestZetacore_GetBlockHeaderChainState(t *testing.T) { + ctx := context.Background() + chainID := chains.BscMainnet.ChainId expectedOutput := lightclienttypes.QueryGetChainStateResponse{ChainState: &lightclienttypes.ChainState{ ChainId: chainID, @@ -802,19 +852,18 @@ func TestZetacore_GetBlockHeaderChainState(t *testing.T) { }} input := lightclienttypes.QueryGetChainStateRequest{ChainId: chainID} method := "/zetachain.zetacore.lightclient.Query/ChainState" - server := setupMockServer(t, lightclienttypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, lightclienttypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetBlockHeaderChainState(chainID) + resp, err := client.GetBlockHeaderChainState(ctx, chainID) require.NoError(t, err) - require.Equal(t, expectedOutput, resp) + require.Equal(t, expectedOutput.ChainState, resp) } func TestZetacore_GetSupportedChains(t *testing.T) { + ctx := context.Background() + expectedOutput := observertypes.QuerySupportedChainsResponse{ Chains: []chains.Chain{ { @@ -839,19 +888,18 @@ func TestZetacore_GetSupportedChains(t *testing.T) { } input := observertypes.QuerySupportedChains{} method := "/zetachain.zetacore.observer.Query/SupportedChains" - server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetSupportedChains() + resp, err := client.GetSupportedChains(ctx) require.NoError(t, err) require.Equal(t, expectedOutput.Chains, resp) } func TestZetacore_GetAdditionalChains(t *testing.T) { + ctx := context.Background() + expectedOutput := authoritytypes.QueryGetChainInfoResponse{ ChainInfo: authoritytypes.ChainInfo{ Chains: []chains.Chain{ @@ -862,19 +910,23 @@ func TestZetacore_GetAdditionalChains(t *testing.T) { } input := observertypes.QuerySupportedChains{} method := "/zetachain.zetacore.authority.Query/ChainInfo" - server := setupMockServer(t, authoritytypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) - client, err := setupZetacoreClient() - require.NoError(t, err) + setupMockServer(t, authoritytypes.RegisterQueryServer, method, input, expectedOutput) + + client := setupZetacoreClient(t, + withDefaultObserverKeys(), + withAccountRetriever(t, 100, 100), + withTendermint(mocks.NewSDKClientWithErr(t, nil, 0).SetBroadcastTxHash(sampleHash)), + ) - resp, err := client.GetAdditionalChains() + resp, err := client.GetAdditionalChains(ctx) require.NoError(t, err) require.Equal(t, expectedOutput.ChainInfo.Chains, resp) } func TestZetacore_GetPendingNonces(t *testing.T) { + ctx := context.Background() + expectedOutput := observertypes.QueryAllPendingNoncesResponse{ PendingNonces: []observertypes.PendingNonces{ { @@ -887,19 +939,18 @@ func TestZetacore_GetPendingNonces(t *testing.T) { } input := observertypes.QueryAllPendingNoncesRequest{} method := "/zetachain.zetacore.observer.Query/PendingNoncesAll" - server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.GetPendingNonces() + resp, err := client.GetPendingNonces(ctx) require.NoError(t, err) require.Equal(t, expectedOutput, *resp) } func TestZetacore_Prove(t *testing.T) { + ctx := context.Background() + chainId := chains.BscMainnet.ChainId txHash := "9c8d02b6956b9c78ecb6090a8160faaa48e7aecfd0026fcdf533721d861436a3" blockHash := "0000000000000000000172c9a64f86f208b867a84dc7a0b7c75be51e750ed8eb" @@ -915,38 +966,36 @@ func TestZetacore_Prove(t *testing.T) { TxIndex: int64(txIndex), } method := "/zetachain.zetacore.lightclient.Query/Prove" - server := setupMockServer(t, lightclienttypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, lightclienttypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.Prove(blockHash, txHash, int64(txIndex), nil, chainId) + resp, err := client.Prove(ctx, blockHash, txHash, int64(txIndex), nil, chainId) require.NoError(t, err) require.Equal(t, expectedOutput.Valid, resp) } func TestZetacore_HasVoted(t *testing.T) { + ctx := context.Background() + expectedOutput := observertypes.QueryHasVotedResponse{HasVoted: true} input := observertypes.QueryHasVotedRequest{ BallotIdentifier: "123456asdf", VoterAddress: "zeta1l40mm7meacx03r4lp87s9gkxfan32xnznp42u6", } method := "/zetachain.zetacore.observer.Query/HasVoted" - server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) - resp, err := client.HasVoted("123456asdf", "zeta1l40mm7meacx03r4lp87s9gkxfan32xnznp42u6") + resp, err := client.HasVoted(ctx, "123456asdf", "zeta1l40mm7meacx03r4lp87s9gkxfan32xnznp42u6") require.NoError(t, err) require.Equal(t, expectedOutput.HasVoted, resp) } func TestZetacore_GetZetaHotKeyBalance(t *testing.T) { + ctx := context.Background() + expectedOutput := banktypes.QueryBalanceResponse{ Balance: &types.Coin{ Denom: config.BaseDenom, @@ -958,22 +1007,19 @@ func TestZetacore_GetZetaHotKeyBalance(t *testing.T) { Denom: config.BaseDenom, } method := "/cosmos.bank.v1beta1.Query/Balance" - server := setupMockServer(t, banktypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) + setupMockServer(t, banktypes.RegisterQueryServer, method, input, expectedOutput) - client, err := setupZetacoreClient() - require.NoError(t, err) + client := setupZetacoreClient(t, withDefaultObserverKeys()) // should be able to get balance of signer client.keys = keys.NewKeysWithKeybase(mocks.NewKeyring(), types.AccAddress{}, "bob", "") - resp, err := client.GetZetaHotKeyBalance() + resp, err := client.GetZetaHotKeyBalance(ctx) require.NoError(t, err) require.Equal(t, expectedOutput.Balance.Amount, resp) // should return error on empty signer client.keys = keys.NewKeysWithKeybase(mocks.NewKeyring(), types.AccAddress{}, "", "") - resp, err = client.GetZetaHotKeyBalance() + resp, err = client.GetZetaHotKeyBalance(ctx) require.Error(t, err) require.Equal(t, types.ZeroInt(), resp) } diff --git a/zetaclient/zetacore/client_vote.go b/zetaclient/zetacore/client_vote.go new file mode 100644 index 0000000000..b9c0e0432a --- /dev/null +++ b/zetaclient/zetacore/client_vote.go @@ -0,0 +1,231 @@ +package zetacore + +import ( + "context" + + "github.com/pkg/errors" + "github.com/zeta-chain/go-tss/blame" + + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/proofs" + "github.com/zeta-chain/zetacore/pkg/retry" + "github.com/zeta-chain/zetacore/x/crosschain/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" + zctx "github.com/zeta-chain/zetacore/zetaclient/context" +) + +// PostVoteBlockHeader posts a vote on an observed block header +func (c *Client) PostVoteBlockHeader( + ctx context.Context, + chainID int64, + blockHash []byte, + height int64, + header proofs.HeaderData, +) (string, error) { + signerAddress := c.keys.GetOperatorAddress().String() + + msg := observertypes.NewMsgVoteBlockHeader(signerAddress, chainID, blockHash, height, header) + + authzMsg, authzSigner, err := WrapMessageWithAuthz(msg) + if err != nil { + return "", err + } + + zetaTxHash, err := retry.DoTypedWithRetry(func() (string, error) { + return c.Broadcast(ctx, DefaultGasLimit, authzMsg, authzSigner) + }) + + if err != nil { + return "", errors.Wrap(err, "unable to broadcast vote block header") + } + + return zetaTxHash, nil +} + +// PostVoteGasPrice posts a gas price vote. Returns txHash and error. +func (c *Client) PostVoteGasPrice( + ctx context.Context, + chain chains.Chain, + gasPrice uint64, + supply string, + blockNum uint64, +) (string, error) { + // apply gas price multiplier for the chain + multiplier, err := GasPriceMultiplier(chain) + if err != nil { + return "", err + } + + // #nosec G115 always in range + gasPrice = uint64(float64(gasPrice) * multiplier) + signerAddress := c.keys.GetOperatorAddress().String() + msg := types.NewMsgVoteGasPrice(signerAddress, chain.ChainId, gasPrice, supply, blockNum) + + authzMsg, authzSigner, err := WrapMessageWithAuthz(msg) + if err != nil { + return "", err + } + + hash, err := retry.DoTypedWithRetry(func() (string, error) { + return c.Broadcast(ctx, PostGasPriceGasLimit, authzMsg, authzSigner) + }) + + if err != nil { + return "", errors.Wrap(err, "unable to broadcast vote gas price") + } + + return hash, nil +} + +// PostVoteTSS sends message to vote TSS. Returns txHash and error. +func (c *Client) PostVoteTSS( + ctx context.Context, + tssPubKey string, + keyGenZetaHeight int64, + status chains.ReceiveStatus, +) (string, error) { + signerAddress := c.keys.GetOperatorAddress().String() + msg := observertypes.NewMsgVoteTSS(signerAddress, tssPubKey, keyGenZetaHeight, status) + + authzMsg, authzSigner, err := WrapMessageWithAuthz(msg) + if err != nil { + return "", err + } + + zetaTxHash, err := retry.DoTypedWithRetry(func() (string, error) { + return c.Broadcast(ctx, DefaultGasLimit, authzMsg, authzSigner) + }) + + if err != nil { + return "", errors.Wrap(err, "unable to broadcast vote for setting tss") + } + + return zetaTxHash, nil +} + +// PostVoteBlameData posts blame data message to zetacore. Returns txHash and error. +func (c *Client) PostVoteBlameData( + ctx context.Context, + blame *blame.Blame, + chainID int64, + index string, +) (string, error) { + signerAddress := c.keys.GetOperatorAddress().String() + zetaBlame := observertypes.Blame{ + Index: index, + FailureReason: blame.FailReason, + Nodes: observertypes.ConvertNodes(blame.BlameNodes), + } + msg := observertypes.NewMsgVoteBlameMsg(signerAddress, chainID, zetaBlame) + + authzMsg, authzSigner, err := WrapMessageWithAuthz(msg) + if err != nil { + return "", err + } + + var gasLimit uint64 = PostBlameDataGasLimit + + zetaTxHash, err := retry.DoTypedWithRetry(func() (string, error) { + return c.Broadcast(ctx, gasLimit, authzMsg, authzSigner) + }) + + if err != nil { + return "", errors.Wrap(err, "unable to broadcast blame data") + } + + return zetaTxHash, nil +} + +// PostVoteOutbound posts a vote on an observed outbound tx from a MsgVoteOutbound. +// Returns tx hash, ballotIndex, and error. +func (c *Client) PostVoteOutbound( + ctx context.Context, + gasLimit, retryGasLimit uint64, + msg *types.MsgVoteOutbound, +) (string, string, error) { + authzMsg, authzSigner, err := WrapMessageWithAuthz(msg) + if err != nil { + return "", "", errors.Wrap(err, "unable to wrap message with authz") + } + + // don't post confirmation if it already voted before + ballotIndex := msg.Digest() + hasVoted, err := c.HasVoted(ctx, ballotIndex, msg.Creator) + if err != nil { + return "", ballotIndex, errors.Wrapf( + err, + "PostVoteOutbound: unable to check if already voted for ballot %s voter %s", + ballotIndex, + msg.Creator, + ) + } + if hasVoted { + return "", ballotIndex, nil + } + + zetaTxHash, err := retry.DoTypedWithRetry(func() (string, error) { + return c.Broadcast(ctx, gasLimit, authzMsg, authzSigner) + }) + + if err != nil { + return "", ballotIndex, errors.Wrap(err, "unable to broadcast vote outbound") + } + + go func() { + ctxForWorker := zctx.Copy(ctx, context.Background()) + + errMonitor := c.MonitorVoteOutboundResult(ctxForWorker, zetaTxHash, retryGasLimit, msg) + if errMonitor != nil { + c.logger.Error().Err(err).Msg("PostVoteOutbound: failed to monitor vote outbound result") + } + }() + + return zetaTxHash, ballotIndex, nil +} + +// PostVoteInbound posts a vote on an observed inbound tx +// retryGasLimit is the gas limit used to resend the tx if it fails because of insufficient gas +// it is used when the ballot is finalized and the inbound tx needs to be processed +func (c *Client) PostVoteInbound( + ctx context.Context, + gasLimit, retryGasLimit uint64, + msg *types.MsgVoteInbound, +) (string, string, error) { + authzMsg, authzSigner, err := WrapMessageWithAuthz(msg) + if err != nil { + return "", "", err + } + + // don't post send if has already voted before + ballotIndex := msg.Digest() + hasVoted, err := c.HasVoted(ctx, ballotIndex, msg.Creator) + if err != nil { + return "", ballotIndex, errors.Wrapf(err, + "PostVoteInbound: unable to check if already voted for ballot %s voter %s", + ballotIndex, + msg.Creator, + ) + } + if hasVoted { + return "", ballotIndex, nil + } + + zetaTxHash, err := retry.DoTypedWithRetry(func() (string, error) { + return c.Broadcast(ctx, gasLimit, authzMsg, authzSigner) + }) + + if err != nil { + return "", ballotIndex, errors.Wrap(err, "unable to broadcast vote inbound") + } + + go func() { + ctxForWorker := zctx.Copy(ctx, context.Background()) + + errMonitor := c.MonitorVoteInboundResult(ctxForWorker, zetaTxHash, retryGasLimit, msg) + if errMonitor != nil { + c.logger.Error().Err(err).Msg("PostVoteInbound: failed to monitor vote inbound result") + } + }() + + return zetaTxHash, ballotIndex, nil +} diff --git a/zetaclient/zetacore/client_worker.go b/zetaclient/zetacore/client_worker.go new file mode 100644 index 0000000000..a3b6026e90 --- /dev/null +++ b/zetaclient/zetacore/client_worker.go @@ -0,0 +1,43 @@ +package zetacore + +import ( + "context" + "time" + + "github.com/rs/zerolog" + + appcontext "github.com/zeta-chain/zetacore/zetaclient/context" +) + +var logSampler = &zerolog.BasicSampler{N: 10} + +// UpdateZetacoreContextWorker is a polling goroutine that checks and updates zetacore context at every height. +// todo implement graceful shutdown and work group +func (c *Client) UpdateZetacoreContextWorker(ctx context.Context, app *appcontext.AppContext) { + defer func() { + if r := recover(); r != nil { + c.logger.Error().Interface("panic", r).Msg("UpdateZetacoreContextWorker: recovered from panic") + } + }() + + var ( + updateEvery = time.Duration(app.Config().ConfigUpdateTicker) * time.Second + ticker = time.NewTicker(updateEvery) + logger = c.logger.Sample(logSampler) + ) + + c.logger.Info().Msg("UpdateZetacoreContextWorker started") + + for { + select { + case <-ticker.C: + c.logger.Debug().Msg("UpdateZetacoreContextWorker invocation") + if err := c.UpdateZetacoreContext(ctx, app, false, logger); err != nil { + c.logger.Err(err).Msg("UpdateZetacoreContextWorker failed to update config") + } + case <-c.stop: + c.logger.Info().Msg("UpdateZetacoreContextWorker stopped") + return + } + } +} diff --git a/zetaclient/zetacore/constant.go b/zetaclient/zetacore/constant.go index fa51df4791..e3ddafad78 100644 --- a/zetaclient/zetacore/constant.go +++ b/zetaclient/zetacore/constant.go @@ -1,13 +1,18 @@ package zetacore +import "time" + const ( + // DefaultBaseGasPrice is the default base gas price + DefaultBaseGasPrice = 1_000_000 + // DefaultGasLimit is the default gas limit used for broadcasting txs DefaultGasLimit = 200_000 // PostGasPriceGasLimit is the gas limit for voting new gas price PostGasPriceGasLimit = 1_500_000 - // PostVoteInboundGasLimit is the gas limit for voting on observed inbound tx + // PostVoteInboundGasLimit is the gas limit for voting on observed inbound tx (for zetachain itself) PostVoteInboundGasLimit = 400_000 // PostVoteInboundExecutionGasLimit is the gas limit for voting on observed inbound tx and executing it @@ -31,22 +36,16 @@ const ( // DefaultRetryInterval is the interval between retries in seconds DefaultRetryInterval = 5 - // MonitorVoteInboundResultInterval is the interval between retries for monitoring tx result in seconds - MonitorVoteInboundResultInterval = 5 - - // MonitorVoteInboundResultRetryCount is the number of retries to fetch monitoring tx result - MonitorVoteInboundResultRetryCount = 20 - - // PostVoteOutboundGasLimit is the gas limit for voting on observed outbound tx + // PostVoteOutboundGasLimit is the gas limit for voting on observed outbound tx (for zetachain itself) PostVoteOutboundGasLimit = 400_000 // PostVoteOutboundRevertGasLimit is the gas limit for voting on observed outbound tx for revert (when outbound fails) // The value needs to be higher because reverting implies interacting with the EVM to perform swaps for the gas token PostVoteOutboundRevertGasLimit = 1_500_000 +) - // MonitorVoteOutboundResultInterval is the interval between retries for monitoring tx result in seconds - MonitorVoteOutboundResultInterval = 5 - - // MonitorVoteOutboundResultRetryCount is the number of retries to fetch monitoring tx result - MonitorVoteOutboundResultRetryCount = 20 +// constants for monitoring tx results +const ( + monitorInterval = 5 * time.Second + monitorRetryCount = 20 ) diff --git a/zetaclient/zetacore/query.go b/zetaclient/zetacore/query.go deleted file mode 100644 index 52a9a8e5fc..0000000000 --- a/zetaclient/zetacore/query.go +++ /dev/null @@ -1,563 +0,0 @@ -package zetacore - -import ( - "context" - "fmt" - "sort" - "time" - - sdkmath "cosmossdk.io/math" - tmhttp "github.com/cometbft/cometbft/rpc/client/http" - "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" - "github.com/cosmos/cosmos-sdk/types/query" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - feemarkettypes "github.com/evmos/ethermint/x/feemarket/types" - "google.golang.org/grpc" - - "github.com/zeta-chain/zetacore/cmd/zetacored/config" - "github.com/zeta-chain/zetacore/pkg/chains" - "github.com/zeta-chain/zetacore/pkg/proofs" - authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" - crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" - lightclienttypes "github.com/zeta-chain/zetacore/x/lightclient/types" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" - "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" -) - -// GetCrosschainFlags returns the crosschain flags -func (c *Client) GetCrosschainFlags() (observertypes.CrosschainFlags, error) { - client := observertypes.NewQueryClient(c.grpcConn) - resp, err := client.CrosschainFlags(context.Background(), &observertypes.QueryGetCrosschainFlagsRequest{}) - if err != nil { - return observertypes.CrosschainFlags{}, err - } - return resp.CrosschainFlags, nil -} - -// GetBlockHeaderEnabledChains returns the enabled chains for block headers -func (c *Client) GetBlockHeaderEnabledChains() ([]lightclienttypes.HeaderSupportedChain, error) { - client := lightclienttypes.NewQueryClient(c.grpcConn) - resp, err := client.HeaderEnabledChains(context.Background(), &lightclienttypes.QueryHeaderEnabledChainsRequest{}) - if err != nil { - return []lightclienttypes.HeaderSupportedChain{}, err - } - return resp.HeaderEnabledChains, nil -} - -// GetRateLimiterFlags returns the rate limiter flags -func (c *Client) GetRateLimiterFlags() (crosschaintypes.RateLimiterFlags, error) { - client := crosschaintypes.NewQueryClient(c.grpcConn) - resp, err := client.RateLimiterFlags(context.Background(), &crosschaintypes.QueryRateLimiterFlagsRequest{}) - if err != nil { - return crosschaintypes.RateLimiterFlags{}, err - } - return resp.RateLimiterFlags, nil -} - -// GetChainParamsForChainID returns the chain params for a given chain ID -func (c *Client) GetChainParamsForChainID(externalChainID int64) (*observertypes.ChainParams, error) { - client := observertypes.NewQueryClient(c.grpcConn) - resp, err := client.GetChainParamsForChain( - context.Background(), - &observertypes.QueryGetChainParamsForChainRequest{ChainId: externalChainID}, - ) - if err != nil { - return &observertypes.ChainParams{}, err - } - return resp.ChainParams, nil -} - -// GetChainParams returns all the chain params -func (c *Client) GetChainParams() ([]*observertypes.ChainParams, error) { - client := observertypes.NewQueryClient(c.grpcConn) - var err error - - resp := &observertypes.QueryGetChainParamsResponse{} - for i := 0; i <= DefaultRetryCount; i++ { - resp, err = client.GetChainParams(context.Background(), &observertypes.QueryGetChainParamsRequest{}) - if err == nil { - return resp.ChainParams.ChainParams, nil - } - time.Sleep(DefaultRetryInterval * time.Second) - } - return nil, fmt.Errorf("failed to get chain params | err %s", err.Error()) -} - -// GetUpgradePlan returns the current upgrade plan -func (c *Client) GetUpgradePlan() (*upgradetypes.Plan, error) { - client := upgradetypes.NewQueryClient(c.grpcConn) - - resp, err := client.CurrentPlan(context.Background(), &upgradetypes.QueryCurrentPlanRequest{}) - if err != nil { - return nil, err - } - return resp.Plan, nil -} - -// GetAllCctx returns all cross chain transactions -func (c *Client) GetAllCctx() ([]*crosschaintypes.CrossChainTx, error) { - client := crosschaintypes.NewQueryClient(c.grpcConn) - resp, err := client.CctxAll(context.Background(), &crosschaintypes.QueryAllCctxRequest{}) - if err != nil { - return nil, err - } - return resp.CrossChainTx, nil -} - -// GetCctxByHash returns a cross chain transaction by hash -func (c *Client) GetCctxByHash(sendHash string) (*crosschaintypes.CrossChainTx, error) { - client := crosschaintypes.NewQueryClient(c.grpcConn) - resp, err := client.Cctx(context.Background(), &crosschaintypes.QueryGetCctxRequest{Index: sendHash}) - if err != nil { - return nil, err - } - return resp.CrossChainTx, nil -} - -// GetCctxByNonce returns a cross chain transaction by nonce -func (c *Client) GetCctxByNonce(chainID int64, nonce uint64) (*crosschaintypes.CrossChainTx, error) { - client := crosschaintypes.NewQueryClient(c.grpcConn) - resp, err := client.CctxByNonce(context.Background(), &crosschaintypes.QueryGetCctxByNonceRequest{ - ChainID: chainID, - Nonce: nonce, - }) - if err != nil { - return nil, err - } - return resp.CrossChainTx, nil -} - -// GetObserverList returns the list of observers -func (c *Client) GetObserverList() ([]string, error) { - var err error - client := observertypes.NewQueryClient(c.grpcConn) - - for i := 0; i <= DefaultRetryCount; i++ { - resp, err := client.ObserverSet(context.Background(), &observertypes.QueryObserverSet{}) - if err == nil { - return resp.Observers, nil - } - time.Sleep(DefaultRetryInterval * time.Second) - } - return nil, err -} - -// GetRateLimiterInput returns input data for the rate limit checker -func (c *Client) GetRateLimiterInput(window int64) (crosschaintypes.QueryRateLimiterInputResponse, error) { - client := crosschaintypes.NewQueryClient(c.grpcConn) - maxSizeOption := grpc.MaxCallRecvMsgSize(32 * 1024 * 1024) - resp, err := client.RateLimiterInput( - context.Background(), - &crosschaintypes.QueryRateLimiterInputRequest{ - Window: window, - }, - maxSizeOption, - ) - if err != nil { - return crosschaintypes.QueryRateLimiterInputResponse{}, err - } - return *resp, nil -} - -// ListPendingCctx returns a list of pending cctxs for a given chainID -// - The max size of the list is crosschainkeeper.MaxPendingCctxs -func (c *Client) ListPendingCctx(chainID int64) ([]*crosschaintypes.CrossChainTx, uint64, error) { - client := crosschaintypes.NewQueryClient(c.grpcConn) - maxSizeOption := grpc.MaxCallRecvMsgSize(32 * 1024 * 1024) - resp, err := client.ListPendingCctx( - context.Background(), - &crosschaintypes.QueryListPendingCctxRequest{ - ChainId: chainID, - }, - maxSizeOption, - ) - if err != nil { - return nil, 0, err - } - return resp.CrossChainTx, resp.TotalPending, nil -} - -// ListPendingCctxWithinRatelimit returns a list of pending cctxs that do not exceed the outbound rate limit -// - The max size of the list is crosschainkeeper.MaxPendingCctxs -// - The returned `rateLimitExceeded` flag indicates if the rate limit is exceeded or not -func (c *Client) ListPendingCctxWithinRatelimit() ([]*crosschaintypes.CrossChainTx, uint64, int64, string, bool, error) { - client := crosschaintypes.NewQueryClient(c.grpcConn) - maxSizeOption := grpc.MaxCallRecvMsgSize(32 * 1024 * 1024) - resp, err := client.ListPendingCctxWithinRateLimit( - context.Background(), - &crosschaintypes.QueryListPendingCctxWithinRateLimitRequest{}, - maxSizeOption, - ) - if err != nil { - return nil, 0, 0, "", false, err - } - return resp.CrossChainTx, resp.TotalPending, resp.CurrentWithdrawWindow, resp.CurrentWithdrawRate, resp.RateLimitExceeded, nil -} - -// GetAbortedZetaAmount returns the amount of zeta that has been aborted -func (c *Client) GetAbortedZetaAmount() (string, error) { - client := crosschaintypes.NewQueryClient(c.grpcConn) - resp, err := client.ZetaAccounting(context.Background(), &crosschaintypes.QueryZetaAccountingRequest{}) - if err != nil { - return "", err - } - return resp.AbortedZetaAmount, nil -} - -// GetGenesisSupply returns the genesis supply -func (c *Client) GetGenesisSupply() (sdkmath.Int, error) { - tmURL := fmt.Sprintf("http://%s", c.cfg.ChainRPC) - s, err := tmhttp.New(tmURL, "/websocket") - if err != nil { - return sdkmath.ZeroInt(), err - } - res, err := s.Genesis(context.Background()) - if err != nil { - return sdkmath.ZeroInt(), err - } - appState, err := genutiltypes.GenesisStateFromGenDoc(*res.Genesis) - if err != nil { - return sdkmath.ZeroInt(), err - } - bankstate := banktypes.GetGenesisStateFromAppState(c.encodingCfg.Codec, appState) - return bankstate.Supply.AmountOf(config.BaseDenom), nil -} - -// GetZetaTokenSupplyOnNode returns the zeta token supply on the node -func (c *Client) GetZetaTokenSupplyOnNode() (sdkmath.Int, error) { - client := banktypes.NewQueryClient(c.grpcConn) - resp, err := client.SupplyOf(context.Background(), &banktypes.QuerySupplyOfRequest{Denom: config.BaseDenom}) - if err != nil { - return sdkmath.ZeroInt(), err - } - return resp.GetAmount().Amount, nil -} - -// GetLastBlockHeight returns the last block height -func (c *Client) GetLastBlockHeight() ([]*crosschaintypes.LastBlockHeight, error) { - client := crosschaintypes.NewQueryClient(c.grpcConn) - resp, err := client.LastBlockHeightAll(context.Background(), &crosschaintypes.QueryAllLastBlockHeightRequest{}) - if err != nil { - c.logger.Error().Err(err).Msg("query GetBlockHeight error") - return nil, err - } - return resp.LastBlockHeight, nil -} - -// GetLatestZetaBlock returns the latest zeta block -func (c *Client) GetLatestZetaBlock() (*tmservice.Block, error) { - client := tmservice.NewServiceClient(c.grpcConn) - res, err := client.GetLatestBlock(context.Background(), &tmservice.GetLatestBlockRequest{}) - if err != nil { - return nil, err - } - return res.SdkBlock, nil -} - -// GetNodeInfo returns the node info -func (c *Client) GetNodeInfo() (*tmservice.GetNodeInfoResponse, error) { - var err error - - client := tmservice.NewServiceClient(c.grpcConn) - for i := 0; i <= DefaultRetryCount; i++ { - res, err := client.GetNodeInfo(context.Background(), &tmservice.GetNodeInfoRequest{}) - if err == nil { - return res, nil - } - time.Sleep(DefaultRetryInterval * time.Second) - } - return nil, err -} - -// GetBlockHeight returns the zetachain block height -func (c *Client) GetBlockHeight() (int64, error) { - client := crosschaintypes.NewQueryClient(c.grpcConn) - resp, err := client.LastZetaHeight(context.Background(), &crosschaintypes.QueryLastZetaHeightRequest{}) - if err != nil { - return 0, err - } - return resp.Height, nil -} - -// GetBaseGasPrice returns the base gas price -func (c *Client) GetBaseGasPrice() (int64, error) { - client := feemarkettypes.NewQueryClient(c.grpcConn) - resp, err := client.Params(context.Background(), &feemarkettypes.QueryParamsRequest{}) - if err != nil { - return 0, err - } - if resp.Params.BaseFee.IsNil() { - return 0, fmt.Errorf("base fee is nil") - } - return resp.Params.BaseFee.Int64(), nil -} - -// GetBallotByID returns a ballot by ID -func (c *Client) GetBallotByID(id string) (*observertypes.QueryBallotByIdentifierResponse, error) { - client := observertypes.NewQueryClient(c.grpcConn) - return client.BallotByIdentifier(context.Background(), &observertypes.QueryBallotByIdentifierRequest{ - BallotIdentifier: id, - }) -} - -// GetNonceByChain returns the nonce by chain -func (c *Client) GetNonceByChain(chain chains.Chain) (observertypes.ChainNonces, error) { - client := observertypes.NewQueryClient(c.grpcConn) - resp, err := client.ChainNonces( - context.Background(), - &observertypes.QueryGetChainNoncesRequest{Index: chain.ChainName.String()}, - ) - if err != nil { - return observertypes.ChainNonces{}, err - } - return resp.ChainNonces, nil -} - -// GetAllNodeAccounts returns all node accounts -func (c *Client) GetAllNodeAccounts() ([]*observertypes.NodeAccount, error) { - client := observertypes.NewQueryClient(c.grpcConn) - resp, err := client.NodeAccountAll(context.Background(), &observertypes.QueryAllNodeAccountRequest{}) - if err != nil { - return nil, err - } - c.logger.Debug().Msgf("GetAllNodeAccounts: %d", len(resp.NodeAccount)) - return resp.NodeAccount, nil -} - -// GetKeyGen returns the keygen -func (c *Client) GetKeyGen() (*observertypes.Keygen, error) { - var err error - client := observertypes.NewQueryClient(c.grpcConn) - - for i := 0; i <= ExtendedRetryCount; i++ { - resp, err := client.Keygen(context.Background(), &observertypes.QueryGetKeygenRequest{}) - if err == nil { - return resp.Keygen, nil - } - time.Sleep(DefaultRetryInterval * time.Second) - } - return nil, fmt.Errorf("failed to get keygen | err %s", err.Error()) -} - -// GetBallot returns a ballot by ID -func (c *Client) GetBallot(ballotIdentifier string) (*observertypes.QueryBallotByIdentifierResponse, error) { - client := observertypes.NewQueryClient(c.grpcConn) - resp, err := client.BallotByIdentifier(context.Background(), &observertypes.QueryBallotByIdentifierRequest{ - BallotIdentifier: ballotIdentifier, - }) - if err != nil { - return nil, err - } - return resp, nil -} - -// GetInboundTrackersForChain returns the inbound trackers for a chain -func (c *Client) GetInboundTrackersForChain(chainID int64) ([]crosschaintypes.InboundTracker, error) { - client := crosschaintypes.NewQueryClient(c.grpcConn) - resp, err := client.InboundTrackerAllByChain( - context.Background(), - &crosschaintypes.QueryAllInboundTrackerByChainRequest{ChainId: chainID}, - ) - if err != nil { - return nil, err - } - return resp.InboundTracker, nil -} - -// GetCurrentTss returns the current TSS -func (c *Client) GetCurrentTss() (observertypes.TSS, error) { - client := observertypes.NewQueryClient(c.grpcConn) - resp, err := client.TSS(context.Background(), &observertypes.QueryGetTSSRequest{}) - if err != nil { - return observertypes.TSS{}, err - } - return resp.TSS, nil -} - -// GetEthTssAddress returns the ETH TSS address -// TODO(revamp): rename to EVM -func (c *Client) GetEthTssAddress() (string, error) { - client := observertypes.NewQueryClient(c.grpcConn) - resp, err := client.GetTssAddress(context.Background(), &observertypes.QueryGetTssAddressRequest{}) - if err != nil { - return "", err - } - return resp.Eth, nil -} - -// GetBtcTssAddress returns the BTC TSS address -func (c *Client) GetBtcTssAddress(chainID int64) (string, error) { - client := observertypes.NewQueryClient(c.grpcConn) - resp, err := client.GetTssAddress(context.Background(), &observertypes.QueryGetTssAddressRequest{ - BitcoinChainId: chainID, - }) - if err != nil { - return "", err - } - return resp.Btc, nil -} - -// GetTssHistory returns the TSS history -func (c *Client) GetTssHistory() ([]observertypes.TSS, error) { - client := observertypes.NewQueryClient(c.grpcConn) - resp, err := client.TssHistory(context.Background(), &observertypes.QueryTssHistoryRequest{}) - if err != nil { - return nil, err - } - return resp.TssList, nil -} - -// GetOutboundTracker returns the outbound tracker for a chain and nonce -func (c *Client) GetOutboundTracker(chain chains.Chain, nonce uint64) (*crosschaintypes.OutboundTracker, error) { - client := crosschaintypes.NewQueryClient(c.grpcConn) - resp, err := client.OutboundTracker(context.Background(), &crosschaintypes.QueryGetOutboundTrackerRequest{ - ChainID: chain.ChainId, - Nonce: nonce, - }) - if err != nil { - return nil, err - } - return &resp.OutboundTracker, nil -} - -// GetAllOutboundTrackerByChain returns all outbound trackers for a chain -func (c *Client) GetAllOutboundTrackerByChain( - chainID int64, - order interfaces.Order, -) ([]crosschaintypes.OutboundTracker, error) { - client := crosschaintypes.NewQueryClient(c.grpcConn) - resp, err := client.OutboundTrackerAllByChain( - context.Background(), - &crosschaintypes.QueryAllOutboundTrackerByChainRequest{ - Chain: chainID, - Pagination: &query.PageRequest{ - Key: nil, - Offset: 0, - Limit: 2000, - CountTotal: false, - Reverse: false, - }, - }, - ) - if err != nil { - return nil, err - } - if order == interfaces.Ascending { - sort.SliceStable(resp.OutboundTracker, func(i, j int) bool { - return resp.OutboundTracker[i].Nonce < resp.OutboundTracker[j].Nonce - }) - } - if order == interfaces.Descending { - sort.SliceStable(resp.OutboundTracker, func(i, j int) bool { - return resp.OutboundTracker[i].Nonce > resp.OutboundTracker[j].Nonce - }) - } - return resp.OutboundTracker, nil -} - -// GetPendingNoncesByChain returns the pending nonces for a chain and current tss address -func (c *Client) GetPendingNoncesByChain(chainID int64) (observertypes.PendingNonces, error) { - client := observertypes.NewQueryClient(c.grpcConn) - resp, err := client.PendingNoncesByChain( - context.Background(), - &observertypes.QueryPendingNoncesByChainRequest{ChainId: chainID}, - ) - if err != nil { - return observertypes.PendingNonces{}, err - } - return resp.PendingNonces, nil -} - -// GetBlockHeaderChainState returns the block header chain state -func (c *Client) GetBlockHeaderChainState(chainID int64) (lightclienttypes.QueryGetChainStateResponse, error) { - client := lightclienttypes.NewQueryClient(c.grpcConn) - resp, err := client.ChainState(context.Background(), &lightclienttypes.QueryGetChainStateRequest{ChainId: chainID}) - if err != nil { - return lightclienttypes.QueryGetChainStateResponse{}, err - } - return *resp, nil -} - -// GetSupportedChains returns the supported chains -func (c *Client) GetSupportedChains() ([]chains.Chain, error) { - client := observertypes.NewQueryClient(c.grpcConn) - resp, err := client.SupportedChains(context.Background(), &observertypes.QuerySupportedChains{}) - if err != nil { - return nil, err - } - return resp.GetChains(), nil -} - -// GetAdditionalChains returns the additional chains -func (c *Client) GetAdditionalChains() ([]chains.Chain, error) { - client := authoritytypes.NewQueryClient(c.grpcConn) - resp, err := client.ChainInfo(context.Background(), &authoritytypes.QueryGetChainInfoRequest{}) - if err != nil { - return nil, err - } - return resp.GetChainInfo().Chains, nil -} - -// GetPendingNonces returns the pending nonces -func (c *Client) GetPendingNonces() (*observertypes.QueryAllPendingNoncesResponse, error) { - client := observertypes.NewQueryClient(c.grpcConn) - resp, err := client.PendingNoncesAll(context.Background(), &observertypes.QueryAllPendingNoncesRequest{}) - if err != nil { - return nil, err - } - return resp, nil -} - -// Prove returns whether a proof is valid -func (c *Client) Prove( - blockHash string, - txHash string, - txIndex int64, - proof *proofs.Proof, - chainID int64, -) (bool, error) { - client := lightclienttypes.NewQueryClient(c.grpcConn) - resp, err := client.Prove(context.Background(), &lightclienttypes.QueryProveRequest{ - BlockHash: blockHash, - TxIndex: txIndex, - Proof: proof, - ChainId: chainID, - TxHash: txHash, - }) - if err != nil { - return false, err - } - return resp.Valid, nil -} - -// HasVoted returns whether an observer has voted -func (c *Client) HasVoted(ballotIndex string, voterAddress string) (bool, error) { - client := observertypes.NewQueryClient(c.grpcConn) - resp, err := client.HasVoted(context.Background(), &observertypes.QueryHasVotedRequest{ - BallotIdentifier: ballotIndex, - VoterAddress: voterAddress, - }) - if err != nil { - return false, err - } - return resp.HasVoted, nil -} - -// GetZetaHotKeyBalance returns the zeta hot key balance -func (c *Client) GetZetaHotKeyBalance() (sdkmath.Int, error) { - client := banktypes.NewQueryClient(c.grpcConn) - address, err := c.keys.GetAddress() - if err != nil { - return sdkmath.ZeroInt(), err - } - resp, err := client.Balance(context.Background(), &banktypes.QueryBalanceRequest{ - Address: address.String(), - Denom: config.BaseDenom, - }) - if err != nil { - return sdkmath.ZeroInt(), err - } - return resp.Balance.Amount, nil -} diff --git a/zetaclient/zetacore/tx.go b/zetaclient/zetacore/tx.go index 2ef142d03f..90a6b1d136 100644 --- a/zetaclient/zetacore/tx.go +++ b/zetaclient/zetacore/tx.go @@ -1,26 +1,21 @@ package zetacore import ( + "context" "fmt" - "math/big" "strings" - "time" + "cosmossdk.io/errors" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/authz" - "github.com/pkg/errors" - "github.com/rs/zerolog" - "github.com/zeta-chain/go-tss/blame" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/pkg/proofs" "github.com/zeta-chain/zetacore/x/crosschain/types" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" clientauthz "github.com/zeta-chain/zetacore/zetaclient/authz" clientcommon "github.com/zeta-chain/zetacore/zetaclient/common" - appcontext "github.com/zeta-chain/zetacore/zetaclient/context" ) // GetInboundVoteMessage returns a new MsgVoteInbound @@ -72,52 +67,24 @@ func GasPriceMultiplier(chain chains.Chain) (float64, error) { // WrapMessageWithAuthz wraps a message with an authz message // used since a hotkey is used to broadcast the transactions, instead of the operator -func (c *Client) WrapMessageWithAuthz(msg sdk.Msg) (sdk.Msg, clientauthz.Signer, error) { +func WrapMessageWithAuthz(msg sdk.Msg) (sdk.Msg, clientauthz.Signer, error) { msgURL := sdk.MsgTypeURL(msg) // verify message validity if err := msg.ValidateBasic(); err != nil { - return nil, clientauthz.Signer{}, fmt.Errorf("%s invalid msg | %s", msgURL, err.Error()) + return nil, clientauthz.Signer{}, errors.Wrapf(err, "invalid message %q", msgURL) } authzSigner := clientauthz.GetSigner(msgURL) authzMessage := authz.NewMsgExec(authzSigner.GranteeAddress, []sdk.Msg{msg}) - return &authzMessage, authzSigner, nil -} - -// PostGasPrice posts a gas price vote -// TODO(revamp): rename to PostVoteGasPrice -func (c *Client) PostGasPrice(chain chains.Chain, gasPrice uint64, supply string, blockNum uint64) (string, error) { - // apply gas price multiplier for the chain - multiplier, err := GasPriceMultiplier(chain) - if err != nil { - return "", err - } - // #nosec G701 always in range - gasPrice = uint64(float64(gasPrice) * multiplier) - signerAddress := c.keys.GetOperatorAddress().String() - msg := types.NewMsgVoteGasPrice(signerAddress, chain.ChainId, gasPrice, supply, blockNum) - authzMsg, authzSigner, err := c.WrapMessageWithAuthz(msg) - if err != nil { - return "", err - } - - for i := 0; i < DefaultRetryCount; i++ { - zetaTxHash, err := zetacoreBroadcast(c, PostGasPriceGasLimit, authzMsg, authzSigner) - if err == nil { - return zetaTxHash, nil - } - c.logger.Debug().Err(err).Msgf("PostGasPrice broadcast fail | Retry count : %d", i+1) - time.Sleep(DefaultRetryInterval * time.Second) - } - - return "", fmt.Errorf("post gasprice failed after %d retries", DefaultRetryInterval) + return &authzMessage, authzSigner, nil } // AddOutboundTracker adds an outbound tracker // TODO(revamp): rename to PostAddOutboundTracker func (c *Client) AddOutboundTracker( + ctx context.Context, chainID int64, nonce uint64, txHash string, @@ -126,7 +93,7 @@ func (c *Client) AddOutboundTracker( txIndex int64, ) (string, error) { // don't report if the tracker already contains the txHash - tracker, err := c.GetOutboundTracker(chains.Chain{ChainId: chainID}, nonce) + tracker, err := c.GetOutboundTracker(ctx, chains.Chain{ChainId: chainID}, nonce) if err == nil { for _, hash := range tracker.HashList { if strings.EqualFold(hash.TxHash, txHash) { @@ -138,344 +105,15 @@ func (c *Client) AddOutboundTracker( signerAddress := c.keys.GetOperatorAddress().String() msg := types.NewMsgAddOutboundTracker(signerAddress, chainID, nonce, txHash, proof, blockHash, txIndex) - authzMsg, authzSigner, err := c.WrapMessageWithAuthz(msg) + authzMsg, authzSigner, err := WrapMessageWithAuthz(msg) if err != nil { return "", err } - zetaTxHash, err := zetacoreBroadcast(c, AddOutboundTrackerGasLimit, authzMsg, authzSigner) + zetaTxHash, err := c.Broadcast(ctx, AddOutboundTrackerGasLimit, authzMsg, authzSigner) if err != nil { return "", err } - return zetaTxHash, nil -} - -// SetTSS sends message to vote tss -// TODO(revamp): rename to PostVoteTSS -func (c *Client) SetTSS(tssPubkey string, keyGenZetaHeight int64, status chains.ReceiveStatus) (string, error) { - signerAddress := c.keys.GetOperatorAddress().String() - msg := observertypes.NewMsgVoteTSS(signerAddress, tssPubkey, keyGenZetaHeight, status) - - authzMsg, authzSigner, err := c.WrapMessageWithAuthz(msg) - if err != nil { - return "", err - } - - zetaTxHash := "" - for i := 0; i <= DefaultRetryCount; i++ { - zetaTxHash, err = zetacoreBroadcast(c, DefaultGasLimit, authzMsg, authzSigner) - if err == nil { - return zetaTxHash, nil - } - c.logger.Debug().Err(err).Msgf("SetTSS broadcast fail | Retry count : %d", i+1) - time.Sleep(DefaultRetryInterval * time.Second) - } - return "", fmt.Errorf("set tss failed | err %s", err.Error()) -} - -// ZetacoreContextUpdater is a polling goroutine that checks and updates zetacore context at every height -// TODO(revamp): move to a different file -// TODO(revamp): rename to UpdateZetacoreContext -func (c *Client) ZetacoreContextUpdater(appContext *appcontext.AppContext) { - c.logger.Info().Msg("ZetacoreContextUpdater started") - ticker := time.NewTicker(time.Duration(appContext.Config().ConfigUpdateTicker) * time.Second) - sampledLogger := c.logger.Sample(&zerolog.BasicSampler{N: 10}) - for { - select { - case <-ticker.C: - c.logger.Debug().Msg("Running Updater") - err := c.UpdateZetacoreContext(appContext, false, sampledLogger) - if err != nil { - c.logger.Err(err).Msg("ZetacoreContextUpdater failed to update config") - } - case <-c.stop: - c.logger.Info().Msg("ZetacoreContextUpdater stopped") - return - } - } -} - -// PostBlameData posts blame data message to zetacore -// TODO(revamp): rename to PostVoteBlame -func (c *Client) PostBlameData(blame *blame.Blame, chainID int64, index string) (string, error) { - signerAddress := c.keys.GetOperatorAddress().String() - zetaBlame := observertypes.Blame{ - Index: index, - FailureReason: blame.FailReason, - Nodes: observertypes.ConvertNodes(blame.BlameNodes), - } - msg := observertypes.NewMsgVoteBlameMsg(signerAddress, chainID, zetaBlame) - - authzMsg, authzSigner, err := c.WrapMessageWithAuthz(msg) - if err != nil { - return "", err - } - - var gasLimit uint64 = PostBlameDataGasLimit - - for i := 0; i < DefaultRetryCount; i++ { - zetaTxHash, err := zetacoreBroadcast(c, gasLimit, authzMsg, authzSigner) - if err == nil { - return zetaTxHash, nil - } - c.logger.Error().Err(err).Msgf("PostBlame broadcast fail | Retry count : %d", i+1) - time.Sleep(DefaultRetryInterval * time.Second) - } - return "", fmt.Errorf("post blame data failed after %d retries", DefaultRetryCount) -} - -// PostVoteBlockHeader posts a vote on an observed block header -func (c *Client) PostVoteBlockHeader( - chainID int64, - blockHash []byte, - height int64, - header proofs.HeaderData, -) (string, error) { - signerAddress := c.keys.GetOperatorAddress().String() - - msg := observertypes.NewMsgVoteBlockHeader(signerAddress, chainID, blockHash, height, header) - - authzMsg, authzSigner, err := c.WrapMessageWithAuthz(msg) - if err != nil { - return "", err - } - - var gasLimit uint64 = DefaultGasLimit - for i := 0; i < DefaultRetryCount; i++ { - zetaTxHash, err := zetacoreBroadcast(c, gasLimit, authzMsg, authzSigner) - if err == nil { - return zetaTxHash, nil - } - c.logger.Error().Err(err).Msgf("PostVoteBlockHeader broadcast fail | Retry count : %d", i+1) - time.Sleep(DefaultRetryInterval * time.Second) - } - return "", fmt.Errorf("post add block header failed after %d retries", DefaultRetryCount) -} - -// PostVoteInbound posts a vote on an observed inbound tx -// retryGasLimit is the gas limit used to resend the tx if it fails because of insufficient gas -// it is used when the ballot is finalized and the inbound tx needs to be processed -func (c *Client) PostVoteInbound(gasLimit, retryGasLimit uint64, msg *types.MsgVoteInbound) (string, string, error) { - authzMsg, authzSigner, err := c.WrapMessageWithAuthz(msg) - if err != nil { - return "", "", err - } - - // don't post send if has already voted before - ballotIndex := msg.Digest() - hasVoted, err := c.HasVoted(ballotIndex, msg.Creator) - if err != nil { - return "", ballotIndex, errors.Wrapf( - err, - "PostVoteInbound: unable to check if already voted for ballot %s voter %s", - ballotIndex, - msg.Creator, - ) - } - if hasVoted { - return "", ballotIndex, nil - } - - for i := 0; i < DefaultRetryCount; i++ { - zetaTxHash, err := zetacoreBroadcast(c, gasLimit, authzMsg, authzSigner) - if err == nil { - // monitor the result of the transaction and resend if necessary - go c.MonitorVoteInboundResult(zetaTxHash, retryGasLimit, msg) - - return zetaTxHash, ballotIndex, nil - } - c.logger.Debug().Err(err).Msgf("PostVoteInbound broadcast fail | Retry count : %d", i+1) - time.Sleep(DefaultRetryInterval * time.Second) - } - return "", ballotIndex, fmt.Errorf("post send failed after %d retries", DefaultRetryInterval) -} - -// MonitorVoteInboundResult monitors the result of a vote inbound tx -// retryGasLimit is the gas limit used to resend the tx if it fails because of insufficient gas -// if retryGasLimit is 0, the tx is not resent -// TODO(revamp): move to a monitor file -func (c *Client) MonitorVoteInboundResult(zetaTxHash string, retryGasLimit uint64, msg *types.MsgVoteInbound) { - var lastErr error - - for i := 0; i < MonitorVoteInboundResultRetryCount; i++ { - time.Sleep(MonitorVoteInboundResultInterval * time.Second) - - // query tx result from ZetaChain - txResult, err := c.QueryTxResult(zetaTxHash) - - if err == nil { - if strings.Contains(txResult.RawLog, "failed to execute message") { - // the inbound vote tx shouldn't fail to execute - // this shouldn't happen - c.logger.Error().Msgf( - "MonitorInboundResult: failed to execute vote, txHash: %s, log %s", zetaTxHash, txResult.RawLog, - ) - } else if strings.Contains(txResult.RawLog, "out of gas") { - // if the tx fails with an out of gas error, resend the tx with more gas if retryGasLimit > 0 - c.logger.Debug().Msgf( - "MonitorInboundResult: out of gas, txHash: %s, log %s", zetaTxHash, txResult.RawLog, - ) - if retryGasLimit > 0 { - // new retryGasLimit set to 0 to prevent reentering this function - _, _, err := c.PostVoteInbound(retryGasLimit, 0, msg) - if err != nil { - c.logger.Error().Err(err).Msgf( - "MonitorInboundResult: failed to resend tx, txHash: %s, log %s", zetaTxHash, txResult.RawLog, - ) - } else { - c.logger.Info().Msgf( - "MonitorInboundResult: successfully resent tx, txHash: %s, log %s", zetaTxHash, txResult.RawLog, - ) - } - } - } else { - c.logger.Debug().Msgf( - "MonitorInboundResult: successful txHash %s, log %s", zetaTxHash, txResult.RawLog, - ) - } - return - } - lastErr = err - } - - c.logger.Error().Err(lastErr).Msgf( - "MonitorInboundResult: unable to query tx result for txHash %s, err %s", zetaTxHash, lastErr.Error(), - ) -} - -// PostVoteOutbound posts a vote on an observed outbound tx -// TODO(revamp): rename and move to a different file -func (c *Client) PostVoteOutbound( - cctxIndex string, - outboundHash string, - outBlockHeight uint64, - outboundGasUsed uint64, - outboundEffectiveGasPrice *big.Int, - outboundEffectiveGasLimit uint64, - amount *big.Int, - status chains.ReceiveStatus, - chain chains.Chain, - nonce uint64, - coinType coin.CoinType, -) (string, string, error) { - signerAddress := c.keys.GetOperatorAddress().String() - msg := types.NewMsgVoteOutbound( - signerAddress, - cctxIndex, - outboundHash, - outBlockHeight, - outboundGasUsed, - math.NewIntFromBigInt(outboundEffectiveGasPrice), - outboundEffectiveGasLimit, - math.NewUintFromBigInt(amount), - status, - chain.ChainId, - nonce, - coinType, - ) - - // when an outbound fails and a revert is required, the gas limit needs to be higher - // this is because the revert tx needs to interact with the EVM to perform swaps for the gas token - // the higher gas limit is only necessary when the vote is finalized and the outbound is processed - // therefore we use a retryGasLimit with a higher value to resend the tx if it fails (when the vote is finalized) - retryGasLimit := uint64(0) - if msg.Status == chains.ReceiveStatus_failed { - retryGasLimit = PostVoteOutboundRevertGasLimit - } - - return c.PostVoteOutboundFromMsg(PostVoteOutboundGasLimit, retryGasLimit, msg) -} - -// PostVoteOutboundFromMsg posts a vote on an observed outbound tx from a MsgVoteOutbound -// TODO(revamp): rename to PostVoteOutbound -func (c *Client) PostVoteOutboundFromMsg( - gasLimit, retryGasLimit uint64, - msg *types.MsgVoteOutbound, -) (string, string, error) { - authzMsg, authzSigner, err := c.WrapMessageWithAuthz(msg) - if err != nil { - return "", "", err - } - - // don't post confirmation if has already voted before - ballotIndex := msg.Digest() - hasVoted, err := c.HasVoted(ballotIndex, msg.Creator) - if err != nil { - return "", ballotIndex, errors.Wrapf( - err, - "PostVoteOutbound: unable to check if already voted for ballot %s voter %s", - ballotIndex, - msg.Creator, - ) - } - if hasVoted { - return "", ballotIndex, nil - } - for i := 0; i < DefaultRetryCount; i++ { - zetaTxHash, err := zetacoreBroadcast(c, gasLimit, authzMsg, authzSigner) - if err == nil { - // monitor the result of the transaction and resend if necessary - go c.MonitorVoteOutboundResult(zetaTxHash, retryGasLimit, msg) - - return zetaTxHash, ballotIndex, nil - } - c.logger.Debug().Err(err).Msgf("PostVoteOutbound broadcast fail | Retry count : %d", i+1) - time.Sleep(DefaultRetryInterval * time.Second) - } - return "", ballotIndex, fmt.Errorf("post receive failed after %d retries", DefaultRetryCount) -} - -// MonitorVoteOutboundResult monitors the result of a vote outbound tx -// retryGasLimit is the gas limit used to resend the tx if it fails because of insufficient gas -// if retryGasLimit is 0, the tx is not resent -// TODO(revamp): move to a monitor file -func (c *Client) MonitorVoteOutboundResult(zetaTxHash string, retryGasLimit uint64, msg *types.MsgVoteOutbound) { - var lastErr error - - for i := 0; i < MonitorVoteOutboundResultRetryCount; i++ { - time.Sleep(MonitorVoteOutboundResultInterval * time.Second) - - // query tx result from ZetaChain - txResult, err := c.QueryTxResult(zetaTxHash) - - if err == nil { - if strings.Contains(txResult.RawLog, "failed to execute message") { - // the inbound vote tx shouldn't fail to execute - // this shouldn't happen - c.logger.Error().Msgf( - "MonitorVoteOutboundResult: failed to execute vote, txHash: %s, log %s", zetaTxHash, txResult.RawLog, - ) - } else if strings.Contains(txResult.RawLog, "out of gas") { - // if the tx fails with an out of gas error, resend the tx with more gas if retryGasLimit > 0 - c.logger.Debug().Msgf( - "MonitorVoteOutboundResult: out of gas, txHash: %s, log %s", zetaTxHash, txResult.RawLog, - ) - if retryGasLimit > 0 { - // new retryGasLimit set to 0 to prevent reentering this function - _, _, err := c.PostVoteOutboundFromMsg(retryGasLimit, 0, msg) - - if err != nil { - c.logger.Error().Err(err).Msgf( - "MonitorVoteOutboundResult: failed to resend tx, txHash: %s, log %s", zetaTxHash, txResult.RawLog, - ) - } else { - c.logger.Info().Msgf( - "MonitorVoteOutboundResult: successfully resent tx, txHash: %s, log %s", zetaTxHash, txResult.RawLog, - ) - } - } - } else { - c.logger.Debug().Msgf( - "MonitorVoteOutboundResult: successful txHash %s, log %s", zetaTxHash, txResult.RawLog, - ) - } - return - } - lastErr = err - } - - c.logger.Error().Err(lastErr).Msgf( - "MonitorVoteOutboundResult: unable to query tx result for txHash %s, err %s", zetaTxHash, lastErr.Error(), - ) + return zetaTxHash, nil } diff --git a/zetaclient/zetacore/tx_test.go b/zetaclient/zetacore/tx_test.go index d53e93bda9..0ad9c0edf7 100644 --- a/zetaclient/zetacore/tx_test.go +++ b/zetaclient/zetacore/tx_test.go @@ -2,22 +2,25 @@ package zetacore import ( "bytes" + "context" "encoding/hex" - "errors" - "github.com/zeta-chain/zetacore/testutil/sample" - authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" - "math/big" "net" "os" "testing" + "github.com/zeta-chain/zetacore/testutil/sample" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + "cosmossdk.io/math" sdktypes "github.com/cosmos/cosmos-sdk/types" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/pkg/errors" "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/zeta-chain/go-tss/blame" + zctx "github.com/zeta-chain/zetacore/zetaclient/context" "go.nhat.io/grpcmock" "go.nhat.io/grpcmock/planner" @@ -27,16 +30,14 @@ import ( crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" lightclienttypes "github.com/zeta-chain/zetacore/x/lightclient/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" - "github.com/zeta-chain/zetacore/zetaclient/authz" "github.com/zeta-chain/zetacore/zetaclient/config" - "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/zetaclient/keys" "github.com/zeta-chain/zetacore/zetaclient/testutils/mocks" ) const ( - testSigner = `jack` - sampleHash = "fa51db4412144f1130669f2bae8cb44aadbd8d85958dbffcb0fe236878097e1a" + testSigner = "jack" + sampleHash = "FA51DB4412144F1130669F2BAE8CB44AADBD8D85958DBFFCB0FE236878097E1A" ethBlockHash = "1a17bcc359e84ba8ae03b17ec425f97022cd11c3e279f6bdf7a96fcffa12b366" ) @@ -117,14 +118,6 @@ func Test_GasPriceMultiplier(t *testing.T) { } } -func MockBroadcast(_ *Client, _ uint64, _ sdktypes.Msg, _ authz.Signer) (string, error) { - return sampleHash, nil -} - -func MockBroadcastError(_ *Client, _ uint64, _ sdktypes.Msg, _ authz.Signer) (string, error) { - return sampleHash, errors.New("broadcast error") -} - func getHeaderData(t *testing.T) proofs.HeaderData { var header ethtypes.Header file, err := os.Open("../../testutil/testdata/eth_header_18495266.json") @@ -142,14 +135,19 @@ func getHeaderData(t *testing.T) proofs.HeaderData { } func TestZetacore_PostGasPrice(t *testing.T) { - client, err := setupZetacoreClient() - require.NoError(t, err) - address := sdktypes.AccAddress(mocks.TestKeyringPair.PubKey().Address().Bytes()) - client.keys = keys.NewKeysWithKeybase(mocks.NewKeyring(), address, testSigner, "") + ctx := context.Background() + + extraGRPC := withDummyServer(100) + setupMockServer(t, observertypes.RegisterQueryServer, skipMethod, nil, nil, extraGRPC...) + + client := setupZetacoreClient(t, + withDefaultObserverKeys(), + withAccountRetriever(t, 100, 100), + withTendermint(mocks.NewSDKClientWithErr(t, nil, 0).SetBroadcastTxHash(sampleHash)), + ) t.Run("post gas price success", func(t *testing.T) { - zetacoreBroadcast = MockBroadcast - hash, err := client.PostGasPrice(chains.BscMainnet, 1000000, "100", 1234) + hash, err := client.PostVoteGasPrice(ctx, chains.BscMainnet, 1000000, "100", 1234) require.NoError(t, err) require.Equal(t, sampleHash, hash) }) @@ -165,35 +163,66 @@ func TestZetacore_PostGasPrice(t *testing.T) { } func TestZetacore_AddOutboundTracker(t *testing.T) { - client, err := setupZetacoreClient() - require.NoError(t, err) - address := sdktypes.AccAddress(mocks.TestKeyringPair.PubKey().Address().Bytes()) - client.keys = keys.NewKeysWithKeybase(mocks.NewKeyring(), address, testSigner, "") + ctx := context.Background() + + const nonce = 123 + chainID := chains.BscMainnet.ChainId + + method := "/zetachain.zetacore.crosschain.Query/OutboundTracker" + input := &crosschaintypes.QueryGetOutboundTrackerRequest{ + ChainID: chains.BscMainnet.ChainId, + Nonce: nonce, + } + output := &crosschaintypes.QueryGetOutboundTrackerResponse{ + OutboundTracker: crosschaintypes.OutboundTracker{ + Index: "456", + ChainId: chainID, + Nonce: nonce, + HashList: nil, + }, + } + + extraGRPC := withDummyServer(100) + setupMockServer(t, observertypes.RegisterQueryServer, method, input, output, extraGRPC...) + + tendermintMock := mocks.NewSDKClientWithErr(t, nil, 0) + + client := setupZetacoreClient(t, + withDefaultObserverKeys(), + withAccountRetriever(t, 100, 100), + withTendermint(tendermintMock), + ) t.Run("add tx hash success", func(t *testing.T) { - zetacoreBroadcast = MockBroadcast - hash, err := client.AddOutboundTracker(chains.BscMainnet.ChainId, 123, "", nil, "", 456) - require.NoError(t, err) - require.Equal(t, sampleHash, hash) + tendermintMock.SetBroadcastTxHash(sampleHash) + hash, err := client.AddOutboundTracker(ctx, chainID, nonce, "", nil, "", 456) + assert.NoError(t, err) + assert.Equal(t, sampleHash, hash) }) t.Run("add tx hash fail", func(t *testing.T) { - zetacoreBroadcast = MockBroadcastError - hash, err := client.AddOutboundTracker(chains.BscMainnet.ChainId, 123, "", nil, "", 456) - require.Error(t, err) - require.Equal(t, "", hash) + tendermintMock.SetError(errors.New("broadcast error")) + hash, err := client.AddOutboundTracker(ctx, chainID, nonce, "", nil, "", 456) + assert.Error(t, err) + assert.Empty(t, hash) }) } func TestZetacore_SetTSS(t *testing.T) { - client, err := setupZetacoreClient() - require.NoError(t, err) - address := sdktypes.AccAddress(mocks.TestKeyringPair.PubKey().Address().Bytes()) - client.keys = keys.NewKeysWithKeybase(mocks.NewKeyring(), address, testSigner, "") + ctx := context.Background() + + extraGRPC := withDummyServer(100) + setupMockServer(t, crosschaintypes.RegisterMsgServer, skipMethod, nil, nil, extraGRPC...) + + client := setupZetacoreClient(t, + withDefaultObserverKeys(), + withAccountRetriever(t, 100, 100), + withTendermint(mocks.NewSDKClientWithErr(t, nil, 0).SetBroadcastTxHash(sampleHash)), + ) t.Run("set tss success", func(t *testing.T) { - zetacoreBroadcast = MockBroadcast - hash, err := client.SetTSS( + hash, err := client.PostVoteTSS( + ctx, "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", 9987, chains.ReceiveStatus_success, @@ -204,6 +233,8 @@ func TestZetacore_SetTSS(t *testing.T) { } func TestZetacore_UpdateZetacoreContext(t *testing.T) { + ctx := context.Background() + //Setup server for multiple grpc calls listener, err := net.Listen("tcp", "127.0.0.1:9090") require.NoError(t, err) @@ -342,32 +373,37 @@ func TestZetacore_UpdateZetacoreContext(t *testing.T) { )(t) server.Serve() - defer closeMockServer(t, server) + defer server.Close() - client, err := setupZetacoreClient() - require.NoError(t, err) address := sdktypes.AccAddress(mocks.TestKeyringPair.PubKey().Address().Bytes()) - client.keys = keys.NewKeysWithKeybase(mocks.NewKeyring(), address, testSigner, "") - client.EnableMockSDKClient(mocks.NewSDKClientWithErr(nil, 0)) + client := setupZetacoreClient(t, + withObserverKeys(keys.NewKeysWithKeybase(mocks.NewKeyring(), address, testSigner, "")), + withTendermint(mocks.NewSDKClientWithErr(t, nil, 0)), + ) t.Run("zetacore update success", func(t *testing.T) { - cfg := config.NewConfig() - appContext := context.New(cfg, zerolog.Nop()) - zetacoreBroadcast = MockBroadcast - err := client.UpdateZetacoreContext(appContext, false, zerolog.Logger{}) + cfg := config.New(false) + appContext := zctx.New(cfg, zerolog.Nop()) + err := client.UpdateZetacoreContext(ctx, appContext, false, zerolog.Logger{}) require.NoError(t, err) }) } func TestZetacore_PostBlameData(t *testing.T) { - client, err := setupZetacoreClient() - require.NoError(t, err) - address := sdktypes.AccAddress(mocks.TestKeyringPair.PubKey().Address().Bytes()) - client.keys = keys.NewKeysWithKeybase(mocks.NewKeyring(), address, testSigner, "") + ctx := context.Background() + + extraGRPC := withDummyServer(100) + setupMockServer(t, observertypes.RegisterQueryServer, skipMethod, nil, nil, extraGRPC...) + + client := setupZetacoreClient(t, + withDefaultObserverKeys(), + withAccountRetriever(t, 100, 100), + withTendermint(mocks.NewSDKClientWithErr(t, nil, 0).SetBroadcastTxHash(sampleHash)), + ) t.Run("post blame data success", func(t *testing.T) { - zetacoreBroadcast = MockBroadcast - hash, err := client.PostBlameData( + hash, err := client.PostVoteBlameData( + ctx, &blame.Blame{ FailReason: "", IsUnicast: false, @@ -376,22 +412,29 @@ func TestZetacore_PostBlameData(t *testing.T) { chains.BscMainnet.ChainId, "102394876-bsc", ) - require.NoError(t, err) - require.Equal(t, sampleHash, hash) + assert.NoError(t, err) + assert.Equal(t, sampleHash, hash) }) } func TestZetacore_PostVoteBlockHeader(t *testing.T) { - client, err := setupZetacoreClient() - require.NoError(t, err) - address := sdktypes.AccAddress(mocks.TestKeyringPair.PubKey().Address().Bytes()) - client.keys = keys.NewKeysWithKeybase(mocks.NewKeyring(), address, testSigner, "") + ctx := context.Background() + + extraGRPC := withDummyServer(100) + setupMockServer(t, observertypes.RegisterQueryServer, skipMethod, nil, nil, extraGRPC...) + + client := setupZetacoreClient(t, + withDefaultObserverKeys(), + withAccountRetriever(t, 100, 100), + withTendermint(mocks.NewSDKClientWithErr(t, nil, 0).SetBroadcastTxHash(sampleHash)), + ) + blockHash, err := hex.DecodeString(ethBlockHash) require.NoError(t, err) t.Run("post add block header success", func(t *testing.T) { - zetacoreBroadcast = MockBroadcast hash, err := client.PostVoteBlockHeader( + ctx, chains.Ethereum.ChainId, blockHash, 18495266, @@ -403,6 +446,8 @@ func TestZetacore_PostVoteBlockHeader(t *testing.T) { } func TestZetacore_PostVoteInbound(t *testing.T) { + ctx := context.Background() + address := sdktypes.AccAddress(mocks.TestKeyringPair.PubKey().Address().Bytes()) expectedOutput := observertypes.QueryHasVotedResponse{HasVoted: false} @@ -411,18 +456,18 @@ func TestZetacore_PostVoteInbound(t *testing.T) { VoterAddress: address.String(), } method := "/zetachain.zetacore.observer.Query/HasVoted" - server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) - client, err := setupZetacoreClient() - require.NoError(t, err) - client.keys = keys.NewKeysWithKeybase(mocks.NewKeyring(), address, testSigner, "") - client.EnableMockSDKClient(mocks.NewSDKClientWithErr(nil, 0)) + extraGRPC := withDummyServer(100) + setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput, extraGRPC...) + + client := setupZetacoreClient(t, + withDefaultObserverKeys(), + withAccountRetriever(t, 100, 100), + withTendermint(mocks.NewSDKClientWithErr(t, nil, 0).SetBroadcastTxHash(sampleHash)), + ) t.Run("post inbound vote already voted", func(t *testing.T) { - zetacoreBroadcast = MockBroadcast - hash, _, err := client.PostVoteInbound(100, 200, &crosschaintypes.MsgVoteInbound{ + hash, _, err := client.PostVoteInbound(ctx, 100, 200, &crosschaintypes.MsgVoteInbound{ Creator: address.String(), }) require.NoError(t, err) @@ -433,7 +478,6 @@ func TestZetacore_PostVoteInbound(t *testing.T) { func TestZetacore_GetInboundVoteMessage(t *testing.T) { address := sdktypes.AccAddress(mocks.TestKeyringPair.PubKey().Address().Bytes()) t.Run("get inbound vote message", func(t *testing.T) { - zetacoreBroadcast = MockBroadcast msg := GetInboundVoteMessage( address.String(), chains.Ethereum.ChainId, @@ -453,71 +497,87 @@ func TestZetacore_GetInboundVoteMessage(t *testing.T) { } func TestZetacore_MonitorVoteInboundResult(t *testing.T) { + ctx := context.Background() + address := sdktypes.AccAddress(mocks.TestKeyringPair.PubKey().Address().Bytes()) - client, err := setupZetacoreClient() - require.NoError(t, err) - client.keys = keys.NewKeysWithKeybase(mocks.NewKeyring(), address, testSigner, "") - client.EnableMockSDKClient(mocks.NewSDKClientWithErr(nil, 0)) + client := setupZetacoreClient(t, + withObserverKeys(keys.NewKeysWithKeybase(mocks.NewKeyring(), address, testSigner, "")), + withTendermint(mocks.NewSDKClientWithErr(t, nil, 0)), + ) t.Run("monitor inbound vote", func(t *testing.T) { - zetacoreBroadcast = MockBroadcast - client.MonitorVoteInboundResult(sampleHash, 1000, &crosschaintypes.MsgVoteInbound{ + err := client.MonitorVoteInboundResult(ctx, sampleHash, 1000, &crosschaintypes.MsgVoteInbound{ Creator: address.String(), }) - // Nothing to verify against this function - // Just running through without panic + + require.NoError(t, err) }) } func TestZetacore_PostVoteOutbound(t *testing.T) { + const ( + blockHeight = 1234 + accountNum = 10 + accountSeq = 10 + ) + + ctx := context.Background() + address := sdktypes.AccAddress(mocks.TestKeyringPair.PubKey().Address().Bytes()) expectedOutput := observertypes.QueryHasVotedResponse{HasVoted: false} input := observertypes.QueryHasVotedRequest{ - BallotIdentifier: "0xc1ebc3b76ebcc7ff9a9e543062c31b9f9445506e4924df858460bf2926be1a25", + BallotIdentifier: "0xf52f379287561dd07869de72b09fb56b7f6dfdda65b01c25882722e315f333f1", VoterAddress: address.String(), } method := "/zetachain.zetacore.observer.Query/HasVoted" - server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) - server.Serve() - defer closeMockServer(t, server) - client, err := setupZetacoreClient() - require.NoError(t, err) - client.keys = keys.NewKeysWithKeybase(mocks.NewKeyring(), address, testSigner, "") - client.EnableMockSDKClient(mocks.NewSDKClientWithErr(nil, 0)) + extraGRPC := withDummyServer(blockHeight) + + server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput, extraGRPC...) + require.NotNil(t, server) + + client := setupZetacoreClient(t, + withDefaultObserverKeys(), + withTendermint(mocks.NewSDKClientWithErr(t, nil, 0).SetBroadcastTxHash(sampleHash)), + withAccountRetriever(t, accountNum, accountSeq), + ) - zetacoreBroadcast = MockBroadcast - hash, ballot, err := client.PostVoteOutbound( + msg := crosschaintypes.NewMsgVoteOutbound( + address.String(), sampleHash, sampleHash, - 1234, + blockHeight, 1000, - big.NewInt(100), + math.NewInt(100), 1200, - big.NewInt(500), + math.NewUint(500), chains.ReceiveStatus_success, - chains.Ethereum, + chains.Ethereum.ChainId, 10001, - coin.CoinType_Gas) - require.NoError(t, err) - require.Equal(t, sampleHash, hash) - require.Equal(t, "0xc1ebc3b76ebcc7ff9a9e543062c31b9f9445506e4924df858460bf2926be1a25", ballot) + coin.CoinType_Gas, + ) + + hash, ballot, err := client.PostVoteOutbound(ctx, 100_000, 200_000, msg) + + assert.NoError(t, err) + assert.Equal(t, sampleHash, hash) + assert.Equal(t, "0xf52f379287561dd07869de72b09fb56b7f6dfdda65b01c25882722e315f333f1", ballot) } func TestZetacore_MonitorVoteOutboundResult(t *testing.T) { + ctx := context.Background() + address := sdktypes.AccAddress(mocks.TestKeyringPair.PubKey().Address().Bytes()) - client, err := setupZetacoreClient() - require.NoError(t, err) - client.keys = keys.NewKeysWithKeybase(mocks.NewKeyring(), address, testSigner, "") - client.EnableMockSDKClient(mocks.NewSDKClientWithErr(nil, 0)) + client := setupZetacoreClient(t, + withObserverKeys(keys.NewKeysWithKeybase(mocks.NewKeyring(), address, testSigner, "")), + withTendermint(mocks.NewSDKClientWithErr(t, nil, 0)), + ) t.Run("monitor outbound vote", func(t *testing.T) { - zetacoreBroadcast = MockBroadcast - client.MonitorVoteOutboundResult(sampleHash, 1000, &crosschaintypes.MsgVoteOutbound{ - Creator: address.String(), - }) - // Nothing to verify against this function - // Just running through without panic + msg := &crosschaintypes.MsgVoteOutbound{Creator: address.String()} + + err := client.MonitorVoteOutboundResult(ctx, sampleHash, 1000, msg) + assert.NoError(t, err) }) }