From 0cc1c40e341ea0673b24f447bf8d0700bbb24c63 Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Wed, 8 Jul 2020 20:11:02 +0200 Subject: [PATCH] x/evm: tests (#381) * run suite * add a few tests (#340) * add a few tests * fixes to tests * add more tests * check err to fix lint * add preimage and refund tests * add more more tests * fix linting errs * lint err Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * set stateDB on suite * fix genesis * logs tests * remove dup imports Co-authored-by: Daniel Choi --- x/evm/types/genesis_test.go | 91 +++++++++++++ x/evm/types/logs.go | 2 +- x/evm/types/logs_test.go | 166 +++++++++++++++++++++++ x/evm/types/statedb_test.go | 262 +++++++++++++++++++++++++++++++++++- 4 files changed, 513 insertions(+), 8 deletions(-) create mode 100644 x/evm/types/logs_test.go diff --git a/x/evm/types/genesis_test.go b/x/evm/types/genesis_test.go index 11b85349..e9fd62d8 100644 --- a/x/evm/types/genesis_test.go +++ b/x/evm/types/genesis_test.go @@ -4,9 +4,12 @@ import ( "math/big" "testing" + "github.com/cosmos/ethermint/crypto" "github.com/stretchr/testify/require" ethcmn "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + ethcrypto "github.com/ethereum/go-ethereum/crypto" ) func TestValidateGenesisAccount(t *testing.T) { @@ -99,6 +102,9 @@ func TestValidateGenesisAccount(t *testing.T) { } func TestValidateGenesis(t *testing.T) { + priv, err := crypto.GenerateKey() + require.NoError(t, err) + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) testCases := []struct { name string @@ -123,6 +129,24 @@ func TestValidateGenesis(t *testing.T) { }, }, }, + TxsLogs: []TransactionLogs{ + { + Hash: ethcmn.BytesToHash([]byte("tx_hash")), + Logs: []*ethtypes.Log{ + { + Address: addr, + Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, + Data: []byte("data"), + BlockNumber: 1, + TxHash: ethcmn.BytesToHash([]byte("tx_hash")), + TxIndex: 1, + BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + Index: 1, + Removed: false, + }, + }, + }, + }, }, expPass: true, }, @@ -161,6 +185,73 @@ func TestValidateGenesis(t *testing.T) { }, expPass: false, }, + { + name: "duplicated tx log", + genState: GenesisState{ + Accounts: []GenesisAccount{ + { + Address: ethcmn.BytesToAddress([]byte{1, 2, 3, 4, 5}), + Balance: big.NewInt(1), + Code: []byte{1, 2, 3}, + Storage: []GenesisStorage{ + {Key: ethcmn.BytesToHash([]byte{1, 2, 3})}, + }, + }, + }, + TxsLogs: []TransactionLogs{ + { + Hash: ethcmn.BytesToHash([]byte("tx_hash")), + Logs: []*ethtypes.Log{ + { + Address: addr, + Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, + Data: []byte("data"), + BlockNumber: 1, + TxHash: ethcmn.BytesToHash([]byte("tx_hash")), + TxIndex: 1, + BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + Index: 1, + Removed: false, + }, + }, + }, + { + Hash: ethcmn.BytesToHash([]byte("tx_hash")), + Logs: []*ethtypes.Log{ + { + Address: addr, + Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, + Data: []byte("data"), + BlockNumber: 1, + TxHash: ethcmn.BytesToHash([]byte("tx_hash")), + TxIndex: 1, + BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + Index: 1, + Removed: false, + }, + }, + }, + }, + }, + expPass: false, + }, + { + name: "invalid tx log", + genState: GenesisState{ + Accounts: []GenesisAccount{ + { + Address: ethcmn.BytesToAddress([]byte{1, 2, 3, 4, 5}), + Balance: big.NewInt(1), + Code: []byte{1, 2, 3}, + Storage: []GenesisStorage{ + {Key: ethcmn.BytesToHash([]byte{1, 2, 3})}, + }, + }, + }, + TxsLogs: []TransactionLogs{NewTransactionLogs(ethcmn.Hash{}, nil)}, + }, + expPass: false, + }, } for _, tc := range testCases { diff --git a/x/evm/types/logs.go b/x/evm/types/logs.go index 2c8c4c37..9694ddbe 100644 --- a/x/evm/types/logs.go +++ b/x/evm/types/logs.go @@ -47,7 +47,7 @@ func (tx TransactionLogs) Validate() error { if err := ValidateLog(log); err != nil { return fmt.Errorf("invalid log %d: %w", i, err) } - if bytes.Equal(log.TxHash.Bytes(), tx.Hash.Bytes()) { + if !bytes.Equal(log.TxHash.Bytes(), tx.Hash.Bytes()) { return fmt.Errorf("log tx hash mismatch (%s ≠ %s)", log.TxHash.String(), tx.Hash.String()) } } diff --git a/x/evm/types/logs_test.go b/x/evm/types/logs_test.go new file mode 100644 index 00000000..4665d82c --- /dev/null +++ b/x/evm/types/logs_test.go @@ -0,0 +1,166 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + + ethcmn "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + ethcrypto "github.com/ethereum/go-ethereum/crypto" + + "github.com/cosmos/ethermint/crypto" +) + +func TestTransactionLogsValidate(t *testing.T) { + priv, err := crypto.GenerateKey() + require.NoError(t, err) + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + + testCases := []struct { + name string + txLogs TransactionLogs + expPass bool + }{ + { + "valid log", + TransactionLogs{ + Hash: ethcmn.BytesToHash([]byte("tx_hash")), + Logs: []*ethtypes.Log{ + { + Address: addr, + Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, + Data: []byte("data"), + BlockNumber: 1, + TxHash: ethcmn.BytesToHash([]byte("tx_hash")), + TxIndex: 1, + BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + Index: 1, + Removed: false, + }, + }, + }, + true, + }, + { + "empty hash", + TransactionLogs{ + Hash: ethcmn.Hash{}, + }, + false, + }, + { + "invalid log", + TransactionLogs{ + Hash: ethcmn.BytesToHash([]byte("tx_hash")), + Logs: []*ethtypes.Log{nil}, + }, + false, + }, + { + "hash mismatch log", + TransactionLogs{ + Hash: ethcmn.BytesToHash([]byte("tx_hash")), + Logs: []*ethtypes.Log{ + { + Address: addr, + Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, + Data: []byte("data"), + BlockNumber: 1, + TxHash: ethcmn.BytesToHash([]byte("other_hash")), + TxIndex: 1, + BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + Index: 1, + Removed: false, + }, + }, + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + err := tc.txLogs.Validate() + if tc.expPass { + require.NoError(t, err, tc.name) + } else { + require.Error(t, err, tc.name) + } + } +} + +func TestValidateLog(t *testing.T) { + priv, err := crypto.GenerateKey() + require.NoError(t, err) + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + + testCases := []struct { + name string + log *ethtypes.Log + expPass bool + }{ + { + "valid log", + ðtypes.Log{ + Address: addr, + Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, + Data: []byte("data"), + BlockNumber: 1, + TxHash: ethcmn.BytesToHash([]byte("tx_hash")), + TxIndex: 1, + BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + Index: 1, + Removed: false, + }, + true, + }, + { + "nil log", nil, false, + }, + { + "zero address", + ðtypes.Log{ + Address: ethcmn.Address{}, + }, + false, + }, + { + "empty block hash", + ðtypes.Log{ + Address: addr, + BlockHash: ethcmn.Hash{}, + }, + false, + }, + { + "zero block number", + ðtypes.Log{ + Address: addr, + BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + BlockNumber: 0, + }, + false, + }, + { + "empty tx hash", + ðtypes.Log{ + Address: addr, + BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + BlockNumber: 1, + TxHash: ethcmn.Hash{}, + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + err := ValidateLog(tc.log) + if tc.expPass { + require.NoError(t, err, tc.name) + } else { + require.Error(t, err, tc.name) + } + } +} diff --git a/x/evm/types/statedb_test.go b/x/evm/types/statedb_test.go index 94bfabfb..01f29431 100644 --- a/x/evm/types/statedb_test.go +++ b/x/evm/types/statedb_test.go @@ -2,6 +2,7 @@ package types_test import ( "math/big" + "testing" "github.com/stretchr/testify/suite" @@ -9,20 +10,27 @@ import ( ethcmn "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" + ethcrypto "github.com/ethereum/go-ethereum/crypto" "github.com/cosmos/ethermint/app" + "github.com/cosmos/ethermint/crypto" "github.com/cosmos/ethermint/x/evm/keeper" + "github.com/cosmos/ethermint/x/evm/types" abci "github.com/tendermint/tendermint/abci/types" ) -// nolint: unused type StateDBTestSuite struct { suite.Suite ctx sdk.Context querier sdk.Querier app *app.EthermintApp + stateDB *types.CommitStateDB +} + +func TestStateDBTestSuite(t *testing.T) { + suite.Run(t, new(StateDBTestSuite)) } func (suite *StateDBTestSuite) SetupTest() { @@ -31,26 +39,25 @@ func (suite *StateDBTestSuite) SetupTest() { suite.app = app.Setup(checkTx) suite.ctx = suite.app.BaseApp.NewContext(checkTx, abci.Header{Height: 1}) suite.querier = keeper.NewQuerier(suite.app.EvmKeeper) + suite.stateDB = suite.app.EvmKeeper.CommitStateDB.WithContext(suite.ctx) } func (suite *StateDBTestSuite) TestBloomFilter() { - stateDB := suite.app.EvmKeeper.CommitStateDB - // Prepare db for logs tHash := ethcmn.BytesToHash([]byte{0x1}) - stateDB.Prepare(tHash, ethcmn.Hash{}, 0) + suite.stateDB.Prepare(tHash, ethcmn.Hash{}, 0) contractAddress := ethcmn.BigToAddress(big.NewInt(1)) // Generate and add a log to test log := ethtypes.Log{Address: contractAddress} - stateDB.AddLog(&log) + suite.stateDB.AddLog(&log) // Get log from db - logs, err := stateDB.GetLogs(tHash) + logs, err := suite.stateDB.GetLogs(tHash) suite.Require().NoError(err) suite.Require().Len(logs, 1) - suite.Require().Equal(log, logs[0]) + suite.Require().Equal(log, *logs[0]) // get logs bloom from the log bloomInt := ethtypes.LogsBloom(logs) @@ -60,3 +67,244 @@ func (suite *StateDBTestSuite) TestBloomFilter() { suite.Require().True(ethtypes.BloomLookup(bloomFilter, contractAddress)) suite.Require().False(ethtypes.BloomLookup(bloomFilter, ethcmn.BigToAddress(big.NewInt(2)))) } + +func (suite *StateDBTestSuite) TestStateDBBalance() { + priv, err := crypto.GenerateKey() + suite.Require().NoError(err) + + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + value := big.NewInt(100) + suite.stateDB.SetBalance(addr, value) + suite.Require().Equal(value, suite.stateDB.GetBalance(addr)) + + suite.stateDB.SubBalance(addr, value) + suite.Require().Equal(big.NewInt(0), suite.stateDB.GetBalance(addr)) + + suite.stateDB.AddBalance(addr, value) + suite.Require().Equal(value, suite.stateDB.GetBalance(addr)) +} + +func (suite *StateDBTestSuite) TestStateDBNonce() { + priv, err := crypto.GenerateKey() + suite.Require().NoError(err) + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + + nonce := uint64(123) + suite.stateDB.SetNonce(addr, nonce) + + suite.Require().Equal(nonce, suite.stateDB.GetNonce(addr)) +} + +func (suite *StateDBTestSuite) TestStateDBState() { + priv, err := crypto.GenerateKey() + suite.Require().NoError(err) + + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + key := ethcmn.BytesToHash([]byte("foo")) + val := ethcmn.BytesToHash([]byte("bar")) + + suite.stateDB.SetState(addr, key, val) + + suite.Require().Equal(val, suite.stateDB.GetState(addr, key)) +} + +func (suite *StateDBTestSuite) TestStateDBCode() { + priv, err := crypto.GenerateKey() + suite.Require().NoError(err) + + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + code := []byte("foobar") + + suite.stateDB.SetCode(addr, code) + + suite.Require().Equal(code, suite.stateDB.GetCode(addr)) + + codelen := len(code) + suite.Require().Equal(codelen, suite.stateDB.GetCodeSize(addr)) +} + +func (suite *StateDBTestSuite) TestStateDBLogs() { + priv, err := crypto.GenerateKey() + suite.Require().NoError(err) + + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + + hash := ethcmn.BytesToHash([]byte("hash")) + log := ethtypes.Log{ + Address: addr, + Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, + Data: []byte("data"), + BlockNumber: 1, + TxHash: ethcmn.Hash{}, + TxIndex: 1, + BlockHash: ethcmn.Hash{}, + Index: 1, + Removed: false, + } + logs := []*ethtypes.Log{&log} + + err = suite.stateDB.SetLogs(hash, logs) + suite.Require().NoError(err) + dbLogs, err := suite.stateDB.GetLogs(hash) + suite.Require().NoError(err) + suite.Require().Equal(logs, dbLogs) + + suite.stateDB.DeleteLogs(hash) + dbLogs, err = suite.stateDB.GetLogs(hash) + suite.Require().NoError(err) + suite.Require().Empty(dbLogs) + + suite.stateDB.AddLog(&log) + suite.Require().Equal(logs, suite.stateDB.AllLogs()) + + //resets state but checking to see if storekey still persists. + err = suite.stateDB.Reset(hash) + suite.Require().NoError(err) + suite.Require().Equal(logs, suite.stateDB.AllLogs()) +} + +func (suite *StateDBTestSuite) TestStateDBPreimage() { + hash := ethcmn.BytesToHash([]byte("hash")) + preimage := []byte("preimage") + + suite.stateDB.AddPreimage(hash, preimage) + + suite.Require().Equal(preimage, suite.stateDB.Preimages()[hash]) +} + +func (suite *StateDBTestSuite) TestStateDBRefund() { + value := uint64(100) + + suite.stateDB.AddRefund(value) + suite.Require().Equal(value, suite.stateDB.GetRefund()) + + suite.stateDB.SubRefund(value) + suite.Require().Equal(uint64(0), suite.stateDB.GetRefund()) +} + +func (suite *StateDBTestSuite) TestStateDBCreateAcct() { + priv, err := crypto.GenerateKey() + suite.Require().NoError(err) + + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + + suite.stateDB.CreateAccount(addr) + suite.Require().True(suite.stateDB.Exist(addr)) + + value := big.NewInt(100) + suite.stateDB.AddBalance(addr, value) + + suite.stateDB.CreateAccount(addr) + suite.Require().Equal(value, suite.stateDB.GetBalance(addr)) +} + +func (suite *StateDBTestSuite) TestStateDBClearStateOjb() { + priv, err := crypto.GenerateKey() + suite.Require().NoError(err) + + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + + suite.stateDB.CreateAccount(addr) + suite.Require().True(suite.stateDB.Exist(addr)) + + suite.stateDB.ClearStateObjects() + suite.Require().False(suite.stateDB.Exist(addr)) +} + +func (suite *StateDBTestSuite) TestStateDBReset() { + priv, err := crypto.GenerateKey() + suite.Require().NoError(err) + + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + + hash := ethcmn.BytesToHash([]byte("hash")) + + suite.stateDB.CreateAccount(addr) + suite.Require().True(suite.stateDB.Exist(addr)) + + err = suite.stateDB.Reset(hash) + suite.Require().NoError(err) + suite.Require().False(suite.stateDB.Exist(addr)) +} + +func (suite *StateDBTestSuite) TestStateDBUpdateAcct() { + +} + +func (suite *StateDBTestSuite) TestSuiteDBPrepare() { + thash := ethcmn.BytesToHash([]byte("thash")) + bhash := ethcmn.BytesToHash([]byte("bhash")) + txi := 1 + + suite.stateDB.Prepare(thash, bhash, txi) + + suite.Require().Equal(txi, suite.stateDB.TxIndex()) + suite.Require().Equal(bhash, suite.stateDB.BlockHash()) +} + +func (suite *StateDBTestSuite) TestSuiteDBCopyState() { + priv, err := crypto.GenerateKey() + suite.Require().NoError(err) + + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + + hash := ethcmn.BytesToHash([]byte("hash")) + log := ethtypes.Log{ + Address: addr, + Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, + Data: []byte("data"), + BlockNumber: 1, + TxHash: ethcmn.Hash{}, + TxIndex: 1, + BlockHash: ethcmn.Hash{}, + Index: 1, + Removed: false, + } + logs := []*ethtypes.Log{&log} + + err = suite.stateDB.SetLogs(hash, logs) + suite.Require().NoError(err) + + copyDB := suite.stateDB.Copy() + + copiedDBLogs, err := copyDB.GetLogs(hash) + suite.Require().NoError(err) + suite.Require().Equal(logs, copiedDBLogs) + suite.Require().Equal(suite.stateDB.Exist(addr), copyDB.Exist(addr)) +} + +func (suite *StateDBTestSuite) TestSuiteDBEmpty() { + priv, err := crypto.GenerateKey() + suite.Require().NoError(err) + + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + + suite.Require().True(suite.stateDB.Empty(addr)) + + suite.stateDB.SetBalance(addr, big.NewInt(100)) + + suite.Require().False(suite.stateDB.Empty(addr)) +} + +func (suite *StateDBTestSuite) TestSuiteDBSuicide() { + priv, err := crypto.GenerateKey() + suite.Require().NoError(err) + + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + + suicide := suite.stateDB.Suicide(addr) + suite.Require().False(suicide) + suite.Require().False(suite.stateDB.HasSuicided(addr)) + + //Suicide only works for an account with non-zero balance/nonce + suite.stateDB.SetBalance(addr, big.NewInt(100)) + suicide = suite.stateDB.Suicide(addr) + + suite.Require().True(suicide) + suite.Require().True(suite.stateDB.HasSuicided(addr)) + + delete := true + _, err = suite.stateDB.Commit(delete) + suite.Require().NoError(err) + suite.Require().False(suite.stateDB.Exist(addr)) +}