diff --git a/app/ante/ante.go b/app/ante/ante.go index df67a9c..077e406 100644 --- a/app/ante/ante.go +++ b/app/ante/ante.go @@ -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), } diff --git a/app/ante/sigverify.go b/app/ante/sigverify.go index 0b3b9c6..e8405b5 100644 --- a/app/ante/sigverify.go +++ b/app/ante/sigverify.go @@ -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, @@ -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") + } + + // increment sequence of all signers + signers, err := sigTx.GetSigners() + if err != nil { + return sdk.Context{}, err + } + + for _, signer := range signers { + acc := isd.ak.GetAccount(ctx, signer) + if err := acc.SetSequence(acc.GetSequence() + 1); err != nil { + panic(err) + } + + isd.ak.SetAccount(ctx, acc) + } + + // 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 + } + + if _, ok := signerMap[caller]; ok { + continue + } + signerMap[caller] = true + + callerAccAddr, err := isd.ak.AddressCodec().StringToBytes(caller) + if err != nil { + return ctx, err + } + + acc := isd.ak.GetAccount(ctx, callerAccAddr) + if err := acc.SetSequence(acc.GetSequence() - 1); err != nil { + panic(err) + } + + isd.ak.SetAccount(ctx, acc) + } + } + + return next(ctx, tx, simulate) +} diff --git a/app/ante/sigverify_test.go b/app/ante/sigverify_test.go new file mode 100644 index 0000000..b2e5cdb --- /dev/null +++ b/app/ante/sigverify_test.go @@ -0,0 +1,97 @@ +package ante_test + +import ( + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/initia-labs/minievm/app/ante" + evmtypes "github.com/initia-labs/minievm/x/evm/types" +) + +func (suite *AnteTestSuite) TestIncrementSequenceDecorator() { + suite.SetupTest() // setup + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + + priv, _, addr := testdata.KeyTestPubAddr() + acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr) + suite.NoError(acc.SetAccountNumber(uint64(50))) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc) + + msgs := []sdk.Msg{testdata.NewTestMsg(addr)} + suite.NoError(suite.txBuilder.SetMsgs(msgs...)) + privs := []cryptotypes.PrivKey{priv} + accNums := []uint64{suite.app.AccountKeeper.GetAccount(suite.ctx, addr).GetAccountNumber()} + accSeqs := []uint64{suite.app.AccountKeeper.GetAccount(suite.ctx, addr).GetSequence()} + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) + + tx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) + suite.NoError(err) + + isd := ante.NewIncrementSequenceDecorator(suite.app.AccountKeeper) + antehandler := sdk.ChainAnteDecorators(isd) + + testCases := []struct { + ctx sdk.Context + simulate bool + expectedSeq uint64 + }{ + {suite.ctx.WithIsReCheckTx(true), false, 1}, + {suite.ctx.WithIsCheckTx(true).WithIsReCheckTx(false), false, 2}, + {suite.ctx.WithIsReCheckTx(true), false, 3}, + {suite.ctx.WithIsReCheckTx(true), false, 4}, + {suite.ctx.WithIsReCheckTx(true), true, 5}, + } + + for i, tc := range testCases { + _, err := antehandler(tc.ctx, tx, tc.simulate) + suite.NoError(err, "unexpected error; tc #%d, %v", i, tc) + suite.Equal(tc.expectedSeq, suite.app.AccountKeeper.GetAccount(suite.ctx, addr).GetSequence()) + } +} + +func (suite *AnteTestSuite) TestIncrementSequenceDecorator_EVMMessages() { + suite.SetupTest() // setup + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + + priv, _, addr := testdata.KeyTestPubAddr() + acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr) + suite.NoError(acc.SetAccountNumber(uint64(50))) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc) + + msgs := []sdk.Msg{&evmtypes.MsgCreate{Sender: addr.String()}, &evmtypes.MsgCreate2{Sender: addr.String()}, &evmtypes.MsgCall{Sender: addr.String()}} + suite.NoError(suite.txBuilder.SetMsgs(msgs...)) + privs := []cryptotypes.PrivKey{priv} + accNums := []uint64{suite.app.AccountKeeper.GetAccount(suite.ctx, addr).GetAccountNumber()} + accSeqs := []uint64{suite.app.AccountKeeper.GetAccount(suite.ctx, addr).GetSequence()} + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) + + tx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) + suite.NoError(err) + + isd := ante.NewIncrementSequenceDecorator(suite.app.AccountKeeper) + antehandler := sdk.ChainAnteDecorators(isd) + + testCases := []struct { + ctx sdk.Context + simulate bool + expectedSeq uint64 + }{ + {suite.ctx.WithExecMode(sdk.ExecModeCheck), false, 1}, + {suite.ctx.WithExecMode(sdk.ExecModeCheck), true, 1}, + {suite.ctx.WithExecMode(sdk.ExecModeFinalize), false, 1}, + {suite.ctx.WithExecMode(sdk.ExecModeCheck), false, 2}, + } + + for i, tc := range testCases { + _, err := antehandler(tc.ctx, tx, tc.simulate) + suite.NoError(err, "unexpected error; tc #%d, %v", i, tc) + suite.Equal(tc.expectedSeq, suite.app.AccountKeeper.GetAccount(suite.ctx, addr).GetSequence()) + } +} diff --git a/jsonrpc/backend/tx.go b/jsonrpc/backend/tx.go index 6285c67..9e4b4b2 100644 --- a/jsonrpc/backend/tx.go +++ b/jsonrpc/backend/tx.go @@ -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" @@ -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) + } + b.logger.Debug("enqueue tx", "sender", senderHex, "txSeq", txSeq, "accSeq", accSeq) cacheKey := fmt.Sprintf("%s-%d", senderHex, txSeq) _ = b.queuedTxs.Add(cacheKey, txBytes) @@ -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 + } + var err error - queryCtx, err = b.app.CreateQueryContext(0, false) + queryCtx, err = b.app.CreateQueryContext(blockNumber.Int64(), false) if err != nil { return nil, err } diff --git a/proto/minievm/evm/v1/auth.proto b/proto/minievm/evm/v1/auth.proto index caf2c94..0d295f2 100644 --- a/proto/minievm/evm/v1/auth.proto +++ b/proto/minievm/evm/v1/auth.proto @@ -2,8 +2,8 @@ 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"; @@ -13,6 +13,7 @@ message 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 @@ -26,4 +27,4 @@ message ShorthandAccount { cosmos.auth.v1beta1.BaseAccount base_account = 1 [(gogoproto.embed) = true]; string original_address = 2; -} \ No newline at end of file +} diff --git a/x/evm/keeper/context.go b/x/evm/keeper/context.go index 75a3639..4f31d8d 100644 --- a/x/evm/keeper/context.go +++ b/x/evm/keeper/context.go @@ -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 @@ -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), diff --git a/x/evm/keeper/msg_server.go b/x/evm/keeper/msg_server.go index ebb6c28..e1f5907 100644 --- a/x/evm/keeper/msg_server.go +++ b/x/evm/keeper/msg_server.go @@ -142,6 +142,19 @@ 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) + } + if err := senderAcc.SetSequence(senderAcc.GetSequence() + 1); err != nil { + return err + } + 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) @@ -149,6 +162,13 @@ func (ms *msgServerImpl) Call(ctx context.Context, msg *types.MsgCall) (*types.M 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 + } + contractAddr, err := types.ContractAddressFromString(ms.ac, msg.ContractAddr) if err != nil { return nil, err @@ -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()) } diff --git a/x/evm/keeper/query_server.go b/x/evm/keeper/query_server.go index dc667d4..f8ea6e3 100644 --- a/x/evm/keeper/query_server.go +++ b/x/evm/keeper/query_server.go @@ -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) } diff --git a/x/evm/state/keys.go b/x/evm/state/keys.go index 20d0759..1919804 100644 --- a/x/evm/state/keys.go +++ b/x/evm/state/keys.go @@ -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...)...) } diff --git a/x/evm/state/state_account.go b/x/evm/state/state_account.go deleted file mode 100644 index b29a44d..0000000 --- a/x/evm/state/state_account.go +++ /dev/null @@ -1,35 +0,0 @@ -package state - -import ( - "encoding/binary" -) - -type StateAccount struct { - Nonce uint64 - CodeHash []byte -} - -func EmptyStateAccount() *StateAccount { - return &StateAccount{ - Nonce: 0, - CodeHash: []byte{}, - } -} - -func (sa *StateAccount) IsEmpty() bool { - return sa.Nonce == 0 && len(sa.CodeHash) == 0 -} - -func (sa StateAccount) Marshal() []byte { - bz := make([]byte, 8+len(sa.CodeHash)) - - binary.BigEndian.PutUint64(bz, sa.Nonce) - copy(bz[8:], sa.CodeHash) - return bz -} - -func (sa *StateAccount) Unmarshal(bz []byte) *StateAccount { - sa.Nonce = bytesToUint64(bz) - sa.CodeHash = bz[8:] - return sa -} diff --git a/x/evm/state/statedb.go b/x/evm/state/statedb.go index b00fc99..0020bcc 100644 --- a/x/evm/state/statedb.go +++ b/x/evm/state/statedb.go @@ -1,7 +1,6 @@ package state import ( - "encoding/binary" "errors" "fmt" "math/big" @@ -262,9 +261,8 @@ func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addre // CreateAccount set the nonce of the account with addr to 0 func (s *StateDB) CreateAccount(addr common.Address) { - if err := s.vmStore.Set(s.ctx, accountKey(addr), EmptyStateAccount().Marshal()); err != nil { - panic(err) - } + acc := s.accountKeeper.NewAccountWithAddress(s.ctx, addr.Bytes()) + s.accountKeeper.SetAccount(s.ctx, acc) } // CreateContract creates a contract account with the given address @@ -276,22 +274,22 @@ func (s *StateDB) CreateContract(contractAddr common.Address) { // If the account is empty, converts a normal account to a contract account // Else, creates a contract account if the account does not exist. if s.accountKeeper.HasAccount(s.ctx, sdk.AccAddress(contractAddr.Bytes())) { - account := s.accountKeeper.GetAccount(s.ctx, sdk.AccAddress(contractAddr.Bytes())) + acc := s.accountKeeper.GetAccount(s.ctx, sdk.AccAddress(contractAddr.Bytes())) // check the account is empty or not - if !evmtypes.IsEmptyAccount(account) { + if !evmtypes.IsEmptyAccount(acc) { panic(evmtypes.ErrAddressAlreadyExists.Wrap(contractAddr.String())) } // convert base account to contract account only if this account is empty - contractAccount := evmtypes.NewContractAccountWithAddress(contractAddr.Bytes()) - contractAccount.AccountNumber = account.GetAccountNumber() - s.accountKeeper.SetAccount(s.ctx, contractAccount) + contractAcc := evmtypes.NewContractAccountWithAddress(contractAddr.Bytes()) + contractAcc.AccountNumber = acc.GetAccountNumber() + s.accountKeeper.SetAccount(s.ctx, contractAcc) } else { // create contract account - contractAccount := evmtypes.NewContractAccountWithAddress(contractAddr.Bytes()) - contractAccount.AccountNumber = s.accountKeeper.NextAccountNumber(s.ctx) - s.accountKeeper.SetAccount(s.ctx, contractAccount) + contractAcc := evmtypes.NewContractAccountWithAddress(contractAddr.Bytes()) + contractAcc.AccountNumber = s.accountKeeper.NextAccountNumber(s.ctx) + s.accountKeeper.SetAccount(s.ctx, contractAcc) } // emit cosmos contract created event @@ -301,6 +299,19 @@ func (s *StateDB) CreateContract(contractAddr common.Address) { )) } +func (s *StateDB) getAccount(addr common.Address) sdk.AccountI { + return s.accountKeeper.GetAccount(s.ctx, sdk.AccAddress(addr.Bytes())) +} + +func (s *StateDB) getOrNewAccount(addr common.Address) sdk.AccountI { + acc := s.accountKeeper.GetAccount(s.ctx, sdk.AccAddress(addr.Bytes())) + if acc == nil { + acc = s.accountKeeper.NewAccountWithAddress(s.ctx, sdk.AccAddress(addr.Bytes())) + } + + return acc +} + // Empty returns empty according to the EIP161 specification (balance = nonce = code = 0) func (s *StateDB) Empty(addr common.Address) bool { // check if the account has non-zero balance @@ -308,47 +319,30 @@ func (s *StateDB) Empty(addr common.Address) bool { return false } - accBz, err := s.vmStore.Get(s.ctx, accountKey(addr)) - if err != nil && errors.Is(err, collections.ErrNotFound) { - return true - } else if err != nil { - panic(err) - } - - // check if the account has non-zero nonce or code hash - return EmptyStateAccount().Unmarshal(accBz).IsEmpty() + acc := s.getAccount(addr) + return acc == nil || evmtypes.IsEmptyAccount(acc) } // Exist reports whether the given account address exists in the state. // Notably this also returns true for self-destructed accounts. func (s *StateDB) Exist(addr common.Address) bool { - ok, err := s.vmStore.Has(s.ctx, accountKey(addr)) - if err != nil { - panic(err) - } - - return ok + acc := s.getAccount(addr) + return acc != nil } -func (s *StateDB) getStateAccount(addr common.Address) *StateAccount { - acc, err := s.vmStore.Get(s.ctx, accountKey(addr)) - if err != nil && errors.Is(err, collections.ErrNotFound) { +// GetCode returns the code of the account with addr +func (s *StateDB) GetCode(addr common.Address) []byte { + acc := s.getAccount(addr) + if acc == nil { return nil - } else if err != nil { - panic(err) } - return EmptyStateAccount().Unmarshal(acc) -} - -// GetCode returns the code of the account with addr -func (s *StateDB) GetCode(addr common.Address) []byte { - sa := s.getStateAccount(addr) - if sa == nil { + cacc, ok := acc.(*evmtypes.ContractAccount) + if !ok { return nil } - code, err := s.vmStore.Get(s.ctx, codeKey(addr, sa.CodeHash)) + code, err := s.vmStore.Get(s.ctx, codeKey(addr, cacc.CodeHash)) if err != nil && errors.Is(err, collections.ErrNotFound) { return nil } else if err != nil { @@ -358,55 +352,68 @@ func (s *StateDB) GetCode(addr common.Address) []byte { return code } -// SetCode store the code of the account with addr +// SetCode store the code of the account with addr, and set the code hash to the account +// It is always used in conjunction with CreateContract, so don't need to check account conversion. func (s *StateDB) SetCode(addr common.Address, code []byte) { - sa := s.getStateAccount(addr) - if sa == nil { - sa = EmptyStateAccount() + ca := s.getOrNewAccount(addr) + if evmtypes.IsEmptyAccount(ca) { + an := ca.GetAccountNumber() + ca = evmtypes.NewContractAccountWithAddress(addr.Bytes()) + if err := ca.SetAccountNumber(an); err != nil { + panic(err) + } } - // set the code hash in the state account - sa.CodeHash = crypto.Keccak256Hash(code).Bytes() - if err := s.vmStore.Set(s.ctx, accountKey(addr), sa.Marshal()); err != nil { - panic(err) - } + codeHash := crypto.Keccak256Hash(code).Bytes() + ca.(*evmtypes.ContractAccount).CodeHash = codeHash + s.accountKeeper.SetAccount(s.ctx, ca) // set the code in the store - if err := s.vmStore.Set(s.ctx, codeKey(addr, sa.CodeHash), code); err != nil { + if err := s.vmStore.Set(s.ctx, codeKey(addr, codeHash), code); err != nil { panic(err) } // set the code size in the store - if err := s.vmStore.Set(s.ctx, codeSizeKey(addr, sa.CodeHash), uint64ToBytes(uint64(len(code)))); err != nil { + if err := s.vmStore.Set(s.ctx, codeSizeKey(addr, codeHash), uint64ToBytes(uint64(len(code)))); err != nil { panic(err) } } // GetCodeHash returns the code hash of the account with addr func (s *StateDB) GetCodeHash(addr common.Address) common.Hash { - sa := s.getStateAccount(addr) - if sa == nil { - return common.Hash{} + acc := s.getAccount(addr) + if acc == nil { + return types.EmptyCodeHash + } + + cacc, ok := acc.(*evmtypes.ContractAccount) + if !ok { + return types.EmptyCodeHash } - return common.BytesToHash(sa.CodeHash) + return common.BytesToHash(cacc.CodeHash) } // GetCodeSize returns the code size of the account with addr func (s *StateDB) GetCodeSize(addr common.Address) int { - sa := s.getStateAccount(addr) - if sa == nil { + acc := s.getAccount(addr) + if acc == nil { + return 0 + } + + cacc, ok := acc.(*evmtypes.ContractAccount) + if !ok { return 0 } - codeSize, err := s.vmStore.Get(s.ctx, codeSizeKey(addr, sa.CodeHash)) + codeSize, err := s.vmStore.Get(s.ctx, codeSizeKey(addr, cacc.CodeHash)) if err != nil && errors.Is(err, collections.ErrNotFound) { return 0 } else if err != nil { panic(err) } - return int(binary.BigEndian.Uint64(codeSize)) + return int(bytesToUint64(codeSize)) } // GetCommittedState returns the committed state of the account with addr @@ -422,25 +429,21 @@ func (s *StateDB) GetCommittedState(addr common.Address, state common.Hash) comm // GetNonce returns the nonce of the account with addr func (s *StateDB) GetNonce(addr common.Address) uint64 { - sa := s.getStateAccount(addr) - if sa != nil { - return sa.Nonce + acc := s.getAccount(addr) + if acc == nil { + return 0 } - return 0 + return acc.GetSequence() } // SetNonce sets the nonce of the account with addr func (s *StateDB) SetNonce(addr common.Address, nonce uint64) { - sa := s.getStateAccount(addr) - if sa == nil { - sa = EmptyStateAccount() - } - - sa.Nonce = nonce - if err := s.vmStore.Set(s.ctx, accountKey(addr), sa.Marshal()); err != nil { + acc := s.getOrNewAccount(addr) + if err := acc.SetSequence(nonce); err != nil { panic(err) } + s.accountKeeper.SetAccount(s.ctx, acc) } // GetRefund returns the refund @@ -455,8 +458,8 @@ func (s *StateDB) GetRefund() uint64 { // GetState returns the state of the account with addr and slot func (s *StateDB) GetState(addr common.Address, slot common.Hash) common.Hash { - sa := s.getStateAccount(addr) - if sa != nil { + acc := s.getAccount(addr) + if acc != nil { state, err := s.vmStore.Get(s.ctx, stateKey(addr, slot)) if err != nil && errors.Is(err, collections.ErrNotFound) { return common.Hash{} @@ -472,8 +475,8 @@ func (s *StateDB) GetState(addr common.Address, slot common.Hash) common.Hash { // HasSelfDestructed return true if the account with addr has self-destructed func (s *StateDB) HasSelfDestructed(addr common.Address) bool { - sa := s.getStateAccount(addr) - if sa != nil { + acc := s.getAccount(addr) + if acc != nil { ok, err := s.transientSelfDestruct.Has(s.ctx, collections.Join(s.execIndex, addr.Bytes())) if err != nil { panic(err) @@ -491,8 +494,8 @@ func (s *StateDB) HasSelfDestructed(addr common.Address) bool { // The account's state object is still available until the state is committed, // getStateObject will return a non-nil account after SelfDestruct. func (s *StateDB) SelfDestruct(addr common.Address) { - sa := s.getStateAccount(addr) - if sa == nil { + acc := s.getAccount(addr) + if acc == nil { return } @@ -507,8 +510,8 @@ func (s *StateDB) SelfDestruct(addr common.Address) { // Selfdestruct6780 calls selfdestruct and clears the account balance if the account is created in the same transaction. func (s *StateDB) Selfdestruct6780(addr common.Address) { - sa := s.getStateAccount(addr) - if sa == nil { + acc := s.getAccount(addr) + if acc == nil { return } @@ -522,14 +525,6 @@ func (s *StateDB) Selfdestruct6780(addr common.Address) { // SetState implements vm.StateDB. func (s *StateDB) SetState(addr common.Address, slot common.Hash, value common.Hash) { - sa := s.getStateAccount(addr) - if sa == nil { - sa = EmptyStateAccount() - if err := s.vmStore.Set(s.ctx, accountKey(addr), sa.Marshal()); err != nil { - panic(err) - } - } - if err := s.vmStore.Set(s.ctx, stateKey(addr, slot), value[:]); err != nil { panic(err) } diff --git a/x/evm/types/auth.go b/x/evm/types/auth.go index 3fab9a6..401b59c 100644 --- a/x/evm/types/auth.go +++ b/x/evm/types/auth.go @@ -23,7 +23,8 @@ var ( // NewContractAccountWithAddress create new contract account with the given address. func NewContractAccountWithAddress(addr sdk.AccAddress) *ContractAccount { return &ContractAccount{ - authtypes.NewBaseAccountWithAddress(addr), + BaseAccount: authtypes.NewBaseAccountWithAddress(addr), + CodeHash: []byte{}, } } @@ -67,5 +68,5 @@ func IsEmptyAccount(account sdk.AccountI) bool { _, isShorthandAccount := account.(ShorthandAccountI) _, isContractAccount := account.(*ContractAccount) - return !isModuleAccount && !isShorthandAccount && !isContractAccount && account.GetPubKey() == nil + return !isModuleAccount && !isShorthandAccount && !isContractAccount && account.GetPubKey() == nil && account.GetSequence() == 0 } diff --git a/x/evm/types/auth.pb.go b/x/evm/types/auth.pb.go index 4a1e2af..32abb96 100644 --- a/x/evm/types/auth.pb.go +++ b/x/evm/types/auth.pb.go @@ -28,6 +28,7 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // ContractAccount defines an account of contract. type ContractAccount struct { *types.BaseAccount `protobuf:"bytes,1,opt,name=base_account,json=baseAccount,proto3,embedded=base_account" json:"base_account,omitempty"` + CodeHash []byte `protobuf:"bytes,2,opt,name=code_hash,json=codeHash,proto3" json:"code_hash,omitempty"` } func (m *ContractAccount) Reset() { *m = ContractAccount{} } @@ -114,27 +115,28 @@ func init() { func init() { proto.RegisterFile("minievm/evm/v1/auth.proto", fileDescriptor_a01464d2d75c2977) } var fileDescriptor_a01464d2d75c2977 = []byte{ - // 308 bytes of a gzipped FileDescriptorProto + // 334 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xcc, 0xcd, 0xcc, 0xcb, 0x4c, 0x2d, 0xcb, 0xd5, 0x07, 0xe1, 0x32, 0x43, 0xfd, 0xc4, 0xd2, 0x92, 0x0c, 0xbd, 0x82, 0xa2, 0xfc, 0x92, 0x7c, 0x21, 0x3e, 0xa8, 0x94, 0x1e, 0x08, 0x97, 0x19, 0x4a, 0x09, 0x26, 0xe6, 0x66, - 0xe6, 0xe5, 0xeb, 0x83, 0x49, 0x88, 0x12, 0x29, 0x91, 0xf4, 0xfc, 0xf4, 0x7c, 0x30, 0x53, 0x1f, - 0xc4, 0x82, 0x8a, 0xca, 0x25, 0xe7, 0x17, 0xe7, 0xe6, 0x17, 0x83, 0xcd, 0xd2, 0x2f, 0x33, 0x4c, - 0x4a, 0x2d, 0x49, 0x44, 0x36, 0x58, 0xa9, 0x8a, 0x8b, 0xdf, 0x39, 0x3f, 0xaf, 0xa4, 0x28, 0x31, - 0xb9, 0xc4, 0x31, 0x39, 0x39, 0xbf, 0x34, 0xaf, 0x44, 0xc8, 0x93, 0x8b, 0x27, 0x29, 0xb1, 0x38, - 0x35, 0x3e, 0x11, 0xc2, 0x97, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x36, 0x52, 0xd0, 0x83, 0x98, 0xa4, - 0x07, 0xd6, 0x0c, 0x35, 0x49, 0xcf, 0x29, 0xb1, 0x38, 0x15, 0xaa, 0xcf, 0x89, 0xe5, 0xc2, 0x3d, - 0x79, 0xc6, 0x20, 0xee, 0x24, 0x84, 0x90, 0x95, 0x4c, 0xc7, 0x02, 0x79, 0x86, 0xae, 0xe7, 0x1b, - 0xb4, 0x84, 0x41, 0x5e, 0x42, 0xb3, 0x48, 0x69, 0x39, 0x23, 0x97, 0x40, 0x70, 0x46, 0x7e, 0x51, - 0x49, 0x46, 0x62, 0x5e, 0x0a, 0xf5, 0x6d, 0x17, 0xd2, 0xe4, 0x12, 0xc8, 0x2f, 0xca, 0x4c, 0xcf, - 0xcc, 0x4b, 0xcc, 0x89, 0x4f, 0x4c, 0x49, 0x29, 0x4a, 0x2d, 0x2e, 0x96, 0x60, 0x52, 0x60, 0xd4, - 0xe0, 0x0c, 0xe2, 0x87, 0x89, 0x3b, 0x42, 0x84, 0xad, 0x64, 0x61, 0x0e, 0x15, 0x01, 0x39, 0x14, - 0xdd, 0x51, 0x4e, 0x2e, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, - 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0x10, 0xa5, 0x95, - 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x9f, 0x99, 0x97, 0x59, 0x92, 0x99, - 0xa8, 0x9b, 0x93, 0x98, 0x54, 0xac, 0x0f, 0x8b, 0xca, 0x0a, 0x70, 0x64, 0x96, 0x54, 0x16, 0xa4, - 0x16, 0x27, 0xb1, 0x81, 0x83, 0xdc, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x32, 0x2a, 0x1a, 0x97, - 0xe8, 0x01, 0x00, 0x00, + 0xe6, 0xe5, 0xeb, 0x83, 0x49, 0x88, 0x12, 0x29, 0xb9, 0xe4, 0xfc, 0xe2, 0xdc, 0xfc, 0x62, 0xb0, + 0x2e, 0xfd, 0x32, 0xc3, 0xa4, 0xd4, 0x92, 0x44, 0x64, 0x23, 0xa4, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, + 0xc1, 0x4c, 0x7d, 0x10, 0x0b, 0x22, 0xaa, 0x34, 0x9d, 0x91, 0x8b, 0xdf, 0x39, 0x3f, 0xaf, 0xa4, + 0x28, 0x31, 0xb9, 0xc4, 0x31, 0x39, 0x39, 0xbf, 0x34, 0xaf, 0x44, 0xc8, 0x93, 0x8b, 0x27, 0x29, + 0xb1, 0x38, 0x35, 0x3e, 0x11, 0xc2, 0x97, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x36, 0x52, 0xd0, 0x83, + 0x58, 0xa0, 0x07, 0x36, 0x13, 0x6a, 0x81, 0x9e, 0x53, 0x62, 0x71, 0x2a, 0x54, 0x9f, 0x13, 0xcb, + 0x85, 0x7b, 0xf2, 0x8c, 0x41, 0xdc, 0x49, 0x08, 0x21, 0x21, 0x69, 0x2e, 0xce, 0xe4, 0xfc, 0x94, + 0xd4, 0xf8, 0x8c, 0xc4, 0xe2, 0x0c, 0x09, 0x26, 0x05, 0x46, 0x0d, 0x9e, 0x20, 0x0e, 0x90, 0x80, + 0x47, 0x62, 0x71, 0x86, 0x95, 0x4c, 0xc7, 0x02, 0x79, 0x86, 0xae, 0xe7, 0x1b, 0xb4, 0x84, 0x41, + 0x1e, 0x46, 0x73, 0x85, 0xd2, 0x72, 0x46, 0x2e, 0x81, 0xe0, 0x8c, 0xfc, 0xa2, 0x92, 0x8c, 0xc4, + 0xbc, 0x14, 0x1a, 0x38, 0x4d, 0x93, 0x4b, 0x20, 0xbf, 0x28, 0x33, 0x3d, 0x33, 0x2f, 0x31, 0x27, + 0x3e, 0x31, 0x25, 0xa5, 0x28, 0xb5, 0xb8, 0x18, 0xec, 0x42, 0xce, 0x20, 0x7e, 0x98, 0xb8, 0x23, + 0x44, 0xd8, 0x4a, 0x16, 0xe6, 0x50, 0x11, 0x90, 0x43, 0xd1, 0x1d, 0xe5, 0xe4, 0x72, 0xe2, 0x91, + 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, + 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0x5a, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, + 0xc9, 0xf9, 0xb9, 0xfa, 0x99, 0x79, 0x99, 0x25, 0x99, 0x89, 0xba, 0x39, 0x89, 0x49, 0xc5, 0xfa, + 0xb0, 0x88, 0xae, 0x00, 0x47, 0x75, 0x49, 0x65, 0x41, 0x6a, 0x71, 0x12, 0x1b, 0x38, 0x42, 0x8c, + 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x78, 0xb6, 0x5f, 0xa3, 0x06, 0x02, 0x00, 0x00, } func (m *ContractAccount) Marshal() (dAtA []byte, err error) { @@ -157,6 +159,13 @@ func (m *ContractAccount) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.CodeHash) > 0 { + i -= len(m.CodeHash) + copy(dAtA[i:], m.CodeHash) + i = encodeVarintAuth(dAtA, i, uint64(len(m.CodeHash))) + i-- + dAtA[i] = 0x12 + } if m.BaseAccount != nil { { size, err := m.BaseAccount.MarshalToSizedBuffer(dAtA[:i]) @@ -235,6 +244,10 @@ func (m *ContractAccount) Size() (n int) { l = m.BaseAccount.Size() n += 1 + l + sovAuth(uint64(l)) } + l = len(m.CodeHash) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } return n } @@ -326,6 +339,40 @@ func (m *ContractAccount) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CodeHash", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CodeHash = append(m.CodeHash[:0], dAtA[iNdEx:postIndex]...) + if m.CodeHash == nil { + m.CodeHash = []byte{} + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipAuth(dAtA[iNdEx:])