diff --git a/x/evm/types/state_object_test.go b/x/evm/types/state_object_test.go new file mode 100644 index 00000000..da2ec013 --- /dev/null +++ b/x/evm/types/state_object_test.go @@ -0,0 +1,117 @@ +package types_test + +import ( + "math/big" + + ethcmn "github.com/ethereum/go-ethereum/common" +) + +func (suite *StateDBTestSuite) TestStateObject_State() { + testCase := []struct { + name string + key ethcmn.Hash + expValue ethcmn.Hash + malleate func() + }{ + { + "no set value, load from KVStore", + ethcmn.BytesToHash([]byte("key")), + ethcmn.Hash{}, + func() {}, + }, + { + "no-op SetState", + ethcmn.BytesToHash([]byte("key")), + ethcmn.Hash{}, + func() { + suite.stateObject.SetState(nil, ethcmn.BytesToHash([]byte("key")), ethcmn.Hash{}) + }, + }, + { + "cached value", + ethcmn.BytesToHash([]byte("key1")), + ethcmn.BytesToHash([]byte("value1")), + func() { + suite.stateObject.SetState(nil, ethcmn.BytesToHash([]byte("key1")), ethcmn.BytesToHash([]byte("value1"))) + }, + }, + } + + for _, tc := range testCase { + tc.malleate() + + value := suite.stateObject.GetState(nil, tc.key) + suite.Require().Equal(tc.expValue, value, tc.name) + } +} + +func (suite *StateDBTestSuite) TestStateObject_AddBalance() { + testCase := []struct { + name string + amount *big.Int + expBalance *big.Int + }{ + {"zero amount", big.NewInt(0), big.NewInt(0)}, + {"positive amount", big.NewInt(10), big.NewInt(10)}, + {"negative amount", big.NewInt(-1), big.NewInt(9)}, + } + + for _, tc := range testCase { + suite.stateObject.AddBalance(tc.amount) + suite.Require().Equal(tc.expBalance, suite.stateObject.Balance(), tc.name) + } +} + +func (suite *StateDBTestSuite) TestStateObject_SubBalance() { + testCase := []struct { + name string + amount *big.Int + expBalance *big.Int + }{ + {"zero amount", big.NewInt(0), big.NewInt(0)}, + {"negative amount", big.NewInt(-10), big.NewInt(10)}, + {"positive amount", big.NewInt(1), big.NewInt(9)}, + } + + for _, tc := range testCase { + suite.stateObject.SubBalance(tc.amount) + suite.Require().Equal(tc.expBalance, suite.stateObject.Balance(), tc.name) + } +} + +func (suite *StateDBTestSuite) TestStateObject_Code() { + testCase := []struct { + name string + expCode []byte + malleate func() + }{ + { + "cached code", + []byte("code"), + func() { + suite.stateObject.SetCode(ethcmn.BytesToHash([]byte("code_hash")), []byte("code")) + }, + }, + { + "empty code hash", + nil, + func() { + suite.stateObject.SetCode(ethcmn.Hash{}, nil) + }, + }, + { + "empty code", + nil, + func() { + suite.stateObject.SetCode(ethcmn.BytesToHash([]byte("code_hash")), nil) + }, + }, + } + + for _, tc := range testCase { + tc.malleate() + + code := suite.stateObject.Code(nil) + suite.Require().Equal(tc.expCode, code, tc.name) + } +} diff --git a/x/evm/types/statedb_test.go b/x/evm/types/statedb_test.go index 01f29431..2957b560 100644 --- a/x/evm/types/statedb_test.go +++ b/x/evm/types/statedb_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/suite" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" ethcmn "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" @@ -14,6 +15,7 @@ import ( "github.com/cosmos/ethermint/app" "github.com/cosmos/ethermint/crypto" + ethermint "github.com/cosmos/ethermint/types" "github.com/cosmos/ethermint/x/evm/keeper" "github.com/cosmos/ethermint/x/evm/types" @@ -23,10 +25,12 @@ import ( type StateDBTestSuite struct { suite.Suite - ctx sdk.Context - querier sdk.Querier - app *app.EthermintApp - stateDB *types.CommitStateDB + ctx sdk.Context + querier sdk.Querier + app *app.EthermintApp + stateDB *types.CommitStateDB + address ethcmn.Address + stateObject types.StateObject } func TestStateDBTestSuite(t *testing.T) { @@ -40,6 +44,18 @@ func (suite *StateDBTestSuite) SetupTest() { 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) + + privkey, err := crypto.GenerateKey() + suite.Require().NoError(err) + + suite.address = ethcmn.BytesToAddress(privkey.PubKey().Address().Bytes()) + acc := ðermint.EthAccount{ + BaseAccount: auth.NewBaseAccount(sdk.AccAddress(suite.address.Bytes()), nil, 0, 0), + CodeHash: ethcrypto.Keccak256(nil), + } + + suite.app.AccountKeeper.SetAccount(suite.ctx, acc) + suite.stateObject = suite.stateDB.GetOrNewStateObject(suite.address) } func (suite *StateDBTestSuite) TestBloomFilter() { @@ -308,3 +324,113 @@ func (suite *StateDBTestSuite) TestSuiteDBSuicide() { suite.Require().NoError(err) suite.Require().False(suite.stateDB.Exist(addr)) } + +func (suite *StateDBTestSuite) TestCommitStateDB_Commit() { + testCase := []struct { + name string + malleate func() + deleteObjs bool + expPass bool + }{ + { + "commit suicided", + func() { + ok := suite.stateDB.Suicide(suite.address) + suite.Require().True(ok) + }, + true, true, + }, + { + "commit with dirty value", + func() { + suite.stateDB.SetCode(suite.address, []byte("code")) + }, + false, true, + }, + { + "faled to update state object", + func() { + suite.stateDB.SubBalance(suite.address, big.NewInt(10)) + }, + false, false, + }, + } + + for _, tc := range testCase { + tc.malleate() + + hash, err := suite.stateDB.Commit(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 *StateDBTestSuite) TestCommitStateDB_Finalize() { + testCase := []struct { + name string + malleate func() + deleteObjs bool + expPass bool + }{ + { + "finalize suicided", + func() { + ok := suite.stateDB.Suicide(suite.address) + suite.Require().True(ok) + }, + true, true, + }, + { + "finalize, not suicided", + func() { + suite.stateDB.AddBalance(suite.address, big.NewInt(5)) + }, + false, true, + }, + { + "faled to update state object", + func() { + suite.stateDB.SubBalance(suite.address, big.NewInt(10)) + }, + false, false, + }, + } + + for _, tc := range testCase { + tc.malleate() + + err := suite.stateDB.Finalise(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) + } +}