diff --git a/x/evm/keeper/statedb_test.go b/x/evm/keeper/statedb_test.go new file mode 100644 index 00000000..14049a53 --- /dev/null +++ b/x/evm/keeper/statedb_test.go @@ -0,0 +1,435 @@ +package keeper_test + +import ( + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + + 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" + ethermint "github.com/cosmos/ethermint/types" +) + +func (suite *KeeperTestSuite) TestBloomFilter() { + // Prepare db for logs + tHash := ethcmn.BytesToHash([]byte{0x1}) + suite.app.EvmKeeper.Prepare(suite.ctx, tHash, ethcmn.Hash{}, 0) + contractAddress := ethcmn.BigToAddress(big.NewInt(1)) + log := ethtypes.Log{Address: contractAddress} + + testCase := []struct { + name string + malleate func() + numLogs int + isBloom bool + }{ + { + "no logs", + func() {}, + 0, + false, + }, + { + "add log", + func() { + suite.app.EvmKeeper.AddLog(suite.ctx, &log) + }, + 1, + false, + }, + { + "bloom", + func() {}, + 0, + true, + }, + } + + for _, tc := range testCase { + tc.malleate() + logs, err := suite.app.EvmKeeper.GetLogs(suite.ctx, tHash) + if !tc.isBloom { + suite.Require().NoError(err, tc.name) + suite.Require().Len(logs, tc.numLogs, tc.name) + if len(logs) != 0 { + suite.Require().Equal(log, *logs[0], tc.name) + } + } else { + // get logs bloom from the log + bloomInt := ethtypes.LogsBloom(logs) + bloomFilter := ethtypes.BytesToBloom(bloomInt.Bytes()) + suite.Require().True(ethtypes.BloomLookup(bloomFilter, contractAddress), tc.name) + suite.Require().False(ethtypes.BloomLookup(bloomFilter, ethcmn.BigToAddress(big.NewInt(2))), tc.name) + } + } +} + +func (suite *KeeperTestSuite) TestStateDBBalance() { + testCase := []struct { + name string + malleate func() + balance *big.Int + }{ + { + "set balance", + func() { + suite.app.EvmKeeper.SetBalance(suite.ctx, suite.address, big.NewInt(100)) + }, + big.NewInt(100), + }, + { + "sub balance", + func() { + suite.app.EvmKeeper.SubBalance(suite.ctx, suite.address, big.NewInt(100)) + }, + big.NewInt(0), + }, + { + "add balance", + func() { + suite.app.EvmKeeper.AddBalance(suite.ctx, suite.address, big.NewInt(200)) + }, + big.NewInt(200), + }, + { + "sub more than balance", + func() { + suite.app.EvmKeeper.SubBalance(suite.ctx, suite.address, big.NewInt(300)) + }, + big.NewInt(-100), + }, + } + + for _, tc := range testCase { + tc.malleate() + suite.Require().Equal(tc.balance, suite.app.EvmKeeper.GetBalance(suite.ctx, suite.address), tc.name) + + } +} + +func (suite *KeeperTestSuite) TestStateDBNonce() { + nonce := uint64(123) + suite.app.EvmKeeper.SetNonce(suite.ctx, suite.address, nonce) + suite.Require().Equal(nonce, suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address)) +} + +func (suite *KeeperTestSuite) TestStateDBState() { + key := ethcmn.BytesToHash([]byte("foo")) + val := ethcmn.BytesToHash([]byte("bar")) + + suite.app.EvmKeeper.SetState(suite.ctx, suite.address, key, val) + suite.Require().Equal(val, suite.app.EvmKeeper.GetState(suite.ctx, suite.address, key)) +} + +func (suite *KeeperTestSuite) TestStateDBCode() { + code := []byte("foobar") + + suite.app.EvmKeeper.SetCode(suite.ctx, suite.address, code) + + suite.Require().Equal(code, suite.app.EvmKeeper.GetCode(suite.ctx, suite.address)) + + codelen := len(code) + suite.Require().Equal(codelen, suite.app.EvmKeeper.GetCodeSize(suite.ctx, suite.address)) +} + +func (suite *KeeperTestSuite) TestStateDBLogs() { + testCase := []struct { + name string + log ethtypes.Log + }{ + { + "state db log", + ethtypes.Log{ + Address: suite.address, + Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, + Data: []byte("data"), + BlockNumber: 1, + TxHash: ethcmn.Hash{}, + TxIndex: 1, + BlockHash: ethcmn.Hash{}, + Index: 1, + Removed: false, + }, + }, + } + + for _, tc := range testCase { + hash := ethcmn.BytesToHash([]byte("hash")) + logs := []*ethtypes.Log{&tc.log} + + err := suite.app.EvmKeeper.SetLogs(suite.ctx, hash, logs) + suite.Require().NoError(err, tc.name) + dbLogs, err := suite.app.EvmKeeper.GetLogs(suite.ctx, hash) + suite.Require().NoError(err, tc.name) + suite.Require().Equal(logs, dbLogs, tc.name) + suite.Require().Equal(logs, suite.app.EvmKeeper.AllLogs(suite.ctx), tc.name) + + //resets state but checking to see if storekey still persists. + err = suite.app.EvmKeeper.Reset(suite.ctx, hash) + suite.Require().NoError(err, tc.name) + suite.Require().Equal(logs, suite.app.EvmKeeper.AllLogs(suite.ctx), tc.name) + } +} + +func (suite *KeeperTestSuite) TestStateDBPreimage() { + hash := ethcmn.BytesToHash([]byte("hash")) + preimage := []byte("preimage") + + suite.app.EvmKeeper.AddPreimage(suite.ctx, hash, preimage) + + suite.Require().Equal(preimage, suite.app.EvmKeeper.Preimages(suite.ctx)[hash]) +} + +func (suite *KeeperTestSuite) TestStateDBRefund() { + testCase := []struct { + name string + amount uint64 + }{ + { + "refund", + 100, + }, + } + + for _, tc := range testCase { + suite.app.EvmKeeper.AddRefund(suite.ctx, tc.amount) + suite.Require().Equal(tc.amount, suite.app.EvmKeeper.GetRefund(suite.ctx), tc.name) + + suite.app.EvmKeeper.SubRefund(suite.ctx, tc.amount) + suite.Require().Equal(uint64(0), suite.app.EvmKeeper.GetRefund(suite.ctx), tc.name) + } +} + +func (suite *KeeperTestSuite) TestStateDBCreateAcct() { + suite.app.EvmKeeper.CreateAccount(suite.ctx, suite.address) + suite.Require().True(suite.app.EvmKeeper.Exist(suite.ctx, suite.address)) + + value := big.NewInt(100) + suite.app.EvmKeeper.AddBalance(suite.ctx, suite.address, value) + + suite.app.EvmKeeper.CreateAccount(suite.ctx, suite.address) + suite.Require().Equal(value, suite.app.EvmKeeper.GetBalance(suite.ctx, suite.address)) +} + +func (suite *KeeperTestSuite) TestStateDBClearStateOjb() { + + suite.app.EvmKeeper.CreateAccount(suite.ctx, suite.address) + suite.Require().True(suite.app.EvmKeeper.Exist(suite.ctx, suite.address)) + + suite.app.EvmKeeper.ClearStateObjects(suite.ctx) + suite.Require().False(suite.app.EvmKeeper.Exist(suite.ctx, suite.address)) +} + +func (suite *KeeperTestSuite) TestStateDBReset() { + hash := ethcmn.BytesToHash([]byte("hash")) + + suite.app.EvmKeeper.CreateAccount(suite.ctx, suite.address) + suite.Require().True(suite.app.EvmKeeper.Exist(suite.ctx, suite.address)) + + err := suite.app.EvmKeeper.Reset(suite.ctx, hash) + suite.Require().NoError(err) + suite.Require().False(suite.app.EvmKeeper.Exist(suite.ctx, suite.address)) + +} + +func (suite *KeeperTestSuite) TestStateDBUpdateAcct() { + +} + +func (suite *KeeperTestSuite) TestSuiteDBPrepare() { + thash := ethcmn.BytesToHash([]byte("thash")) + bhash := ethcmn.BytesToHash([]byte("bhash")) + txi := 1 + + suite.app.EvmKeeper.Prepare(suite.ctx, thash, bhash, txi) + + suite.Require().Equal(txi, suite.app.EvmKeeper.TxIndex(suite.ctx)) + suite.Require().Equal(bhash, suite.app.EvmKeeper.BlockHash(suite.ctx)) + +} + +func (suite *KeeperTestSuite) TestSuiteDBCopyState() { + copyDB := suite.app.EvmKeeper.Copy(suite.ctx) + suite.Require().Equal(suite.app.EvmKeeper.Exist(suite.ctx, suite.address), copyDB.Exist(suite.address)) +} + +func (suite *KeeperTestSuite) TestSuiteDBEmpty() { + suite.Require().True(suite.app.EvmKeeper.Empty(suite.ctx, suite.address)) + + suite.app.EvmKeeper.SetBalance(suite.ctx, suite.address, big.NewInt(100)) + + suite.Require().False(suite.app.EvmKeeper.Empty(suite.ctx, suite.address)) +} + +func (suite *KeeperTestSuite) TestSuiteDBSuicide() { + + testCase := []struct { + name string + amount *big.Int + expPass bool + delete bool + }{ + { + "suicide zero balance", + big.NewInt(0), + false, false, + }, + { + "suicide with balance", + big.NewInt(100), + true, false, + }, + { + "delete", + big.NewInt(0), + true, true, + }, + } + + for _, tc := range testCase { + if tc.delete { + _, err := suite.app.EvmKeeper.Commit(suite.ctx, tc.delete) + suite.Require().NoError(err, tc.name) + suite.Require().False(suite.app.EvmKeeper.Exist(suite.ctx, suite.address), tc.name) + continue + } + + if tc.expPass { + suite.app.EvmKeeper.SetBalance(suite.ctx, suite.address, tc.amount) + suicide := suite.app.EvmKeeper.Suicide(suite.ctx, suite.address) + suite.Require().True(suicide, tc.name) + suite.Require().True(suite.app.EvmKeeper.HasSuicided(suite.ctx, suite.address), tc.name) + } else { + //Suicide only works for an account with non-zero balance/nonce + priv, err := crypto.GenerateKey() + suite.Require().NoError(err) + + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + suicide := suite.app.EvmKeeper.Suicide(suite.ctx, addr) + suite.Require().False(suicide, tc.name) + suite.Require().False(suite.app.EvmKeeper.HasSuicided(suite.ctx, addr), tc.name) + } + } +} + +func (suite *KeeperTestSuite) TestCommitStateDB_Commit() { + suite.app.EvmKeeper.AddBalance(suite.ctx, suite.address, big.NewInt(100)) + testCase := []struct { + name string + malleate func() + deleteObjs bool + expPass bool + }{ + { + "commit suicided", + func() { + ok := suite.app.EvmKeeper.Suicide(suite.ctx, suite.address) + suite.Require().True(ok) + }, + true, true, + }, + { + "commit with dirty value", + func() { + suite.app.EvmKeeper.SetCode(suite.ctx, suite.address, []byte("code")) + }, + false, true, + }, + { + "faled to update state object", + func() { + suite.app.EvmKeeper.SubBalance(suite.ctx, suite.address, big.NewInt(10)) + }, + false, false, + }, + } + + for _, tc := range testCase { + tc.malleate() + + hash, err := suite.app.EvmKeeper.Commit(suite.ctx, tc.deleteObjs) + suite.Require().Equal(ethcmn.Hash{}, hash) + + if !tc.expPass { + suite.Require().Error(err, tc.name) + continue + } + + suite.Require().NoError(err, tc.name) + acc := suite.app.AccountKeeper.GetAccount(suite.ctx, sdk.AccAddress(suite.address.Bytes())) + + if tc.deleteObjs { + suite.Require().Nil(acc, tc.name) + continue + } + + suite.Require().NotNil(acc, tc.name) + ethAcc, ok := acc.(*ethermint.EthAccount) + suite.Require().True(ok) + suite.Require().Equal(ethcrypto.Keccak256([]byte("code")), ethAcc.CodeHash) + } +} + +func (suite *KeeperTestSuite) TestCommitStateDB_Finalize() { + suite.app.EvmKeeper.AddBalance(suite.ctx, suite.address, big.NewInt(100)) + testCase := []struct { + name string + malleate func() + deleteObjs bool + expPass bool + }{ + { + "finalize suicided", + func() { + ok := suite.app.EvmKeeper.Suicide(suite.ctx, suite.address) + suite.Require().True(ok) + }, + true, true, + }, + { + "finalize, not suicided", + func() { + suite.app.EvmKeeper.AddBalance(suite.ctx, suite.address, big.NewInt(5)) + }, + false, true, + }, + { + "finalize, dirty storage", + func() { + suite.app.EvmKeeper.SetState(suite.ctx, suite.address, ethcmn.BytesToHash([]byte("key")), ethcmn.BytesToHash([]byte("value"))) + }, + false, true, + }, + { + "faled to update state object", + func() { + suite.app.EvmKeeper.SubBalance(suite.ctx, suite.address, big.NewInt(10)) + }, + false, false, + }, + } + + for _, tc := range testCase { + tc.malleate() + + err := suite.app.EvmKeeper.Finalise(suite.ctx, tc.deleteObjs) + + if !tc.expPass { + suite.Require().Error(err, tc.name) + continue + } + + suite.Require().NoError(err, tc.name) + acc := suite.app.AccountKeeper.GetAccount(suite.ctx, sdk.AccAddress(suite.address.Bytes())) + + if tc.deleteObjs { + suite.Require().Nil(acc, tc.name) + continue + } + + suite.Require().NotNil(acc, tc.name) + } +} diff --git a/x/evm/types/statedb.go b/x/evm/types/statedb.go index 446fbb2d..0e54be25 100644 --- a/x/evm/types/statedb.go +++ b/x/evm/types/statedb.go @@ -292,7 +292,6 @@ func (csdb *CommitStateDB) GetCodeSize(addr ethcmn.Address) int { return len(so.code) } - // TODO: we may need to cache these lookups directly return len(so.Code(nil)) } diff --git a/x/evm/types/statedb_test.go b/x/evm/types/statedb_test.go index f8a8c437..716e157e 100644 --- a/x/evm/types/statedb_test.go +++ b/x/evm/types/statedb_test.go @@ -57,126 +57,225 @@ func (suite *StateDBTestSuite) SetupTest() { suite.app.AccountKeeper.SetAccount(suite.ctx, acc) suite.stateObject = suite.stateDB.GetOrNewStateObject(suite.address) } - func (suite *StateDBTestSuite) TestBloomFilter() { // Prepare db for logs tHash := ethcmn.BytesToHash([]byte{0x1}) 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} - suite.stateDB.AddLog(&log) - // Get log from db - logs, err := suite.stateDB.GetLogs(tHash) - suite.Require().NoError(err) - suite.Require().Len(logs, 1) - suite.Require().Equal(log, *logs[0]) + testCase := []struct { + name string + malleate func() + numLogs int + isBloom bool + }{ + { + "no logs", + func() {}, + 0, + false, + }, + { + "add log", + func() { + suite.stateDB.AddLog(&log) + }, + 1, + false, + }, + { + "bloom", + func() {}, + 0, + true, + }, + } - // get logs bloom from the log - bloomInt := ethtypes.LogsBloom(logs) - bloomFilter := ethtypes.BytesToBloom(bloomInt.Bytes()) - - // Check to make sure bloom filter will succeed on - suite.Require().True(ethtypes.BloomLookup(bloomFilter, contractAddress)) - suite.Require().False(ethtypes.BloomLookup(bloomFilter, ethcmn.BigToAddress(big.NewInt(2)))) + for _, tc := range testCase { + tc.malleate() + logs, err := suite.stateDB.GetLogs(tHash) + if !tc.isBloom { + suite.Require().NoError(err, tc.name) + suite.Require().Len(logs, tc.numLogs, tc.name) + if len(logs) != 0 { + suite.Require().Equal(log, *logs[0], tc.name) + } + } else { + // get logs bloom from the log + bloomInt := ethtypes.LogsBloom(logs) + bloomFilter := ethtypes.BytesToBloom(bloomInt.Bytes()) + suite.Require().True(ethtypes.BloomLookup(bloomFilter, contractAddress), tc.name) + suite.Require().False(ethtypes.BloomLookup(bloomFilter, ethcmn.BigToAddress(big.NewInt(2))), tc.name) + } + } } func (suite *StateDBTestSuite) TestStateDBBalance() { - priv, err := crypto.GenerateKey() - suite.Require().NoError(err) + testCase := []struct { + name string + malleate func() + balance *big.Int + }{ + { + "set balance", + func() { + suite.stateDB.SetBalance(suite.address, big.NewInt(100)) + }, + big.NewInt(100), + }, + { + "sub balance", + func() { + suite.stateDB.SubBalance(suite.address, big.NewInt(100)) + }, + big.NewInt(0), + }, + { + "add balance", + func() { + suite.stateDB.AddBalance(suite.address, big.NewInt(200)) + }, + big.NewInt(200), + }, + { + "sub more than balance", + func() { + suite.stateDB.SubBalance(suite.address, big.NewInt(300)) + }, + big.NewInt(-100), + }, + } - 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)) + for _, tc := range testCase { + tc.malleate() + suite.Require().Equal(tc.balance, suite.stateDB.GetBalance(suite.address), tc.name) + } } 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)) + suite.stateDB.SetNonce(suite.address, nonce) + suite.Require().Equal(nonce, suite.stateDB.GetNonce(suite.address)) } 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(suite.address, key, val) - suite.stateDB.SetState(addr, key, val) - - suite.Require().Equal(val, suite.stateDB.GetState(addr, key)) + testCase := []struct { + name string + address ethcmn.Address + key ethcmn.Hash + value ethcmn.Hash + }{ + { + "found state", + suite.address, + ethcmn.BytesToHash([]byte("foo")), + ethcmn.BytesToHash([]byte("bar")), + }, + { + "state not found", + suite.address, + ethcmn.BytesToHash([]byte("key")), + ethcmn.Hash{}, + }, + { + "object not found", + ethcmn.Address{}, + ethcmn.BytesToHash([]byte("foo")), + ethcmn.Hash{}, + }, + } + for _, tc := range testCase { + value := suite.stateDB.GetState(tc.address, tc.key) + suite.Require().Equal(tc.value, value, tc.name) + } } func (suite *StateDBTestSuite) TestStateDBCode() { - priv, err := crypto.GenerateKey() - suite.Require().NoError(err) + testCase := []struct { + name string + address ethcmn.Address + code []byte + malleate func() + }{ + { + "no stored code for state object", + suite.address, + nil, + func() {}, + }, + { + "existing address", + suite.address, + []byte("code"), + func() { + suite.stateDB.SetCode(suite.address, []byte("code")) + }, + }, + { + "state object not found", + ethcmn.Address{}, + nil, + func() {}, + }, + } - addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) - code := []byte("foobar") + for _, tc := range testCase { + tc.malleate() - suite.stateDB.SetCode(addr, code) - - suite.Require().Equal(code, suite.stateDB.GetCode(addr)) - - codelen := len(code) - suite.Require().Equal(codelen, suite.stateDB.GetCodeSize(addr)) + suite.Require().Equal(tc.code, suite.stateDB.GetCode(tc.address), tc.name) + suite.Require().Equal(len(tc.code), suite.stateDB.GetCodeSize(tc.address), tc.name) + } } 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, + testCase := []struct { + name string + log ethtypes.Log + }{ + { + "state db log", + ethtypes.Log{ + Address: suite.address, + 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) + for _, tc := range testCase { + hash := ethcmn.BytesToHash([]byte("hash")) + logs := []*ethtypes.Log{&tc.log} - suite.stateDB.DeleteLogs(hash) - dbLogs, err = suite.stateDB.GetLogs(hash) - suite.Require().NoError(err) - suite.Require().Empty(dbLogs) + err := suite.stateDB.SetLogs(hash, logs) + suite.Require().NoError(err, tc.name) + dbLogs, err := suite.stateDB.GetLogs(hash) + suite.Require().NoError(err, tc.name) + suite.Require().Equal(logs, dbLogs, tc.name) - suite.stateDB.AddLog(&log) - suite.Require().Equal(logs, suite.stateDB.AllLogs()) + suite.stateDB.DeleteLogs(hash) + dbLogs, err = suite.stateDB.GetLogs(hash) + suite.Require().NoError(err, tc.name) + suite.Require().Empty(dbLogs, tc.name) - //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()) + suite.stateDB.AddLog(&tc.log) + suite.Require().Equal(logs, suite.stateDB.AllLogs(), tc.name) + + //resets state but checking to see if storekey still persists. + err = suite.stateDB.Reset(hash) + suite.Require().NoError(err, tc.name) + suite.Require().Equal(logs, suite.stateDB.AllLogs(), tc.name) + } } func (suite *StateDBTestSuite) TestStateDBPreimage() { @@ -189,29 +288,80 @@ func (suite *StateDBTestSuite) TestStateDBPreimage() { } func (suite *StateDBTestSuite) TestStateDBRefund() { - value := uint64(100) + testCase := []struct { + name string + addAmount uint64 + subAmount uint64 + expRefund uint64 + expPanic bool + }{ + { + "refund 0", + 0, 0, 0, + false, + }, + { + "refund positive amount", + 100, 0, 100, + false, + }, + { + "refund panic", + 100, 200, 100, + true, + }, + } - suite.stateDB.AddRefund(value) - suite.Require().Equal(value, suite.stateDB.GetRefund()) + for _, tc := range testCase { + suite.Run(tc.name, func() { + suite.SetupTest() // reset - suite.stateDB.SubRefund(value) - suite.Require().Equal(uint64(0), suite.stateDB.GetRefund()) + suite.stateDB.AddRefund(tc.addAmount) + suite.Require().Equal(tc.addAmount, suite.stateDB.GetRefund()) + + if tc.expPanic { + suite.Panics(func() { + suite.stateDB.SubRefund(tc.subAmount) + }) + } else { + suite.stateDB.SubRefund(tc.subAmount) + suite.Require().Equal(tc.expRefund, suite.stateDB.GetRefund()) + } + }) + } } func (suite *StateDBTestSuite) TestStateDBCreateAcct() { - priv, err := crypto.GenerateKey() - suite.Require().NoError(err) + prevBalance := big.NewInt(12) - addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + testCase := []struct { + name string + address ethcmn.Address + malleate func() + }{ + { + "existing account", + suite.address, + func() { + suite.stateDB.AddBalance(suite.address, prevBalance) + }, + }, + { + "new account", + ethcmn.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b4c1"), + func() { + prevBalance = big.NewInt(0) + }, + }, + } - suite.stateDB.CreateAccount(addr) - suite.Require().True(suite.stateDB.Exist(addr)) + for _, tc := range testCase { + tc.malleate() - value := big.NewInt(100) - suite.stateDB.AddBalance(addr, value) - - suite.stateDB.CreateAccount(addr) - suite.Require().Equal(value, suite.stateDB.GetBalance(addr)) + suite.stateDB.CreateAccount(tc.address) + suite.Require().True(suite.stateDB.Exist(tc.address), tc.name) + suite.Require().Equal(prevBalance, suite.stateDB.GetBalance(tc.address), tc.name) + } } func (suite *StateDBTestSuite) TestStateDBClearStateOjb() { @@ -233,20 +383,14 @@ func (suite *StateDBTestSuite) TestStateDBReset() { 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) + err = suite.stateDB.Reset(ethcmn.BytesToHash(nil)) 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")) @@ -259,70 +403,99 @@ func (suite *StateDBTestSuite) TestSuiteDBPrepare() { } 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, + testCase := []struct { + name string + log ethtypes.Log + }{ + { + "copy state", + ethtypes.Log{ + Address: suite.address, + 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) + for _, tc := range testCase { + hash := ethcmn.BytesToHash([]byte("hash")) + logs := []*ethtypes.Log{&tc.log} - copyDB := suite.stateDB.Copy() + err := suite.stateDB.SetLogs(hash, logs) + suite.Require().NoError(err, tc.name) - copiedDBLogs, err := copyDB.GetLogs(hash) - suite.Require().NoError(err) - suite.Require().Equal(logs, copiedDBLogs) - suite.Require().Equal(suite.stateDB.Exist(addr), copyDB.Exist(addr)) + copyDB := suite.stateDB.Copy() + + copiedDBLogs, err := copyDB.GetLogs(hash) + suite.Require().NoError(err, tc.name) + suite.Require().Equal(logs, copiedDBLogs, tc.name) + suite.Require().Equal(suite.stateDB.Exist(suite.address), copyDB.Exist(suite.address), tc.name) + } } func (suite *StateDBTestSuite) TestSuiteDBEmpty() { - priv, err := crypto.GenerateKey() - suite.Require().NoError(err) + suite.Require().True(suite.stateDB.Empty(suite.address)) - addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + suite.stateDB.SetBalance(suite.address, big.NewInt(100)) - suite.Require().True(suite.stateDB.Empty(addr)) - - suite.stateDB.SetBalance(addr, big.NewInt(100)) - - suite.Require().False(suite.stateDB.Empty(addr)) + suite.Require().False(suite.stateDB.Empty(suite.address)) } func (suite *StateDBTestSuite) TestSuiteDBSuicide() { - priv, err := crypto.GenerateKey() - suite.Require().NoError(err) - addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + testCase := []struct { + name string + amount *big.Int + expPass bool + delete bool + }{ + { + "suicide zero balance", + big.NewInt(0), + false, false, + }, + { + "suicide with balance", + big.NewInt(100), + true, false, + }, + { + "delete", + big.NewInt(0), + true, true, + }, + } - suicide := suite.stateDB.Suicide(addr) - suite.Require().False(suicide) - suite.Require().False(suite.stateDB.HasSuicided(addr)) + for _, tc := range testCase { + if tc.delete { + _, err := suite.stateDB.Commit(tc.delete) + suite.Require().NoError(err, tc.name) + suite.Require().False(suite.stateDB.Exist(suite.address), tc.name) + continue + } - //Suicide only works for an account with non-zero balance/nonce - suite.stateDB.SetBalance(addr, big.NewInt(100)) - suicide = suite.stateDB.Suicide(addr) + if tc.expPass { + suite.stateDB.SetBalance(suite.address, tc.amount) + suicide := suite.stateDB.Suicide(suite.address) + suite.Require().True(suicide, tc.name) + suite.Require().True(suite.stateDB.HasSuicided(suite.address), tc.name) + } else { + //Suicide only works for an account with non-zero balance/nonce + priv, err := crypto.GenerateKey() + suite.Require().NoError(err) - 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)) + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + suicide := suite.stateDB.Suicide(addr) + suite.Require().False(suicide, tc.name) + suite.Require().False(suite.stateDB.HasSuicided(addr), tc.name) + } + } } func (suite *StateDBTestSuite) TestCommitStateDB_Commit() {