Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: sync evm nonce with cosmos #54

Merged
merged 3 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@

"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 @@

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
}
beer-1 marked this conversation as resolved.
Show resolved Hide resolved
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 @@
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 @@
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
beer-1 marked this conversation as resolved.
Show resolved Hide resolved

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 @@
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
beer-1 marked this conversation as resolved.
Show resolved Hide resolved
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 @@
}, 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
}
Comment on lines +145 to +156
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add tests for the increaseNonce function.

The function correctly increments the nonce of the specified account. However, the function is not covered by tests.

Do you want me to generate the unit testing code or open a GitHub issue to track this task?

Tools
GitHub Check: codecov/patch

[warning] 149-150: x/evm/keeper/msg_server.go#L149-L150
Added lines #L149 - L150 were not covered by tests


[warning] 152-153: x/evm/keeper/msg_server.go#L152-L153
Added lines #L152 - L153 were not covered by tests


// 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

Comment on lines +165 to +171
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add tests for the modified Call function.

The function correctly invokes increaseNonce before executing the main logic. However, the added lines are not covered by tests.

Do you want me to generate the unit testing code or open a GitHub issue to track this task?

Tools
GitHub Check: codecov/patch

[warning] 169-170: 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 @@
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
Loading