Skip to content

Commit

Permalink
Merge pull request #18 from strangelove-ventures/justin/fix-invalid-char
Browse files Browse the repository at this point in the history
fix: add checks on `ResultBroadcastTx` to avoid NPE + cleanup error handling code
  • Loading branch information
bd21 authored Nov 21, 2023
2 parents 2dc7fdd + 9003c00 commit 8c2d916
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 43 deletions.
9 changes: 5 additions & 4 deletions cmd/ethereum/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@ package ethereum
import (
"bytes"
"context"
"cosmossdk.io/log"
"embed"
"fmt"
"math/big"
"os"

"cosmossdk.io/log"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/pascaldekloe/etherstream"
"github.com/strangelove-ventures/noble-cctp-relayer/config"
"github.com/strangelove-ventures/noble-cctp-relayer/types"
"math/big"
"os"
)

//go:embed abi/MessageTransmitter.json
Expand Down Expand Up @@ -66,7 +67,7 @@ func StartListener(cfg config.Config, logger log.Logger, processingQueue chan *t
for _, historicalLog := range history {
parsedMsg, err := types.EvmLogToMessageState(messageTransmitterABI, messageSent, &historicalLog)
if err != nil {
logger.Error("Unable to parse history log into MessageState, skipping")
logger.Error("Unable to parse history log into MessageState, skipping", "err", err)
continue
}
logger.Info(fmt.Sprintf("New historical msg from source domain %d with tx hash %s", parsedMsg.SourceDomain, parsedMsg.SourceTxHash))
Expand Down
86 changes: 55 additions & 31 deletions cmd/noble/broadcast.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,50 +131,74 @@ func Broadcast(
}

rpcResponse, err := rpcClient.BroadcastTxSync(context.Background(), txBytes)
if err == nil && rpcResponse.Code == 0 {
msg.Status = types.Complete
return rpcResponse, nil
}
if err != nil || (rpcResponse != nil && rpcResponse.Code != 0) {
// Log the error
logger.Error(fmt.Sprintf("error during broadcast: %s", getErrorString(err, rpcResponse)))

if err != nil || rpcResponse == nil {
// Log retry information
logger.Info(fmt.Sprintf("Retrying in %d seconds", cfg.Networks.Destination.Noble.BroadcastRetryInterval))
time.Sleep(time.Duration(cfg.Networks.Destination.Noble.BroadcastRetryInterval) * time.Second)
continue
}

// Log details for non-zero response code
logger.Error(fmt.Sprintf("received non-zero: %d - %s", rpcResponse.Code, rpcResponse.Log))

// check tx response code
logger.Error(fmt.Sprintf("received non zero : %d - %s", rpcResponse.Code, rpcResponse.Log))

if err == nil && rpcResponse.Code == 32 {
// on account sequence mismatch, extract correct account sequence and retry
pattern := `expected (\d+), got (\d+)`
re := regexp.MustCompile(pattern)
match := re.FindStringSubmatch(rpcResponse.Log)

var newAccountSequence int64
if len(match) == 3 {
// Extract the numbers from the match.
newAccountSequence, _ = strconv.ParseInt(match[1], 10, 0)
} else {
// otherwise, just request the account sequence
_, newAccountSequence, err = GetNobleAccountNumberSequence(cfg.Networks.Destination.Noble.API, nobleAddress)
if err != nil {
logger.Error("unable to retrieve account number")
}
// Handle specific error code (32)
if rpcResponse.Code == 32 {
newAccountSequence := extractAccountSequence(logger, rpcResponse.Log, nobleAddress, cfg.Networks.Destination.Noble.API)
logger.Debug(fmt.Sprintf("retrying with new account sequence: %d", newAccountSequence))
sequenceMap.Put(cfg.Networks.Destination.Noble.DomainId, newAccountSequence)
}
logger.Debug(fmt.Sprintf("error during broadcast: %s", rpcResponse.Log))
logger.Debug(fmt.Sprintf("retrying with new account sequence: %d", newAccountSequence))
sequenceMap.Put(cfg.Networks.Destination.Noble.DomainId, newAccountSequence)
}
if err != nil {
logger.Error(fmt.Sprintf("error during broadcast: %s", err.Error()))

// Log retry information
logger.Info(fmt.Sprintf("Retrying in %d seconds", cfg.Networks.Destination.Noble.BroadcastRetryInterval))
time.Sleep(time.Duration(cfg.Networks.Destination.Noble.BroadcastRetryInterval) * time.Second)
continue
}

logger.Info(fmt.Sprintf("Retrying in %d seconds", cfg.Networks.Destination.Noble.BroadcastRetryInterval))
time.Sleep(time.Duration(cfg.Networks.Destination.Noble.BroadcastRetryInterval) * time.Second)
// Tx was successfully broadcast
msg.Status = types.Complete
return rpcResponse, nil
}

msg.Status = types.Failed

return nil, errors.New("reached max number of broadcast attempts")
}

// getErrorString returns the appropriate value to log when tx broadcast errors are encountered.
func getErrorString(err error, rpcResponse *ctypes.ResultBroadcastTx) string {
if rpcResponse != nil {
return rpcResponse.Log
}
return err.Error()
}

// extractAccountSequence attempts to extract the account sequence number from the RPC response logs when
// account sequence mismatch errors are encountered. If the account sequence number cannot be extracted from the logs,
// it is retrieved by making a request to the API endpoint.
func extractAccountSequence(logger log.Logger, rpcResponseLog, nobleAddress, nobleAPI string) int64 {
pattern := `expected (\d+), got (\d+)`
re := regexp.MustCompile(pattern)
match := re.FindStringSubmatch(rpcResponseLog)

if len(match) == 3 {
// Extract the numbers from the match.
newAccountSequence, _ := strconv.ParseInt(match[1], 10, 0)
return newAccountSequence
}

// Otherwise, just request the account sequence
_, newAccountSequence, err := GetNobleAccountNumberSequence(nobleAPI, nobleAddress)
if err != nil {
logger.Error("unable to retrieve account number")
}

return newAccountSequence
}

// NewRPCClient initializes a new tendermint RPC client connected to the specified address.
func NewRPCClient(addr string, timeout time.Duration) (*rpchttp.HTTP, error) {
httpClient, err := libclient.DefaultHTTPClient(addr)
Expand Down
7 changes: 4 additions & 3 deletions cmd/noble/listener.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
package noble

import (
"cosmossdk.io/log"
"encoding/json"
"fmt"
"github.com/strangelove-ventures/noble-cctp-relayer/config"
"github.com/strangelove-ventures/noble-cctp-relayer/types"
"io"
"net/http"
"strconv"
"sync"
"time"

"cosmossdk.io/log"
"github.com/strangelove-ventures/noble-cctp-relayer/config"
"github.com/strangelove-ventures/noble-cctp-relayer/types"
)

func StartListener(cfg config.Config, logger log.Logger, processingQueue chan *types.MessageState) {
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ require (
)

require (
cosmossdk.io/math v1.1.2
github.com/circlefin/noble-cctp v0.0.0-20230911222715-829029fbba29
github.com/cometbft/cometbft v0.38.0
github.com/gin-gonic/gin v1.8.1
Expand All @@ -30,7 +31,6 @@ require (
cosmossdk.io/api v0.3.1 // indirect
cosmossdk.io/core v0.5.1 // indirect
cosmossdk.io/depinject v1.0.0-alpha.4 // indirect
cosmossdk.io/math v1.1.2 // indirect
filippo.io/edwards25519 v1.0.0 // indirect
github.com/4meepo/tagalign v1.3.2 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
Expand Down
8 changes: 4 additions & 4 deletions types/message_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import (
"encoding/json"
"errors"
"fmt"
"strconv"
"time"

"github.com/circlefin/noble-cctp/x/cctp/types"
"github.com/cosmos/cosmos-sdk/types/bech32"
"github.com/ethereum/go-ethereum/accounts/abi"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"strconv"
"time"
)

const (
Expand Down Expand Up @@ -45,7 +46,6 @@ type MessageState struct {

// EvmLogToMessageState transforms an evm log into a messageState given an ABI
func EvmLogToMessageState(abi abi.ABI, messageSent abi.Event, log *ethtypes.Log) (messageState *MessageState, err error) {

event := make(map[string]interface{})
_ = abi.UnpackIntoMap(event, messageSent.Name, log.Data)

Expand Down Expand Up @@ -80,7 +80,7 @@ func EvmLogToMessageState(abi abi.ABI, messageSent abi.Event, log *ethtypes.Log)
return messageState, nil
}

return nil, errors.New(fmt.Sprintf("unable to parse txn into message. tx hash %s", log.TxHash.Hex()))
return nil, errors.New(fmt.Sprintf("unable to parse tx into message, tx hash %s", log.TxHash.Hex()))
}

// NobleLogToMessageState transforms a Noble log into a messageState
Expand Down

0 comments on commit 8c2d916

Please sign in to comment.