diff --git a/.github/workflows/linuxpackage.yml b/.github/workflows/linuxpackage.yml deleted file mode 100644 index 99d28d58..00000000 --- a/.github/workflows/linuxpackage.yml +++ /dev/null @@ -1,110 +0,0 @@ -name: Linux package - -on: - push: - tags: - - "v*.*.*" -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - name: Install Go - uses: actions/setup-go@v1 - with: - go-version: 1.13 - - - name: Set up Ruby 2.6 - uses: actions/setup-ruby@v1 - with: - ruby-version: 2.6 - - - name: Retrieve release version - run: echo "::set-env name=RELEASE_VERSION::${GITHUB_REF/refs\/tags\/v/}" - - - name: Build package - run: | - set -x - - sudo apt-get -yqq install libpq-dev build-essential - gem install --no-document fpm - fpm --version - - export GOBIN=$(go env GOPATH)/bin - make install - mkdir -p build - cp -rf $GOBIN/* build/ - - cat > heimdalld.service <<- "EOF" - [Unit] - Description=heimdall - [Service] - WorkingDirectory=/usr/bin/ - ExecStart=/bin/bash -c '/usr/bin/heimdalld start --home /etc/heimdall/' - Type=simple - User=root - EOF - - cat > heimdalld-bridge.service <<- "EOF" - [Unit] - Description=heimdall-bridge - [Service] - WorkingDirectory=/usr/bin/ - ExecStart=/bin/bash -c "/usr/bin/bridge start --all --home /etc/heimdall/" - Type=simple - User=root - EOF - - cat > heimdalld-rest-server.service <<- "EOF" - [Unit] - Description=heimdalld-rest-server - [Service] - WorkingDirectory=/usr/bin/ - ExecStart=/bin/bash -c "/usr/bin/heimdalld rest-server --home /etc/heimdall/" - Type=simple - User=root - EOF - - cat > after_install.sh <<- "EOF" - #!/bin/bash - mkdir -p /etc/heimdall/ - echo alias heimdalld='"sudo heimdalld --home /etc/heimdall"' >> ~/.bashrc - echo alias heimdallcli='"sudo heimdallcli --home /etc/heimdall"' >> ~/.bashrc - source ~/.bashrc - EOF - - fpm -s dir -t deb --deb-user root --deb-group root -n matic-heimdall -v ${{ env.RELEASE_VERSION }} --config-files heimdalld.service \ - --after-install after_install.sh \ - heimdalld.service=/etc/systemd/system/ \ - heimdalld-rest-server.service heimdalld-rest-server.service=/etc/systemd/system/ \ - heimdalld-bridge.service heimdalld-bridge.service=/etc/systemd/system/ \ - build/bridge=/usr/bin/ \ - build/heimdallcli=/usr/bin/ \ - build/heimdalld=/usr/bin/ - - mkdir packages-v${{ env.RELEASE_VERSION }} - - mv matic-heimdall_${{ env.RELEASE_VERSION }}_amd64.deb packages-v${{ env.RELEASE_VERSION }}/ - - ls packages-v${{ env.RELEASE_VERSION }}/ - - - name: S3 upload - uses: jakejarvis/s3-sync-action@master - with: - args: --acl public-read - env: - AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - AWS_REGION: "us-east-1" # optional: defaults to us-east-1 - SOURCE_DIR: "packages-v${{ env.RELEASE_VERSION }}" - DEST_DIR: "v${{ env.RELEASE_VERSION }}" - - - name: Slack Notification - uses: rtCamp/action-slack-notify@v2.0.0 - env: - SLACK_CHANNEL: code-releases - SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} - SLACK_TITLE: "New linux package for Heimdall v${{ env.RELEASE_VERSION }} just got released" - SLACK_MESSAGE: "Package has been uploaded to S3 bucket for public use and available at https://matic-public.s3.amazonaws.com/v${{ env.RELEASE_VERSION }}/matic-heimdall_${{ env.RELEASE_VERSION }}_amd64.deb" diff --git a/README.md b/README.md index 5934921f..617c2df6 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Make sure your have go1.11+ already installed ### Install ```bash $ make install -``` +``` ### Run-delivery ```bash diff --git a/bridge/setu/processor/staking.go b/bridge/setu/processor/staking.go index d8053b5d..d3bc4da1 100644 --- a/bridge/setu/processor/staking.go +++ b/bridge/setu/processor/staking.go @@ -3,6 +3,7 @@ package processor import ( "context" "encoding/json" + "fmt" "time" checkpointTypes "github.com/maticnetwork/heimdall/checkpoint/types" @@ -22,6 +23,10 @@ import ( hmTypes "github.com/maticnetwork/heimdall/types" ) +const ( + defaultDelayDuration = 10 * time.Second +) + // StakingProcessor - process staking related events type StakingProcessor struct { BaseProcessor @@ -200,6 +205,17 @@ func (sp *StakingProcessor) sendUnstakeInitToHeimdall(eventName string, logBytes return nil } + validNonce, nonceDelay, err := sp.checkValidNonce(event.ValidatorId.Uint64(), event.Nonce.Uint64()) + if err != nil { + sp.Logger.Error("Error while validating nonce for the validator", "error", err) + return err + } + + if !validNonce { + sp.Logger.Info("Ignoring task to send unstake-init to heimdall as nonce is out of order") + return tasks.NewErrRetryTaskLater("Nonce out of order", defaultDelayDuration*time.Duration(nonceDelay)) + } + sp.Logger.Info( "✅ Received task to send unstake-init to heimdall", "event", eventName, @@ -327,6 +343,18 @@ func (sp *StakingProcessor) sendSignerChangeToHeimdall(eventName string, logByte ) return nil } + + validNonce, nonceDelay, err := sp.checkValidNonce(event.ValidatorId.Uint64(), event.Nonce.Uint64()) + if err != nil { + sp.Logger.Error("Error while validating nonce for the validator", "error", err) + return err + } + + if !validNonce { + sp.Logger.Info("Ignoring task to send signer-change to heimdall as nonce is out of order") + return tasks.NewErrRetryTaskLater("Nonce out of order", defaultDelayDuration*time.Duration(nonceDelay)) + } + sp.Logger.Info( "✅ Received task to send signer-change to heimdall", "event", eventName, @@ -681,3 +709,61 @@ func (sp *StakingProcessor) getStakingContext(rootChain string) (*StakingContext ChainmanagerParams: chainmanagerParams, }, nil } + +func (sp *StakingProcessor) checkValidNonce(validatorId uint64, txnNonce uint64) (bool, uint64, error) { + currentNonce, currentHeight, err := util.GetValidatorNonce(sp.cliCtx, validatorId) + if err != nil { + sp.Logger.Error("Failed to fetch validator nonce and height data from API", "validatorId", validatorId) + return false, 0, err + } + + if currentNonce+1 != txnNonce { + sp.Logger.Error("Nonce for the given event not in order", "validatorId", validatorId, "currentNonce", currentNonce, "txnNonce", txnNonce) + return false, txnNonce - currentNonce, nil + } + + stakingTxnCount, err := queryTxCount(sp.cliCtx, validatorId, currentHeight) + if err != nil { + sp.Logger.Error("Failed to query stake txns by txquery for the given validator", "validatorId", validatorId) + return false, 0, err + } + + if stakingTxnCount != 0 { + sp.Logger.Info("Recent staking txn count for the given validator is not zero", "validatorId", validatorId, "currentNonce", currentNonce, "txnNonce", txnNonce, "currentHeight", currentHeight) + return false, 1, nil + } + + return true, 0, nil +} + +func queryTxCount(cliCtx cliContext.CLIContext, validatorId uint64, currentHeight int64) (int, error) { + const ( + defaultPage = 1 + defaultLimit = 30 // should be consistent with tendermint/tendermint/rpc/core/pipe.go:19 + ) + + stakingTxnMsgMap := map[string]string{ + "validator-stake-update": "stake-update", + "validator-join": "validator-join", + "signer-update": "signer-update", + "validator-exit": "validator-exit", + } + + for msg, action := range stakingTxnMsgMap { + events := []string{ + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, msg), + fmt.Sprintf("%s.%s=%d", action, "validator-id", validatorId), + fmt.Sprintf("%s.%s>%d", "tx", "height", currentHeight-3), + } + + searchResult, err := helper.QueryTxsByEvents(cliCtx, events, defaultPage, defaultLimit) + if err != nil { + return 0, err + } + + if searchResult.TotalCount != 0 { + return searchResult.TotalCount, nil + } + } + return 0, nil +} diff --git a/bridge/setu/util/common.go b/bridge/setu/util/common.go index 9906f6a5..0bf80203 100644 --- a/bridge/setu/util/common.go +++ b/bridge/setu/util/common.go @@ -142,8 +142,8 @@ func IsInProposerList(cliCtx cliContext.CLIContext, count uint64) (bool, error) } //default offset 0 -func CalculateTaskDelay(cliCtx cliContext.CLIContext) (bool, time.Duration){ - return CalculateTaskDelayWithOffset(cliCtx,0) +func CalculateTaskDelay(cliCtx cliContext.CLIContext) (bool, time.Duration) { + return CalculateTaskDelayWithOffset(cliCtx, 0) } // CalculateTaskDelay calculates delay required for current validator to propose the tx @@ -462,3 +462,27 @@ func GetNextStakingRecord(cliCtx cliContext.CLIContext, rootChain string) (*stak return &stakingRecord, nil } + +// GetValidatorNonce fethes validator nonce and height +func GetValidatorNonce(cliCtx cliContext.CLIContext, validatorID uint64) (uint64, int64, error) { + var validator hmtypes.Validator + + result, err := helper.FetchFromAPI(cliCtx, + helper.GetHeimdallServerEndpoint(fmt.Sprintf(ValidatorURL, strconv.FormatUint(validatorID, 10))), + ) + + if err != nil { + logger.Error("Error fetching validator data", "error", err) + return 0, 0, err + } + + err = json.Unmarshal(result.Result, &validator) + if err != nil { + logger.Error("error unmarshalling validator data", "error", err) + return 0, 0, err + } + + logger.Debug("Validator data recieved ", "validator", validator.String()) + + return validator.Nonce, result.Height, nil +} diff --git a/checkpoint/client/cli/tx.go b/checkpoint/client/cli/tx.go index bc5470e2..613d51b6 100644 --- a/checkpoint/client/cli/tx.go +++ b/checkpoint/client/cli/tx.go @@ -105,31 +105,26 @@ func SendCheckpointTx(cdc *codec.Codec) *cobra.Command { } // start block - startBlockStr := viper.GetString(FlagStartBlock) if startBlockStr == "" { return fmt.Errorf("start block cannot be empty") } - startBlock, err := strconv.ParseUint(startBlockStr, 10, 64) if err != nil { return err } // end block - endBlockStr := viper.GetString(FlagEndBlock) if endBlockStr == "" { return fmt.Errorf("end block cannot be empty") } - endBlock, err := strconv.ParseUint(endBlockStr, 10, 64) if err != nil { return err } // root hash - rootHashStr := viper.GetString(FlagRootHash) if rootHashStr == "" { return fmt.Errorf("root hash cannot be empty") diff --git a/checkpoint/handler_test.go b/checkpoint/handler_test.go index ce9f663b..9ed44aef 100644 --- a/checkpoint/handler_test.go +++ b/checkpoint/handler_test.go @@ -8,6 +8,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/maticnetwork/heimdall/app" + cmTypes "github.com/maticnetwork/heimdall/chainmanager/types" "github.com/maticnetwork/heimdall/checkpoint/types" errs "github.com/maticnetwork/heimdall/common" @@ -425,7 +426,7 @@ func (suite *HandlerTestSuite) SendCheckpoint(header hmTypes.Checkpoint) (res sd hmTypes.RootChainTypeStake, ) - suite.contractCaller.On("CheckIfBlocksExist", header.EndBlock).Return(true) + suite.contractCaller.On("CheckIfBlocksExist", header.EndBlock+cmTypes.DefaultMaticchainTxConfirmations).Return(true) suite.contractCaller.On("GetRootHash", header.StartBlock, header.EndBlock, uint64(1024)).Return(header.RootHash.Bytes(), nil) // send checkpoint to handler diff --git a/checkpoint/side_handler.go b/checkpoint/side_handler.go index 5c2366a3..b80285c3 100644 --- a/checkpoint/side_handler.go +++ b/checkpoint/side_handler.go @@ -41,12 +41,13 @@ func NewSideTxHandler(k Keeper, contractCaller helper.IContractCaller) hmTypes.S func SideHandleMsgCheckpoint(ctx sdk.Context, k Keeper, msg types.MsgCheckpoint, contractCaller helper.IContractCaller) (result abci.ResponseDeliverSideTx) { // get params params := k.GetParams(ctx) + maticTxConfirmations := k.ck.GetParams(ctx).MaticchainTxConfirmations // logger logger := k.Logger(ctx) // validate checkpoint - validCheckpoint, err := types.ValidateCheckpoint(msg.StartBlock, msg.EndBlock, msg.RootHash, params.MaxCheckpointLength, contractCaller) + validCheckpoint, err := types.ValidateCheckpoint(msg.StartBlock, msg.EndBlock, msg.RootHash, params.MaxCheckpointLength, contractCaller, maticTxConfirmations) if err != nil { logger.Error("Error validating checkpoint", "error", err, diff --git a/checkpoint/side_handler_test.go b/checkpoint/side_handler_test.go index ae1cce44..f7e266cc 100644 --- a/checkpoint/side_handler_test.go +++ b/checkpoint/side_handler_test.go @@ -8,6 +8,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/maticnetwork/heimdall/app" + cmTypes "github.com/maticnetwork/heimdall/chainmanager/types" "github.com/maticnetwork/heimdall/checkpoint" chSim "github.com/maticnetwork/heimdall/checkpoint/simulation" "github.com/maticnetwork/heimdall/checkpoint/types" @@ -90,7 +91,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgCheckpoint() { hmTypes.RootChainTypeEth, ) - suite.contractCaller.On("CheckIfBlocksExist", header.EndBlock).Return(true) + suite.contractCaller.On("CheckIfBlocksExist", header.EndBlock+cmTypes.DefaultMaticchainTxConfirmations).Return(true) suite.contractCaller.On("GetRootHash", header.StartBlock, header.EndBlock, uint64(1024)).Return(header.RootHash.Bytes(), nil) result := suite.sideHandler(ctx, msgCheckpoint) @@ -115,7 +116,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgCheckpoint() { hmTypes.RootChainTypeEth, ) - suite.contractCaller.On("CheckIfBlocksExist", header.EndBlock).Return(true) + suite.contractCaller.On("CheckIfBlocksExist", header.EndBlock+cmTypes.DefaultMaticchainTxConfirmations).Return(true) suite.contractCaller.On("GetRootHash", header.StartBlock, header.EndBlock, uint64(1024)).Return(nil, nil) result := suite.sideHandler(ctx, msgCheckpoint) @@ -143,7 +144,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgCheckpoint() { hmTypes.RootChainTypeEth, ) - suite.contractCaller.On("CheckIfBlocksExist", header.EndBlock).Return(true) + suite.contractCaller.On("CheckIfBlocksExist", header.EndBlock+cmTypes.DefaultMaticchainTxConfirmations).Return(true) suite.contractCaller.On("GetRootHash", header.StartBlock, header.EndBlock, uint64(1024)).Return([]byte{1}, nil) result := suite.sideHandler(ctx, msgCheckpoint) diff --git a/checkpoint/types/merkel.go b/checkpoint/types/merkel.go index 382f8aca..45341d14 100644 --- a/checkpoint/types/merkel.go +++ b/checkpoint/types/merkel.go @@ -15,9 +15,9 @@ import ( ) // ValidateCheckpoint - Validates if checkpoint rootHash matches or not -func ValidateCheckpoint(start uint64, end uint64, rootHash hmTypes.HeimdallHash, checkpointLength uint64, contractCaller helper.IContractCaller) (bool, error) { +func ValidateCheckpoint(start uint64, end uint64, rootHash hmTypes.HeimdallHash, checkpointLength uint64, contractCaller helper.IContractCaller, confirmations uint64) (bool, error) { // Check if blocks exist locally - if !contractCaller.CheckIfBlocksExist(end) { + if !contractCaller.CheckIfBlocksExist(end + confirmations) { return false, errors.New("blocks not found locally") } diff --git a/helper/call.go b/helper/call.go index 1788283d..39ce219f 100644 --- a/helper/call.go +++ b/helper/call.go @@ -5,6 +5,7 @@ import ( "context" "encoding/json" "errors" + "fmt" "math/big" "net/http" "strconv" @@ -764,21 +765,17 @@ func (c *ContractCaller) CurrentStateCounter(stateSenderInstance *statesender.St return result } -// CheckIfBlocksExist - check if latest block number is greater than end block +// CheckIfBlocksExist - check if the given block exists on local chain func (c *ContractCaller) CheckIfBlocksExist(end uint64) bool { - // Get Latest block number. - var latestBlock *ethTypes.Header + // Get block by number. + var block *ethTypes.Header - err := c.MaticChainRPC.Call(&latestBlock, "eth_getBlockByNumber", "latest", false) + err := c.MaticChainRPC.Call(&block, "eth_getBlockByNumber", fmt.Sprintf("0x%x", end), false) if err != nil { return false } - if end > latestBlock.Number.Uint64() { - return false - } - - return true + return end == block.Number.Uint64() } // diff --git a/helper/config.go b/helper/config.go index b3a144f2..7402000e 100644 --- a/helper/config.go +++ b/helper/config.go @@ -73,8 +73,8 @@ const ( DefaultStakingPollInterval = 1 * time.Minute DefaultStartListenBlock = 0 - DefaultMainchainGasLimit = uint64(5000000) - DefaultTronFeeLimit = uint64(1000000000) + DefaultMainchainMaxGasPrice = 400000000000 // 400 Gwei + DefaultTronFeeLimit = uint64(1000000000) DefaultEthBusyLimitTxs = 1000 DefaultBscBusyLimitTxs = 1000 @@ -118,9 +118,10 @@ type Configuration struct { // tron TronGridApiKey string `mapstructure:"tron_grid_api_key"` // tron api key - MainchainGasLimit uint64 `mapstructure:"main_chain_gas_limit"` // gas limit to mainchain transaction. eg....submit checkpoint. TronchainFeeLimit uint64 `mapstructure:"tron_chain_fee_limit"` // gas limit to tron transaction. eg....submit checkpoint. + MainchainMaxGasPrice int64 `mapstructure:"main_chain_max_gas_price"` // max gas price to mainchain transaction. eg....submit checkpoint. + // config related to bridge CheckpointerPollInterval time.Duration `mapstructure:"checkpoint_poll_interval"` // Poll interval for checkpointer service to send new checkpoints or missing ACK EthSyncerPollInterval time.Duration `mapstructure:"eth_syncer_poll_interval"` // Poll interval for syncher service to sync for changes on eth chain @@ -269,9 +270,10 @@ func GetDefaultHeimdallConfig() Configuration { AmqpURL: DefaultAmqpURL, DeliveryServerURL: DefaultDeliveryServerURL, - MainchainGasLimit: DefaultMainchainGasLimit, TronchainFeeLimit: DefaultTronFeeLimit, + MainchainMaxGasPrice: DefaultMainchainMaxGasPrice, + CheckpointerPollInterval: DefaultCheckpointerPollInterval, EthSyncerPollInterval: DefaultSyncerPollInterval, BscSyncerPollInterval: DefaultBscSyncerPollInterval, diff --git a/helper/toml.go b/helper/toml.go index 8d76158e..5ba948e6 100644 --- a/helper/toml.go +++ b/helper/toml.go @@ -51,9 +51,11 @@ span_poll_interval = "{{ .SpanPollInterval }}" staking_poll_interval = "{{ .StakingPollInterval }}" #### gas limits #### -main_chain_gas_limit = "{{ .MainchainGasLimit }}" tron_chain_fee_limit = "{{ .TronchainFeeLimit }}" +#### gas price #### +main_chain_max_gas_price = "{{ .MainchainMaxGasPrice }}" + #### busy limits #### eth_unconfirmed_txs_busy_limit = "{{ .EthUnconfirmedTxsBusyLimit }}" bsc_unconfirmed_txs_busy_limit = "{{ .BscUnconfirmedTxsBusyLimit }}" diff --git a/helper/tx.go b/helper/tx.go index 7b933326..87bbc11b 100644 --- a/helper/tx.go +++ b/helper/tx.go @@ -45,6 +45,18 @@ func GenerateAuthObj(client *ethclient.Client, address common.Address, data []by if err != nil { return } + + mainChainMaxGasPrice := GetConfig().MainchainMaxGasPrice + // Check if configured or not, Use default in case of invalid value + if mainChainMaxGasPrice <= 0 { + mainChainMaxGasPrice = DefaultMainchainMaxGasPrice + } + if gasprice.Cmp(big.NewInt(mainChainMaxGasPrice)) == 1 { + Logger.Error("Gas price is more than max gas price", "gasprice", gasprice) + err = fmt.Errorf("gas price is more than max_gas_price, gasprice = %v, maxGasPrice = %d", gasprice, mainChainMaxGasPrice) + return + } + // fetch nonce nonce, err := client.PendingNonceAt(context.Background(), fromAddress) if err != nil { @@ -84,8 +96,7 @@ func (c *ContractCaller) SendCheckpoint(signedData []byte, sigs [][3]*big.Int, auth, err := GenerateAuthObj(client, rootChainAddress, data) if err != nil { Logger.Error("Unable to create auth object", "error", err) - Logger.Info("Setting custom gaslimit", "gaslimit", GetConfig().MainchainGasLimit) - auth.GasLimit = GetConfig().MainchainGasLimit + return err } s := make([]string, 0) @@ -118,8 +129,7 @@ func (c *ContractCaller) SendTick(signedData []byte, sigs []byte, slashManagerAd auth, err := GenerateAuthObj(GetMainClient(), slashManagerAddress, data) if err != nil { Logger.Error("Unable to create auth object", "error", err) - Logger.Info("Setting custom gaslimit", "gaslimit", GetConfig().MainchainGasLimit) - auth.GasLimit = GetConfig().MainchainGasLimit + return err } Logger.Info("Sending new tick", @@ -280,8 +290,7 @@ func (c *ContractCaller) SendMainStakingSync(syncMethod string, signedData []byt auth, err := GenerateAuthObj(client, stakingManager, data) if err != nil { Logger.Error("Unable to create auth object", "error", err) - Logger.Info("Setting custom gaslimit", "gaslimit", GetConfig().MainchainGasLimit) - auth.GasLimit = GetConfig().MainchainGasLimit + return err } s := make([]string, 0) diff --git a/staking/handler.go b/staking/handler.go index 747df8c2..81961c3f 100644 --- a/staking/handler.go +++ b/staking/handler.go @@ -136,7 +136,7 @@ func HandleMsgStakeUpdate(ctx sdk.Context, msg types.MsgStakeUpdate, k Keeper, c return hmCommon.ErrNoValidator(k.Codespace()).Result() } - if msg.Nonce <= validator.Nonce { + if msg.Nonce != validator.Nonce+1 { k.Logger(ctx).Error("Incorrect validator nonce") return hmCommon.ErrNonce(k.Codespace()).Result() } @@ -201,7 +201,7 @@ func HandleMsgSignerUpdate(ctx sdk.Context, msg types.MsgSignerUpdate, k Keeper, } // check nonce validity - if msg.Nonce <= validator.Nonce { + if msg.Nonce != validator.Nonce+1 { k.Logger(ctx).Error("Incorrect validator nonce") return hmCommon.ErrNonce(k.Codespace()).Result() } @@ -256,7 +256,7 @@ func HandleMsgValidatorExit(ctx sdk.Context, msg types.MsgValidatorExit, k Keepe } // check nonce validity - if msg.Nonce <= validator.Nonce { + if msg.Nonce != validator.Nonce+1 { k.Logger(ctx).Error("Incorrect validator nonce") return hmCommon.ErrNonce(k.Codespace()).Result() }