Skip to content

Commit

Permalink
sync evm nonce with cosmos
Browse files Browse the repository at this point in the history
  • Loading branch information
beer-1 committed Aug 29, 2024
1 parent 8d5d08b commit a7794c7
Show file tree
Hide file tree
Showing 12 changed files with 274 additions and 166 deletions.
2 changes: 1 addition & 1 deletion app/ante/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
ante.NewValidateSigCountDecorator(options.AccountKeeper),
ante.NewSigGasConsumeDecorator(options.AccountKeeper, sigGasConsumer),
NewSigVerificationDecorator(options.AccountKeeper, options.EVMKeeper, options.SignModeHandler),
ante.NewIncrementSequenceDecorator(options.AccountKeeper),
NewIncrementSequenceDecorator(options.AccountKeeper),
ibcante.NewRedundantRelayDecorator(options.IBCkeeper),
auctionante.NewAuctionDecorator(options.AuctionKeeper, options.TxEncoder, options.MevLane),
}
Expand Down
83 changes: 83 additions & 0 deletions app/ante/sigverify.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"github.com/initia-labs/initia/crypto/ethsecp256k1"
evmkeeper "github.com/initia-labs/minievm/x/evm/keeper"
evmtypes "github.com/initia-labs/minievm/x/evm/types"
)

// SigVerificationDecorator verifies all signatures for a tx and return an error if any are invalid. Note,
Expand Down Expand Up @@ -198,3 +199,85 @@ func consumeMultisignatureVerificationGas(

return nil
}

// IncrementSequenceDecorator handles incrementing sequences of all signers.
// Use the IncrementSequenceDecorator decorator to prevent replay attacks. Note,
// there is need to execute IncrementSequenceDecorator on RecheckTx since
// BaseApp.Commit() will set the check state based on the latest header.
//
// NOTE: Since CheckTx and DeliverTx state are managed separately, subsequent and
// sequential txs orginating from the same account cannot be handled correctly in
// a reliable way unless sequence numbers are managed and tracked manually by a
// client. It is recommended to instead use multiple messages in a tx.
//
// NOTE: When we execute evm messages, it whould handle sequence number increment internally,
// so we need to decrease sequence number if it is used in EVM.
type IncrementSequenceDecorator struct {
ak authante.AccountKeeper
}

func NewIncrementSequenceDecorator(ak authante.AccountKeeper) IncrementSequenceDecorator {
return IncrementSequenceDecorator{
ak: ak,
}
}

func (isd IncrementSequenceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
sigTx, ok := tx.(authsigning.SigVerifiableTx)
if !ok {
return ctx, errorsmod.Wrap(sdkerrors.ErrTxDecode, "invalid transaction type")
}

Check warning on line 229 in app/ante/sigverify.go

View check run for this annotation

Codecov / codecov/patch

app/ante/sigverify.go#L225-L229

Added lines #L225 - L229 were not covered by tests

// increment sequence of all signers
signers, err := sigTx.GetSigners()
if err != nil {
return sdk.Context{}, err
}

Check warning on line 235 in app/ante/sigverify.go

View check run for this annotation

Codecov / codecov/patch

app/ante/sigverify.go#L232-L235

Added lines #L232 - L235 were not covered by tests

for _, signer := range signers {
acc := isd.ak.GetAccount(ctx, signer)
if err := acc.SetSequence(acc.GetSequence() + 1); err != nil {
panic(err)

Check warning on line 240 in app/ante/sigverify.go

View check run for this annotation

Codecov / codecov/patch

app/ante/sigverify.go#L237-L240

Added lines #L237 - L240 were not covered by tests
}

isd.ak.SetAccount(ctx, acc)

Check warning on line 243 in app/ante/sigverify.go

View check run for this annotation

Codecov / codecov/patch

app/ante/sigverify.go#L243

Added line #L243 was not covered by tests
}

// decrement sequence of all signers which is used in EVM
// when we execute evm messages, it whould handle sequence number increment.
if simulate || ctx.ExecMode() == sdk.ExecModeFinalize {
signerMap := make(map[string]bool)
for _, msg := range tx.GetMsgs() {
var caller string
switch msg := msg.(type) {
case *evmtypes.MsgCreate:
caller = msg.Sender
case *evmtypes.MsgCreate2:
caller = msg.Sender
case *evmtypes.MsgCall:
caller = msg.Sender
default:
continue

Check warning on line 260 in app/ante/sigverify.go

View check run for this annotation

Codecov / codecov/patch

app/ante/sigverify.go#L248-L260

Added lines #L248 - L260 were not covered by tests
}

if _, ok := signerMap[caller]; ok {
continue

Check warning on line 264 in app/ante/sigverify.go

View check run for this annotation

Codecov / codecov/patch

app/ante/sigverify.go#L263-L264

Added lines #L263 - L264 were not covered by tests
}
signerMap[caller] = true

callerAccAddr, err := isd.ak.AddressCodec().StringToBytes(caller)
if err != nil {
return ctx, err
}

Check warning on line 271 in app/ante/sigverify.go

View check run for this annotation

Codecov / codecov/patch

app/ante/sigverify.go#L266-L271

Added lines #L266 - L271 were not covered by tests

acc := isd.ak.GetAccount(ctx, callerAccAddr)
if err := acc.SetSequence(acc.GetSequence() - 1); err != nil {
panic(err)

Check warning on line 275 in app/ante/sigverify.go

View check run for this annotation

Codecov / codecov/patch

app/ante/sigverify.go#L273-L275

Added lines #L273 - L275 were not covered by tests
}

isd.ak.SetAccount(ctx, acc)

Check warning on line 278 in app/ante/sigverify.go

View check run for this annotation

Codecov / codecov/patch

app/ante/sigverify.go#L278

Added line #L278 was not covered by tests
}
}

return next(ctx, tx, simulate)

Check warning on line 282 in app/ante/sigverify.go

View check run for this annotation

Codecov / codecov/patch

app/ante/sigverify.go#L282

Added line #L282 was not covered by tests
}
11 changes: 10 additions & 1 deletion jsonrpc/backend/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
coretypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc"

