keeper: StateDB unit tests (#47)

* keeper: statedb unit tests

* evm: balance tests

* evm: nonce and code tests

* evm: refund test

* evm: fix tx encoding

* storage and access list tests
This commit is contained in:
Federico Kunze 2021-06-02 04:06:12 -04:00 committed by GitHub
parent 8e7ebe80e9
commit c08dcfad0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 685 additions and 116 deletions

View File

@ -7,9 +7,11 @@ import (
"github.com/cosmos/cosmos-sdk/simapp/params" "github.com/cosmos/cosmos-sdk/simapp/params"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/tx" "github.com/cosmos/cosmos-sdk/x/auth/tx"
evmtypes "github.com/cosmos/ethermint/x/evm/types"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/cosmos/ethermint/codec" "github.com/cosmos/ethermint/codec"
evmtypes "github.com/cosmos/ethermint/x/evm/types"
) )
// MakeEncodingConfig creates an EncodingConfig for testing // MakeEncodingConfig creates an EncodingConfig for testing
@ -49,9 +51,9 @@ func NewTxConfig(marshaler amino.ProtoCodecMarshaler) client.TxConfig {
// TxEncoder overwrites sdk.TxEncoder to support MsgEthereumTx // TxEncoder overwrites sdk.TxEncoder to support MsgEthereumTx
func (g txConfig) TxEncoder() sdk.TxEncoder { func (g txConfig) TxEncoder() sdk.TxEncoder {
return func(tx sdk.Tx) ([]byte, error) { return func(tx sdk.Tx) ([]byte, error) {
ethtx, ok := tx.(*evmtypes.MsgEthereumTx) msg, ok := tx.(*evmtypes.MsgEthereumTx)
if ok { if ok {
return g.cdc.MarshalBinaryBare(ethtx) return msg.AsTransaction().MarshalBinary()
} }
return g.TxConfig.TxEncoder()(tx) return g.TxConfig.TxEncoder()(tx)
} }
@ -60,11 +62,13 @@ func (g txConfig) TxEncoder() sdk.TxEncoder {
// TxDecoder overwrites sdk.TxDecoder to support MsgEthereumTx // TxDecoder overwrites sdk.TxDecoder to support MsgEthereumTx
func (g txConfig) TxDecoder() sdk.TxDecoder { func (g txConfig) TxDecoder() sdk.TxDecoder {
return func(txBytes []byte) (sdk.Tx, error) { return func(txBytes []byte) (sdk.Tx, error) {
var ethtx evmtypes.MsgEthereumTx tx := &ethtypes.Transaction{}
err := g.cdc.UnmarshalBinaryBare(txBytes, &ethtx) err := tx.UnmarshalBinary(txBytes)
if err == nil { if err == nil {
return &ethtx, nil msg := &evmtypes.MsgEthereumTx{}
msg.FromEthereumTx(tx)
return msg, nil
} }
return g.TxConfig.TxDecoder()(txBytes) return g.TxConfig.TxDecoder()(txBytes)

View File

@ -3,6 +3,9 @@ package tests
import ( import (
"fmt" "fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/crypto/keyring"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
@ -10,6 +13,20 @@ import (
"github.com/cosmos/ethermint/crypto/ethsecp256k1" "github.com/cosmos/ethermint/crypto/ethsecp256k1"
) )
// NewAddrKey generates an Ethereum address and its corresponding private key.
func NewAddrKey() (common.Address, cryptotypes.PrivKey) {
privkey, _ := ethsecp256k1.GenerateKey()
addr := crypto.PubkeyToAddress(privkey.ToECDSA().PublicKey)
return addr, privkey
}
// GenerateAddress generates an Ethereum address.
func GenerateAddress() common.Address {
addr, _ := NewAddrKey()
return addr
}
var _ keyring.Signer = &Signer{} var _ keyring.Signer = &Signer{}
// Signer defines a type that is used on testing for signing MsgEthereumTx // Signer defines a type that is used on testing for signing MsgEthereumTx

View File

@ -1,7 +1,6 @@
package keeper_test package keeper_test
import ( import (
"math/big"
"testing" "testing"
"time" "time"
@ -16,7 +15,6 @@ import (
"github.com/cosmos/ethermint/x/evm/types" "github.com/cosmos/ethermint/x/evm/types"
ethcmn "github.com/ethereum/go-ethereum/common" ethcmn "github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
ethcrypto "github.com/ethereum/go-ethereum/crypto" ethcrypto "github.com/ethereum/go-ethereum/crypto"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
@ -43,7 +41,7 @@ func (suite *KeeperTestSuite) SetupTest() {
suite.app = app.Setup(checkTx) suite.app = app.Setup(checkTx)
suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1, ChainID: "ethermint-1", Time: time.Now().UTC()}) suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1, ChainID: "ethermint-1", Time: time.Now().UTC()})
suite.app.EvmKeeper.CommitStateDB.WithContext(suite.ctx) suite.app.EvmKeeper.WithContext(suite.ctx)
suite.address = ethcmn.HexToAddress(addrHex) suite.address = ethcmn.HexToAddress(addrHex)
@ -65,82 +63,6 @@ func TestKeeperTestSuite(t *testing.T) {
suite.Run(t, new(KeeperTestSuite)) suite.Run(t, new(KeeperTestSuite))
} }
func (suite *KeeperTestSuite) TestTransactionLogs() {
ethHash := ethcmn.BytesToHash(hash)
log := &ethtypes.Log{
Address: suite.address,
Data: []byte("log"),
BlockNumber: 10,
}
log2 := &ethtypes.Log{
Address: suite.address,
Data: []byte("log2"),
BlockNumber: 11,
}
expLogs := []*ethtypes.Log{log}
err := suite.app.EvmKeeper.CommitStateDB.SetLogs(ethHash, expLogs)
suite.Require().NoError(err)
logs, err := suite.app.EvmKeeper.CommitStateDB.GetLogs(ethHash)
suite.Require().NoError(err)
suite.Require().Equal(expLogs, logs)
expLogs = []*ethtypes.Log{log2, log}
// add another log under the zero hash
suite.app.EvmKeeper.CommitStateDB.AddLog(log2)
logs = suite.app.EvmKeeper.CommitStateDB.AllLogs()
suite.Require().Equal(expLogs, logs)
// add another log under the zero hash
log3 := &ethtypes.Log{
Address: suite.address,
Data: []byte("log3"),
BlockNumber: 10,
}
suite.app.EvmKeeper.CommitStateDB.AddLog(log3)
txLogs := suite.app.EvmKeeper.GetAllTxLogs(suite.ctx)
suite.Require().Equal(2, len(txLogs))
suite.Require().Equal(ethcmn.Hash{}.String(), txLogs[0].Hash)
suite.Require().Equal([]*ethtypes.Log{log2, log3}, txLogs[0].Logs)
suite.Require().Equal(ethHash.String(), txLogs[1].Hash)
suite.Require().Equal([]*ethtypes.Log{log}, txLogs[1].Logs)
}
func (suite *KeeperTestSuite) TestDBStorage() {
// Perform state transitions
suite.app.EvmKeeper.CommitStateDB.CreateAccount(suite.address)
suite.app.EvmKeeper.CommitStateDB.SetBalance(suite.address, big.NewInt(5))
suite.app.EvmKeeper.CommitStateDB.SetNonce(suite.address, 4)
suite.app.EvmKeeper.CommitStateDB.SetState(suite.address, ethcmn.HexToHash("0x2"), ethcmn.HexToHash("0x3"))
suite.app.EvmKeeper.CommitStateDB.SetCode(suite.address, []byte{0x1})
// Test block height mapping functionality
testBloom := ethtypes.BytesToBloom([]byte{0x1, 0x3})
suite.app.EvmKeeper.SetBlockBloom(suite.ctx, 4, testBloom)
// Get those state transitions
suite.Require().Equal(suite.app.EvmKeeper.CommitStateDB.GetBalance(suite.address).Cmp(big.NewInt(5)), 0)
suite.Require().Equal(suite.app.EvmKeeper.CommitStateDB.GetNonce(suite.address), uint64(4))
suite.Require().Equal(suite.app.EvmKeeper.CommitStateDB.GetState(suite.address, ethcmn.HexToHash("0x2")), ethcmn.HexToHash("0x3"))
suite.Require().Equal(suite.app.EvmKeeper.CommitStateDB.GetCode(suite.address), []byte{0x1})
bloom, found := suite.app.EvmKeeper.GetBlockBloom(suite.ctx, 4)
suite.Require().True(found)
suite.Require().Equal(bloom, testBloom)
// commit stateDB
_, err := suite.app.EvmKeeper.CommitStateDB.Commit(false)
suite.Require().NoError(err, "failed to commit StateDB")
// simulate BaseApp EndBlocker commitment
suite.app.Commit()
}
func (suite *KeeperTestSuite) TestChainConfig() { func (suite *KeeperTestSuite) TestChainConfig() {
config, found := suite.app.EvmKeeper.GetChainConfig(suite.ctx) config, found := suite.app.EvmKeeper.GetChainConfig(suite.ctx)
suite.Require().True(found) suite.Require().True(found)

View File

@ -9,7 +9,6 @@ import (
ethtypes "github.com/ethereum/go-ethereum/core/types" ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/store/prefix" "github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
@ -37,7 +36,8 @@ func (k *Keeper) CreateAccount(addr common.Address) {
k.ResetAccount(addr) k.ResetAccount(addr)
} }
_ = k.accountKeeper.NewAccountWithAddress(k.ctx, cosmosAddr) account = k.accountKeeper.NewAccountWithAddress(k.ctx, cosmosAddr)
k.accountKeeper.SetAccount(k.ctx, account)
k.Logger(k.ctx).Debug( k.Logger(k.ctx).Debug(
log, log,
@ -52,6 +52,15 @@ func (k *Keeper) CreateAccount(addr common.Address) {
// AddBalance calls CommitStateDB.AddBalance using the passed in context // AddBalance calls CommitStateDB.AddBalance using the passed in context
func (k *Keeper) AddBalance(addr common.Address, amount *big.Int) { func (k *Keeper) AddBalance(addr common.Address, amount *big.Int) {
if amount.Sign() != 1 {
k.Logger(k.ctx).Debug(
"ignored non-positive amount addition",
"ethereum-address", addr.Hex(),
"amount", amount.Int64(),
)
return
}
cosmosAddr := sdk.AccAddress(addr.Bytes()) cosmosAddr := sdk.AccAddress(addr.Bytes())
params := k.GetParams(k.ctx) params := k.GetParams(k.ctx)
@ -76,6 +85,15 @@ func (k *Keeper) AddBalance(addr common.Address, amount *big.Int) {
// SubBalance calls CommitStateDB.SubBalance using the passed in context // SubBalance calls CommitStateDB.SubBalance using the passed in context
func (k *Keeper) SubBalance(addr common.Address, amount *big.Int) { func (k *Keeper) SubBalance(addr common.Address, amount *big.Int) {
if amount.Sign() != 1 {
k.Logger(k.ctx).Debug(
"ignored non-positive amount addition",
"ethereum-address", addr.Hex(),
"amount", amount.Int64(),
)
return
}
cosmosAddr := sdk.AccAddress(addr.Bytes()) cosmosAddr := sdk.AccAddress(addr.Bytes())
params := k.GetParams(k.ctx) params := k.GetParams(k.ctx)
@ -128,7 +146,8 @@ func (k *Keeper) GetNonce(addr common.Address) uint64 {
return nonce return nonce
} }
// SetNonce calls CommitStateDB.SetNonce using the passed in context // SetNonce sets the given nonce as the sequence of the address' account. If the
// account doesn't exist, a new one will be created from the address.
func (k *Keeper) SetNonce(addr common.Address, nonce uint64) { func (k *Keeper) SetNonce(addr common.Address, nonce uint64) {
cosmosAddr := sdk.AccAddress(addr.Bytes()) cosmosAddr := sdk.AccAddress(addr.Bytes())
account := k.accountKeeper.GetAccount(k.ctx, cosmosAddr) account := k.accountKeeper.GetAccount(k.ctx, cosmosAddr)
@ -215,6 +234,7 @@ func (k *Keeper) SetCode(addr common.Address, code []byte) {
account := k.accountKeeper.GetAccount(k.ctx, addr.Bytes()) account := k.accountKeeper.GetAccount(k.ctx, addr.Bytes())
if account == nil { if account == nil {
account = k.accountKeeper.NewAccountWithAddress(k.ctx, addr.Bytes()) account = k.accountKeeper.NewAccountWithAddress(k.ctx, addr.Bytes())
k.accountKeeper.SetAccount(k.ctx, account)
} }
ethAccount, isEthAccount := account.(*ethermint.EthAccount) ethAccount, isEthAccount := account.(*ethermint.EthAccount)
@ -306,7 +326,8 @@ func (k *Keeper) GetRefund() uint64 {
// State // State
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// GetCommittedState calls CommitStateDB.GetCommittedState using the passed in context // GetCommittedState returns the value set in store for the given key hash. If the key is not registered
// this function returns the empty hash.
func (k *Keeper) GetCommittedState(addr common.Address, hash common.Hash) common.Hash { func (k *Keeper) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
store := prefix.NewStore(k.ctx.KVStore(k.storeKey), types.AddressStoragePrefix(addr)) store := prefix.NewStore(k.ctx.KVStore(k.storeKey), types.AddressStoragePrefix(addr))
@ -319,13 +340,14 @@ func (k *Keeper) GetCommittedState(addr common.Address, hash common.Hash) common
return common.BytesToHash(value) return common.BytesToHash(value)
} }
// GetState calls CommitStateDB.GetState using the passed in context // GetState returns the commited state for the given key hash, as all changes are commited directly
// to the KVStore.
func (k *Keeper) GetState(addr common.Address, hash common.Hash) common.Hash { func (k *Keeper) GetState(addr common.Address, hash common.Hash) common.Hash {
// All state is committed directly
return k.GetCommittedState(addr, hash) return k.GetCommittedState(addr, hash)
} }
// SetState calls CommitStateDB.SetState using the passed in context // SetState sets the given hashes (key, value) to the KVStore. If the value hash is empty, this
// function deletes the key from the store.
func (k *Keeper) SetState(addr common.Address, key, value common.Hash) { func (k *Keeper) SetState(addr common.Address, key, value common.Hash) {
store := prefix.NewStore(k.ctx.KVStore(k.storeKey), types.AddressStoragePrefix(addr)) store := prefix.NewStore(k.ctx.KVStore(k.storeKey), types.AddressStoragePrefix(addr))
key = types.KeyAddressStorage(addr, key) key = types.KeyAddressStorage(addr, key)
@ -415,6 +437,8 @@ func (k *Keeper) Exist(addr common.Address) bool {
// - nonce is 0 // - nonce is 0
// - balance amount for evm denom is 0 // - balance amount for evm denom is 0
// - account code hash is empty // - account code hash is empty
//
// Non-ethereum accounts are considered not empty
func (k *Keeper) Empty(addr common.Address) bool { func (k *Keeper) Empty(addr common.Address) bool {
nonce := uint64(0) nonce := uint64(0)
codeHash := types.EmptyCodeHash codeHash := types.EmptyCodeHash
@ -426,7 +450,6 @@ func (k *Keeper) Empty(addr common.Address) bool {
nonce = account.GetSequence() nonce = account.GetSequence()
ethAccount, isEthAccount := account.(*ethermint.EthAccount) ethAccount, isEthAccount := account.(*ethermint.EthAccount)
if !isEthAccount { if !isEthAccount {
// NOTE: non-ethereum accounts are considered not empty
return false return false
} }
@ -526,20 +549,31 @@ func (k *Keeper) RevertToSnapshot(_ int) {}
// context. This function also fills in the tx hash, block hash, tx index and log index fields before setting the log // context. This function also fills in the tx hash, block hash, tx index and log index fields before setting the log
// to store. // to store.
func (k *Keeper) AddLog(log *ethtypes.Log) { func (k *Keeper) AddLog(log *ethtypes.Log) {
txHash := common.BytesToHash(tmtypes.Tx(k.ctx.TxBytes()).Hash()) if len(k.ctx.TxBytes()) > 0 {
tx := &ethtypes.Transaction{}
if err := tx.UnmarshalBinary(k.ctx.TxBytes()); err != nil {
k.Logger(k.ctx).Error(
"ethereum tx unmarshaling failed",
"error", err,
)
return
}
log.TxHash = tx.Hash()
}
log.BlockHash = k.headerHash log.BlockHash = k.headerHash
log.TxHash = txHash
log.TxIndex = uint(k.GetTxIndexTransient()) log.TxIndex = uint(k.GetTxIndexTransient())
logs := k.GetTxLogs(txHash) logs := k.GetTxLogs(log.TxHash)
log.Index = uint(len(logs)) log.Index = uint(len(logs))
logs = append(logs, log) logs = append(logs, log)
k.SetLogs(txHash, logs) k.SetLogs(log.TxHash, logs)
k.Logger(k.ctx).Debug( k.Logger(k.ctx).Debug(
"log added", "log added",
"tx-hash", txHash.Hex(), "tx-hash", log.TxHash.Hex(),
"log-index", int(log.Index), "log-index", int(log.Index),
) )
} }

View File

@ -1 +1,601 @@
package keeper_test package keeper_test
import (
"fmt"
"math/big"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/ethermint/tests"
"github.com/cosmos/ethermint/x/evm/types"
"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
)
func (suite *KeeperTestSuite) TestCreateAccount() {
testCases := []struct {
name string
addr common.Address
malleate func(common.Address)
callback func(common.Address)
}{
{
"reset account",
suite.address,
func(addr common.Address) {
suite.app.EvmKeeper.AddBalance(addr, big.NewInt(100))
suite.Require().NotZero(suite.app.EvmKeeper.GetBalance(addr).Int64())
},
func(addr common.Address) {
suite.Require().Zero(suite.app.EvmKeeper.GetBalance(addr).Int64())
},
},
{
"create account",
tests.GenerateAddress(),
func(addr common.Address) {
acc := suite.app.AccountKeeper.GetAccount(suite.ctx, addr.Bytes())
suite.Require().Nil(acc)
},
func(addr common.Address) {
acc := suite.app.AccountKeeper.GetAccount(suite.ctx, addr.Bytes())
suite.Require().NotNil(acc)
},
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
tc.malleate(tc.addr)
suite.app.EvmKeeper.CreateAccount(tc.addr)
tc.callback(tc.addr)
})
}
}
func (suite *KeeperTestSuite) TestAddBalance() {
testCases := []struct {
name string
amount *big.Int
isNoOp bool
}{
{
"positive amount",
big.NewInt(100),
false,
},
{
"zero amount",
big.NewInt(0),
true,
},
{
"negative amount",
big.NewInt(-1),
true,
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
prev := suite.app.EvmKeeper.GetBalance(suite.address)
suite.app.EvmKeeper.AddBalance(suite.address, tc.amount)
post := suite.app.EvmKeeper.GetBalance(suite.address)
if tc.isNoOp {
suite.Require().Equal(prev.Int64(), post.Int64())
} else {
suite.Require().Equal(new(big.Int).Add(prev, tc.amount).Int64(), post.Int64())
}
})
}
}
func (suite *KeeperTestSuite) TestSubBalance() {
testCases := []struct {
name string
amount *big.Int
malleate func()
isNoOp bool
}{
{
"positive amount, below zero",
big.NewInt(100),
func() {},
true,
},
{
"positive amount, below zero",
big.NewInt(50),
func() {
suite.app.EvmKeeper.AddBalance(suite.address, big.NewInt(100))
},
false,
},
{
"zero amount",
big.NewInt(0),
func() {},
true,
},
{
"negative amount",
big.NewInt(-1),
func() {},
true,
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
tc.malleate()
prev := suite.app.EvmKeeper.GetBalance(suite.address)
suite.app.EvmKeeper.SubBalance(suite.address, tc.amount)
post := suite.app.EvmKeeper.GetBalance(suite.address)
if tc.isNoOp {
suite.Require().Equal(prev.Int64(), post.Int64())
} else {
suite.Require().Equal(new(big.Int).Sub(prev, tc.amount).Int64(), post.Int64())
}
})
}
}
func (suite *KeeperTestSuite) TestGetNonce() {
testCases := []struct {
name string
address common.Address
expectedNonce uint64
malleate func()
}{
{
"account not found",
tests.GenerateAddress(),
0,
func() {},
},
{
"existing account",
suite.address,
1,
func() {
suite.app.EvmKeeper.SetNonce(suite.address, 1)
},
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
tc.malleate()
nonce := suite.app.EvmKeeper.GetNonce(tc.address)
suite.Require().Equal(tc.expectedNonce, nonce)
})
}
}
func (suite *KeeperTestSuite) TestSetNonce() {
testCases := []struct {
name string
address common.Address
nonce uint64
malleate func()
}{
{
"new account",
tests.GenerateAddress(),
10,
func() {},
},
{
"existing account",
suite.address,
99,
func() {},
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.app.EvmKeeper.SetNonce(tc.address, tc.nonce)
nonce := suite.app.EvmKeeper.GetNonce(tc.address)
suite.Require().Equal(tc.nonce, nonce)
})
}
}
func (suite *KeeperTestSuite) TestGetCodeHash() {
addr := tests.GenerateAddress()
baseAcc := &authtypes.BaseAccount{Address: sdk.AccAddress(addr.Bytes()).String()}
suite.app.AccountKeeper.SetAccount(suite.ctx, baseAcc)
testCases := []struct {
name string
address common.Address
expHash common.Hash
malleate func()
}{
{
"account not found",
tests.GenerateAddress(),
common.BytesToHash(types.EmptyCodeHash),
func() {},
},
{
"account not EthAccount type",
addr,
common.BytesToHash(types.EmptyCodeHash),
func() {},
},
{
"existing account",
suite.address,
crypto.Keccak256Hash([]byte("codeHash")),
func() {
suite.app.EvmKeeper.SetCode(suite.address, []byte("codeHash"))
},
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
tc.malleate()
hash := suite.app.EvmKeeper.GetCodeHash(tc.address)
suite.Require().Equal(tc.expHash, hash)
})
}
}
func (suite *KeeperTestSuite) TestSetCode() {
addr := tests.GenerateAddress()
baseAcc := &authtypes.BaseAccount{Address: sdk.AccAddress(addr.Bytes()).String()}
suite.app.AccountKeeper.SetAccount(suite.ctx, baseAcc)
testCases := []struct {
name string
address common.Address
code []byte
isNoOp bool
}{
{
"account not found",
tests.GenerateAddress(),
[]byte("code"),
false,
},
{
"account not EthAccount type",
addr,
nil,
true,
},
{
"existing account",
suite.address,
[]byte("code"),
false,
},
{
"existing account, code deleted from store",
suite.address,
nil,
false,
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
prev := suite.app.EvmKeeper.GetCode(tc.address)
suite.app.EvmKeeper.SetCode(tc.address, tc.code)
post := suite.app.EvmKeeper.GetCode(tc.address)
if tc.isNoOp {
suite.Require().Equal(prev, post)
} else {
suite.Require().Equal(tc.code, post)
}
suite.Require().Equal(len(post), suite.app.EvmKeeper.GetCodeSize(tc.address))
})
}
}
func (suite *KeeperTestSuite) TestRefund() {
testCases := []struct {
name string
malleate func()
expRefund uint64
expPanic bool
}{
{
"success - add and subtract refund",
func() {
suite.app.EvmKeeper.AddRefund(11)
},
1,
false,
},
{
"fail - subtract amount > current refund",
func() {
},
0,
true,
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
tc.malleate()
if tc.expPanic {
suite.Require().Panics(func() { suite.app.EvmKeeper.SubRefund(10) })
} else {
suite.app.EvmKeeper.SubRefund(10)
suite.Require().Equal(tc.expRefund, suite.app.EvmKeeper.GetRefund())
}
// clear and reset refund from store
suite.app.EvmKeeper.ResetRefundTransient(suite.ctx)
suite.Require().Zero(suite.app.EvmKeeper.GetRefund())
})
}
}
func (suite *KeeperTestSuite) TestState() {
testCases := []struct {
name string
key, value common.Hash
}{
{
"set state - delete from store",
common.BytesToHash([]byte("key")),
common.Hash{},
},
{
"set state - update value",
common.BytesToHash([]byte("key")),
common.BytesToHash([]byte("value")),
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.app.EvmKeeper.SetState(suite.address, tc.key, tc.value)
value := suite.app.EvmKeeper.GetState(suite.address, tc.key)
suite.Require().Equal(tc.value, value)
})
}
}
func (suite *KeeperTestSuite) TestSuicide() {
testCases := []struct {
name string
suicided bool
}{
{"success, first time suicided", true},
{"success, already suicided", true},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.Require().Equal(tc.suicided, suite.app.EvmKeeper.Suicide(suite.address))
suite.Require().Equal(tc.suicided, suite.app.EvmKeeper.HasSuicided(suite.address))
})
}
}
func (suite *KeeperTestSuite) TestExist() {
testCases := []struct {
name string
address common.Address
malleate func()
exists bool
}{
{"success, account exists", suite.address, func() {}, true},
{"success, has suicided", suite.address, func() {
suite.app.EvmKeeper.Suicide(suite.address)
}, true},
{"success, account doesn't exist", tests.GenerateAddress(), func() {}, false},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
tc.malleate()
suite.Require().Equal(tc.exists, suite.app.EvmKeeper.Exist(tc.address))
})
}
}
func (suite *KeeperTestSuite) TestEmpty() {
addr := tests.GenerateAddress()
baseAcc := &authtypes.BaseAccount{Address: sdk.AccAddress(addr.Bytes()).String()}
suite.app.AccountKeeper.SetAccount(suite.ctx, baseAcc)
testCases := []struct {
name string
address common.Address
malleate func()
empty bool
}{
{"empty, account exists", suite.address, func() {}, true},
{"not empty, non ethereum account", addr, func() {}, false},
{"not empty, positive balance", suite.address, func() {
suite.app.EvmKeeper.AddBalance(suite.address, big.NewInt(100))
}, false},
{"empty, account doesn't exist", tests.GenerateAddress(), func() {}, true},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
tc.malleate()
suite.Require().Equal(tc.empty, suite.app.EvmKeeper.Empty(tc.address))
})
}
}
func (suite *KeeperTestSuite) TestSnapshot() {
revision := suite.app.EvmKeeper.Snapshot()
suite.Require().Zero(revision)
suite.app.EvmKeeper.RevertToSnapshot(revision) // no-op
}
func (suite *KeeperTestSuite) TestAddLog() {
addr := tests.GenerateAddress()
msg := types.NewMsgEthereumTx(big.NewInt(1), 0, &suite.address, big.NewInt(1), 100000, big.NewInt(1), []byte("test"), nil)
tx := msg.AsTransaction()
txBz, err := tx.MarshalBinary()
suite.Require().NoError(err)
txHash := tx.Hash()
testCases := []struct {
name string
log, expLog *ethtypes.Log // pre and post populating log fields
malleate func()
}{
{
"block hash not found",
&ethtypes.Log{
Address: addr,
},
&ethtypes.Log{
Address: addr,
},
func() {},
},
{
"tx hash from message",
&ethtypes.Log{
Address: addr,
},
&ethtypes.Log{
Address: addr,
TxHash: txHash,
},
func() {
suite.app.EvmKeeper.WithContext(suite.ctx.WithTxBytes(txBz))
},
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
tc.malleate()
prev := suite.app.EvmKeeper.GetTxLogs(tc.expLog.TxHash)
suite.app.EvmKeeper.AddLog(tc.log)
post := suite.app.EvmKeeper.GetTxLogs(tc.expLog.TxHash)
suite.Require().NotZero(len(post), tc.expLog.TxHash.Hex())
suite.Require().Equal(len(prev)+1, len(post))
suite.Require().NotNil(post[len(post)-1])
suite.Require().Equal(tc.log, post[len(post)-1])
})
}
}
func (suite *KeeperTestSuite) TestAccessList() {
dest := tests.GenerateAddress()
precompiles := []common.Address{tests.GenerateAddress(), tests.GenerateAddress()}
accesses := ethtypes.AccessList{
{Address: tests.GenerateAddress(), StorageKeys: []common.Hash{common.BytesToHash([]byte("key"))}},
{Address: tests.GenerateAddress(), StorageKeys: []common.Hash{common.BytesToHash([]byte("key1"))}},
}
suite.app.EvmKeeper.PrepareAccessList(suite.address, &dest, precompiles, accesses)
suite.Require().True(suite.app.EvmKeeper.AddressInAccessList(suite.address))
suite.Require().True(suite.app.EvmKeeper.AddressInAccessList(dest))
for _, precompile := range precompiles {
suite.Require().True(suite.app.EvmKeeper.AddressInAccessList(precompile))
}
for _, access := range accesses {
for _, key := range access.StorageKeys {
addrOK, slotOK := suite.app.EvmKeeper.SlotInAccessList(access.Address, key)
suite.Require().True(addrOK)
suite.Require().True(slotOK)
}
}
}
func (suite *KeeperTestSuite) TestForEachStorage() {
var storage types.Storage
testCase := []struct {
name string
malleate func()
callback func(key, value common.Hash) (stop bool)
expValues []common.Hash
}{
{
"aggregate state",
func() {
for i := 0; i < 5; i++ {
suite.app.EvmKeeper.SetState(suite.address, common.BytesToHash([]byte(fmt.Sprintf("key%d", i))), common.BytesToHash([]byte(fmt.Sprintf("value%d", i))))
}
},
func(key, value common.Hash) bool {
storage = append(storage, types.NewState(key, value))
return false
},
[]common.Hash{
common.BytesToHash([]byte("value0")),
common.BytesToHash([]byte("value1")),
common.BytesToHash([]byte("value2")),
common.BytesToHash([]byte("value3")),
common.BytesToHash([]byte("value4")),
},
},
{
"filter state",
func() {
suite.app.EvmKeeper.SetState(suite.address, common.BytesToHash([]byte("key")), common.BytesToHash([]byte("value")))
suite.app.EvmKeeper.SetState(suite.address, common.BytesToHash([]byte("filterkey")), common.BytesToHash([]byte("filtervalue")))
},
func(key, value common.Hash) bool {
if value == common.BytesToHash([]byte("filtervalue")) {
storage = append(storage, types.NewState(key, value))
return true
}
return false
},
[]common.Hash{
common.BytesToHash([]byte("filtervalue")),
},
},
}
for _, tc := range testCase {
suite.Run(tc.name, func() {
suite.SetupTest() // reset
tc.malleate()
err := suite.app.EvmKeeper.ForEachStorage(suite.address, tc.callback)
suite.Require().NoError(err)
suite.Require().Equal(len(tc.expValues), len(storage), fmt.Sprintf("Expected values:\n%v\nStorage Values\n%v", tc.expValues, storage))
vals := make([]common.Hash, len(storage))
for i := range storage {
vals[i] = common.HexToHash(storage[i].Value)
}
// TODO: not sure why Equals fails
suite.Require().ElementsMatch(tc.expValues, vals)
})
storage = types.Storage{}
}
}

View File

@ -4,8 +4,6 @@ import (
"errors" "errors"
"fmt" "fmt"
log "github.com/xlab/suplog"
ethcmn "github.com/ethereum/go-ethereum/common" ethcmn "github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types" ethtypes "github.com/ethereum/go-ethereum/core/types"
@ -77,9 +75,9 @@ func (log *Log) Validate() error {
// ToEthereum returns the Ethereum type Log from a Ethermint-proto compatible Log. // ToEthereum returns the Ethereum type Log from a Ethermint-proto compatible Log.
func (log *Log) ToEthereum() *ethtypes.Log { func (log *Log) ToEthereum() *ethtypes.Log {
topics := make([]ethcmn.Hash, len(log.Topics)) var topics []ethcmn.Hash // nolint: prealloc
for i := range log.Topics { for i := range log.Topics {
topics[i] = ethcmn.HexToHash(log.Topics[i]) topics = append(topics, ethcmn.HexToHash(log.Topics[i]))
} }
return &ethtypes.Log{ return &ethtypes.Log{
@ -97,24 +95,18 @@ func (log *Log) ToEthereum() *ethtypes.Log {
// LogsToEthereum casts the Ethermint Logs to a slice of Ethereum Logs. // LogsToEthereum casts the Ethermint Logs to a slice of Ethereum Logs.
func LogsToEthereum(logs []*Log) []*ethtypes.Log { func LogsToEthereum(logs []*Log) []*ethtypes.Log {
ethLogs := make([]*ethtypes.Log, len(logs)) var ethLogs []*ethtypes.Log // nolint: prealloc
for i := range logs { for i := range logs {
err := logs[i].Validate() ethLogs = append(ethLogs, logs[i].ToEthereum())
if err != nil {
log.WithError(err).Errorln("failed log validation", logs[i].String())
continue
}
ethLogs[i] = logs[i].ToEthereum()
} }
return ethLogs return ethLogs
} }
// NewLogFromEth creates a new Log instance from a Ethereum type Log. // NewLogFromEth creates a new Log instance from a Ethereum type Log.
func NewLogFromEth(log *ethtypes.Log) *Log { func NewLogFromEth(log *ethtypes.Log) *Log {
topics := make([]string, len(log.Topics)) var topics []string // nolint: prealloc
for i := range log.Topics { for _, topic := range log.Topics {
topics[i] = log.Topics[i].String() topics = append(topics, topic.String())
} }
return &Log{ return &Log{

View File

@ -89,7 +89,7 @@ func newMsgEthereumTx(
} }
// fromEthereumTx populates the message fields from the given ethereum transaction // fromEthereumTx populates the message fields from the given ethereum transaction
func (msg *MsgEthereumTx) fromEthereumTx(tx *ethtypes.Transaction) { func (msg *MsgEthereumTx) FromEthereumTx(tx *ethtypes.Transaction) {
to := "" to := ""
if tx.To() != nil { if tx.To() != nil {
to = tx.To().Hex() to = tx.To().Hex()
@ -227,7 +227,7 @@ func (msg *MsgEthereumTx) DecodeRLP(stream *rlp.Stream) error {
return err return err
} }
msg.fromEthereumTx(tx) msg.FromEthereumTx(tx)
return nil return nil
} }
@ -258,7 +258,7 @@ func (msg *MsgEthereumTx) Sign(ethSigner ethtypes.Signer, keyringSigner keyring.
return err return err
} }
msg.fromEthereumTx(tx) msg.FromEthereumTx(tx)
return nil return nil
} }