0g-chain/x/evmutil/keeper/sequence_manager.go

117 lines
3.0 KiB
Go

package keeper
import (
"fmt"
"strings"
"time"
"github.com/0glabs/0g-chain/x/evmutil/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// sequenceManager implements the SequenceManager interface
type sequenceManager struct {
keeper Keeper
}
// NewSequenceManager creates a new sequence manager
func NewSequenceManager(k Keeper) types.SequenceManager {
return &sequenceManager{
keeper: k,
}
}
// GetNextSequence implements SequenceManager
func (sm *sequenceManager) GetNextSequence(ctx sdk.Context, addr sdk.AccAddress) (uint64, error) {
acc := sm.keeper.accountKeeper.GetAccount(ctx, addr)
if acc == nil {
return 0, fmt.Errorf("account not found: %s", addr)
}
return acc.GetSequence(), nil
}
// ValidateSequence implements SequenceManager
func (sm *sequenceManager) ValidateSequence(ctx sdk.Context, addr sdk.AccAddress, sequence uint64) error {
expected, err := sm.GetNextSequence(ctx, addr)
if err != nil {
return fmt.Errorf("failed to get account sequence: %w", err)
}
if sequence != expected {
return fmt.Errorf("%w: got %d, expected %d", types.ErrSequenceMismatch, sequence, expected)
}
return nil
}
// IncrementSequence implements SequenceManager
func (sm *sequenceManager) IncrementSequence(ctx sdk.Context, addr sdk.AccAddress) error {
acc := sm.keeper.accountKeeper.GetAccount(ctx, addr)
if acc == nil {
return fmt.Errorf("account not found: %s", addr)
}
if err := acc.SetSequence(acc.GetSequence() + 1); err != nil {
return fmt.Errorf("failed to increment sequence: %w", err)
}
sm.keeper.accountKeeper.SetAccount(ctx, acc)
return nil
}
// ExecuteWithRetry executes a message with retry mechanism for sequence mismatch
func (k Keeper) ExecuteWithRetry(
ctx sdk.Context,
msg sdk.Msg,
config types.RetryConfig,
) error {
attempt := 0
backoff := config.BackoffInterval
for attempt < config.MaxAttempts {
err := k.Execute(ctx, msg)
if err == nil {
return nil
}
if !isSequenceMismatchError(err) {
return err
}
// Wait before retry
time.Sleep(backoff)
// Exponential backoff
backoff = time.Duration(float64(backoff) * 1.5)
if backoff > config.MaxBackoff {
backoff = config.MaxBackoff
}
attempt++
}
return fmt.Errorf("%w: sequence mismatch persists", types.ErrMaxRetriesReached)
}
// isSequenceMismatchError checks if an error is a sequence mismatch error
func isSequenceMismatchError(err error) bool {
if err == nil {
return false
}
return strings.Contains(err.Error(), "account sequence mismatch")
}
// Execute executes a message
func (k Keeper) Execute(ctx sdk.Context, msg sdk.Msg) error {
// Implementation depends on message type
switch m := msg.(type) {
case *types.MsgConvertCoinToERC20:
_, err := k.ConvertCoinToERC20(ctx, sdk.AccAddress(m.Initiator), m.Receiver, *m.Amount)
return err
case *types.MsgConvertERC20ToCoin:
_, err := k.ConvertERC20ToCoin(ctx, m.Initiator, sdk.AccAddress(m.Receiver), m.ZgChainERC20Address, m.Amount)
return err
default:
return fmt.Errorf("unsupported message type: %T", msg)
}
}