Expand Down Expand Up @@ -78,6 +79,10 @@ func (b *JSONRPCBackend) SendTx(tx *coretypes.Transaction) error {
accSeq = acc.GetSequence()
}

if accSeq > txSeq {
return fmt.Errorf("%w: next nonce %v, tx nonce %v", core.ErrNonceTooLow, accSeq, txSeq)
}

Check warning on line 84 in jsonrpc/backend/tx.go

View check run for this annotation

Codecov / codecov/patch

jsonrpc/backend/tx.go#L82-L84

Added lines #L82 - L84 were not covered by tests

b.logger.Debug("enqueue tx", "sender", senderHex, "txSeq", txSeq, "accSeq", accSeq)
cacheKey := fmt.Sprintf("%s-%d", senderHex, txSeq)
_ = b.queuedTxs.Add(cacheKey, txBytes)
Expand Down Expand Up @@ -161,8 +166,12 @@ func (b *JSONRPCBackend) GetTransactionCount(address common.Address, blockNrOrHa
if blockNumber == rpc.PendingBlockNumber {
queryCtx = b.app.GetContextForCheckTx(nil)
} else {
if blockNumber < 0 {
blockNumber = 0
}

Check warning on line 171 in jsonrpc/backend/tx.go

View check run for this annotation

Codecov / codecov/patch

jsonrpc/backend/tx.go#L169-L171

Added lines #L169 - L171 were not covered by tests

var err error
queryCtx, err = b.app.CreateQueryContext(0, false)
queryCtx, err = b.app.CreateQueryContext(blockNumber.Int64(), false)

Check warning on line 174 in jsonrpc/backend/tx.go

View check run for this annotation

Codecov / codecov/patch

jsonrpc/backend/tx.go#L174

Added line #L174 was not covered by tests
if err != nil {
return nil, err
}
Expand Down
13 changes: 7 additions & 6 deletions proto/minievm/evm/v1/auth.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@ syntax = "proto3";
package minievm.evm.v1;

import "amino/amino.proto";
import "gogoproto/gogo.proto";
import "cosmos/auth/v1beta1/auth.proto";
import "gogoproto/gogo.proto";

option go_package = "github.com/initia-labs/minievm/x/evm/types";

// ContractAccount defines an account of contract.
message ContractAccount {
option (amino.name) = "evm/ContractAccount";
option (amino.name) = "evm/ContractAccount";
option (gogoproto.goproto_getters) = false;

cosmos.auth.v1beta1.BaseAccount base_account = 1 [(gogoproto.embed) = true];
bytes code_hash = 2;
}

// ShorthandAccount defines an account of shorthand address
Expand All @@ -21,9 +22,9 @@ message ContractAccount {
// Also it is used to check the existence of the account before
// creating a new account.
message ShorthandAccount {
option (amino.name) = "evm/ShorthandAccount";
option (amino.name) = "evm/ShorthandAccount";
option (gogoproto.goproto_getters) = false;

cosmos.auth.v1beta1.BaseAccount base_account = 1 [(gogoproto.embed) = true];
string original_address = 2;
}
cosmos.auth.v1beta1.BaseAccount base_account = 1 [(gogoproto.embed) = true];
string original_address = 2;
}
12 changes: 2 additions & 10 deletions x/evm/keeper/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,16 +226,11 @@ func (k Keeper) EVMStaticCallWithTracer(ctx context.Context, caller common.Addre

// EVMCall executes an EVM call with the given input data.
func (k Keeper) EVMCall(ctx context.Context, caller common.Address, contractAddr common.Address, inputBz []byte, value *uint256.Int) ([]byte, types.Logs, error) {
return k.EVMCallWithTracer(ctx, caller, contractAddr, inputBz, value, false, nil)
}

// EVMCallWithNonceIncrement executes an EVM call with the given input data and increment the nonce of the caller.
func (k Keeper) EVMCallWithNonceIncrement(ctx context.Context, caller common.Address, contractAddr common.Address, inputBz []byte, value *uint256.Int) ([]byte, types.Logs, error) {
return k.EVMCallWithTracer(ctx, caller, contractAddr, inputBz, value, true, nil)
return k.EVMCallWithTracer(ctx, caller, contractAddr, inputBz, value, nil)
}

// EVMCallWithTracer executes an EVM call with the given input data and tracer.
func (k Keeper) EVMCallWithTracer(ctx context.Context, caller common.Address, contractAddr common.Address, inputBz []byte, value *uint256.Int, increseNonce bool, tracer *tracing.Hooks) ([]byte, types.Logs, error) {
func (k Keeper) EVMCallWithTracer(ctx context.Context, caller common.Address, contractAddr common.Address, inputBz []byte, value *uint256.Int, tracer *tracing.Hooks) ([]byte, types.Logs, error) {
ctx, evm, err := k.CreateEVM(ctx, caller, tracer)
if err != nil {
return nil, nil, err
Expand All @@ -249,9 +244,6 @@ func (k Keeper) EVMCallWithTracer(ctx context.Context, caller common.Address, co

rules := evm.ChainConfig().Rules(evm.Context.BlockNumber, evm.Context.Random != nil, evm.Context.Time)
evm.StateDB.Prepare(rules, caller, types.NullAddress, &contractAddr, append(vm.ActivePrecompiles(rules), k.precompiles.toAddrs()...), nil)
if increseNonce {
evm.StateDB.SetNonce(caller, evm.StateDB.GetNonce(caller)+1)
}

retBz, gasRemaining, err := evm.Call(
vm.AccountRef(caller),
Expand Down
22 changes: 21 additions & 1 deletion x/evm/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,33 @@ func (ms *msgServerImpl) Create2(ctx context.Context, msg *types.MsgCreate2) (*t
}, nil
}

// increaseNonce increases the nonce of the given account.
func (ms *msgServerImpl) increaseNonce(ctx context.Context, caller sdk.AccAddress) error {
senderAcc := ms.accountKeeper.GetAccount(ctx, caller)
if senderAcc == nil {
senderAcc = ms.accountKeeper.NewAccountWithAddress(ctx, caller)
}

Check warning on line 150 in x/evm/keeper/msg_server.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/msg_server.go#L149-L150

Added lines #L149 - L150 were not covered by tests
if err := senderAcc.SetSequence(senderAcc.GetSequence() + 1); err != nil {
return err
}

Check warning on line 153 in x/evm/keeper/msg_server.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/msg_server.go#L152-L153

Added lines #L152 - L153 were not covered by tests
ms.accountKeeper.SetAccount(ctx, senderAcc)
return nil
}

// Call implements types.MsgServer.
func (ms *msgServerImpl) Call(ctx context.Context, msg *types.MsgCall) (*types.MsgCallResponse, error) {
sender, err := ms.ac.StringToBytes(msg.Sender)
if err != nil {
return nil, err
}

// increase nonce before execution like evm does
//
// NOTE: evm only increases nonce at Call not Create, so we should do the same.
if err := ms.increaseNonce(ctx, sender); err != nil {
return nil, err
}

Check warning on line 170 in x/evm/keeper/msg_server.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/msg_server.go#L169-L170

Added lines #L169 - L170 were not covered by tests

contractAddr, err := types.ContractAddressFromString(ms.ac, msg.ContractAddr)
if err != nil {
return nil, err
Expand All @@ -168,7 +188,7 @@ func (ms *msgServerImpl) Call(ctx context.Context, msg *types.MsgCall) (*types.M
return nil, types.ErrInvalidValue.Wrap("value is out of range")
}

retBz, logs, err := ms.EVMCallWithNonceIncrement(ctx, caller, contractAddr, inputBz, value)
retBz, logs, err := ms.EVMCall(ctx, caller, contractAddr, inputBz, value)
if err != nil {
return nil, types.ErrEVMCallFailed.Wrap(err.Error())
}
Expand Down
2 changes: 1 addition & 1 deletion x/evm/keeper/query_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func (qs *queryServerImpl) Call(ctx context.Context, req *types.QueryCallRequest
// if contract address is not provided, then it's a contract creation
retBz, _, logs, err = qs.EVMCreateWithTracer(sdkCtx, caller, inputBz, value, nil, tracer)
} else {
retBz, logs, err = qs.EVMCallWithTracer(sdkCtx, caller, contractAddr, inputBz, value, false, tracer)
retBz, logs, err = qs.EVMCallWithTracer(sdkCtx, caller, contractAddr, inputBz, value, tracer)

}

Expand Down
5 changes: 0 additions & 5 deletions x/evm/state/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,11 @@ import (
)

var (
AccountKeyPrefix = []byte("account")
CodeKeyPrefix = []byte("code")
CodeSizeKeyPrefix = []byte("codesize")
StateKeyPrefix = []byte("state")
)

func accountKey(addr common.Address) []byte {
return append(addr.Bytes(), AccountKeyPrefix...)
}

func codeKey(addr common.Address, codeHash []byte) []byte {
return append(addr.Bytes(), append(CodeKeyPrefix, codeHash...)...)
}
Expand Down
35 changes: 0 additions & 35 deletions x/evm/state/state_account.go

This file was deleted.

Loading

0 comments on commit a7794c7

Please sign in to comment.