Skip to content

Commit

Permalink
fix: allow retries for messages signed by relayer. (#3402)
Browse files Browse the repository at this point in the history
* fix: allow retries for messages signed by relayer.

---------

Co-authored-by: Cian Hatton <[email protected]>
  • Loading branch information
DimitrisJim and chatton authored Apr 12, 2023
1 parent 3a7106c commit f726023
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 1 deletion.
19 changes: 19 additions & 0 deletions e2e/relayer/relayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,22 @@ func newCosmosRelayer(t *testing.T, tag string, logger *zap.Logger, dockerClient
func newHermesRelayer() ibc.Relayer {
panic("hermes relayer not yet implemented for interchaintest")
}

// RelayerMap is a mapping from test names to a relayer set for that test.
type RelayerMap map[string]map[ibc.Wallet]bool

// AddRelayer adds the given relayer to the relayer set for the given test name.
func (r RelayerMap) AddRelayer(testName string, relayer ibc.Wallet) {
if _, ok := r[testName]; !ok {
r[testName] = make(map[ibc.Wallet]bool)
}
r[testName][relayer] = true
}

// containsRelayer returns true if the given relayer is in the relayer set for the given test name.
func (r RelayerMap) ContainsRelayer(testName string, wallet ibc.Wallet) bool {
if relayerSet, ok := r[testName]; ok {
return relayerSet[wallet]
}
return false
}
50 changes: 49 additions & 1 deletion e2e/testsuite/testsuite.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ type E2ETestSuite struct {

grpcClients map[string]GRPCClients
paths map[string]path
relayers relayer.RelayerMap
logger *zap.Logger
DockerClient *dockerclient.Client
network string
Expand Down Expand Up @@ -117,6 +118,12 @@ func (s *E2ETestSuite) GetRelayerUsers(ctx context.Context, chainOpts ...testcon
chainARelayerUser := cosmos.NewWallet(ChainARelayerName, chainAAccountBytes, "", chainA.Config())
chainBRelayerUser := cosmos.NewWallet(ChainBRelayerName, chainBAccountBytes, "", chainB.Config())

if s.relayers == nil {
s.relayers = make(relayer.RelayerMap)
}
s.relayers.AddRelayer(s.T().Name(), chainARelayerUser)
s.relayers.AddRelayer(s.T().Name(), chainBRelayerUser)

return chainARelayerUser, chainBRelayerUser
}

Expand Down Expand Up @@ -281,7 +288,18 @@ func (s *E2ETestSuite) BroadcastMessages(ctx context.Context, chain *cosmos.Cosm
return factory.WithGas(DefaultGasValue)
})

resp, err := cosmos.BroadcastTx(ctx, broadcaster, user, msgs...)
// Retry the operation a few times if the user signing the transaction is a relayer. (See issue #3264)
var resp sdk.TxResponse
var err error
broadcastFunc := func() (sdk.TxResponse, error) {
return cosmos.BroadcastTx(ctx, broadcaster, user, msgs...)
}
if s.relayers.ContainsRelayer(s.T().Name(), user) {
// Retry five times, the value of 5 chosen is arbitrary.
resp, err = s.retryNtimes(broadcastFunc, 5)
} else {
resp, err = broadcastFunc()
}
if err != nil {
return sdk.TxResponse{}, err
}
Expand Down Expand Up @@ -611,6 +629,36 @@ func (s *E2ETestSuite) QueryGranterGrants(ctx context.Context, chain *cosmos.Cos
return grants.Grants, nil
}

// retryNtimes retries the provided function up to the provided number of attempts.
func (s *E2ETestSuite) retryNtimes(f func() (sdk.TxResponse, error), attempts int) (sdk.TxResponse, error) {
// Ignore account sequence mismatch errors.
retryMessages := []string{"account sequence mismatch"}
var resp sdk.TxResponse
var err error
// If the response's raw log doesn't contain any of the allowed prefixes we return, else, we retry.
for i := 0; i < attempts; i++ {
resp, err = f()
if err != nil {
return sdk.TxResponse{}, err
}
if !containsMessage(resp.RawLog, retryMessages) {
return resp, err
}
s.T().Logf("retrying tx due to non deterministic failure: %+v", resp)
}
return resp, err
}

// containsMessages returns true if the string s contains any of the messages in the slice.
func containsMessage(s string, messages []string) bool {
for _, message := range messages {
if strings.Contains(s, message) {
return true
}
}
return false
}

// GetIBCToken returns the denomination of the full token denom sent to the receiving channel
func GetIBCToken(fullTokenDenom string, portID, channelID string) transfertypes.DenomTrace {
return transfertypes.ParseDenomTrace(fmt.Sprintf("%s/%s/%s", portID, channelID, fullTokenDenom))
Expand Down

0 comments on commit f726023

Please sign in to comment.