x/evm: StateBD tests (#407)

* draft state_transition

* working test

* keeper test

* statedb rewrite

* fix tests

* add keeper statedb test

* minor changes

* x/evm: StateBD tests

* try fix

* fix stateObject.setState

* Update x/evm/types/state_object.go

* update stateObject.setState

* uncomment test

* increase coverage

* fix test-import

* update rpc tests

Co-authored-by: Justin Thompson <justin.thompson12@hotmail.com>
Co-authored-by: noot <elizabethjbinks@gmail.com>
This commit is contained in:
Federico Kunze 2020-07-27 21:33:16 +02:00 committed by GitHub
parent 7b4f712f66
commit de8d8acf77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 122 additions and 29 deletions

View File

@ -7,7 +7,7 @@ on:
branches: branches:
- development - development
jobs: jobs:
rpc-tests: test-rpc:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 10 timeout-minutes: 10
steps: steps:
@ -19,7 +19,7 @@ jobs:
.go .go
.mod .mod
.sum .sum
- name: rpc-test - name: test-rpc
run: | run: |
make test-rpc make test-rpc
if: "env.GIT_DIFF != ''" if: "env.GIT_DIFF != ''"

View File

@ -360,9 +360,9 @@ func applyTransaction(
// Apply the transaction to the current state (included in the env) // Apply the transaction to the current state (included in the env)
execResult, err := ethcore.ApplyMessage(vmenv, msg, gp) execResult, err := ethcore.ApplyMessage(vmenv, msg, gp)
// NOTE: ignore vm execution error (eg: tx out of gas) as we care only about state transition errors
if err != nil { if err != nil {
return nil, 0, err // NOTE: ignore vm execution error (eg: tx out of gas at block 51169) as we care only about state transition errors
return &ethtypes.Receipt{}, 0, nil
} }
// Update the state with pending changes // Update the state with pending changes

View File

@ -532,6 +532,7 @@ func TestEth_GetFilterChanges_NoTopics(t *testing.T) {
// instantiate new filter // instantiate new filter
rpcRes = call(t, "eth_newFilter", param) rpcRes = call(t, "eth_newFilter", param)
require.Nil(t, rpcRes.Error)
var ID hexutil.Bytes var ID hexutil.Bytes
err = json.Unmarshal(rpcRes.Result, &ID) err = json.Unmarshal(rpcRes.Result, &ID)
require.NoError(t, err) require.NoError(t, err)

View File

@ -142,6 +142,10 @@ func (so *stateObject) setState(key, value ethcmn.Hash) {
so.dirtyStorage = append(so.dirtyStorage, NewState(key, value)) so.dirtyStorage = append(so.dirtyStorage, NewState(key, value))
idx = len(so.dirtyStorage) - 1 idx = len(so.dirtyStorage) - 1
so.keyToDirtyStorageIndex[key] = idx so.keyToDirtyStorageIndex[key] = idx
so.originStorage = append(so.originStorage, State{})
idx = len(so.originStorage) - 1
so.keyToOriginStorageIndex[key] = idx
} }
// SetCode sets the state object's code. // SetCode sets the state object's code.
@ -242,9 +246,8 @@ func (so *stateObject) commitState() {
ctx := so.stateDB.ctx ctx := so.stateDB.ctx
store := prefix.NewStore(ctx.KVStore(so.stateDB.storeKey), AddressStoragePrefix(so.Address())) store := prefix.NewStore(ctx.KVStore(so.stateDB.storeKey), AddressStoragePrefix(so.Address()))
for i, state := range so.dirtyStorage { for _, state := range so.dirtyStorage {
delete(so.keyToDirtyStorageIndex, state.Key) delete(so.keyToDirtyStorageIndex, state.Key)
so.dirtyStorage = append(so.dirtyStorage[:i], so.dirtyStorage[i+1:]...)
// skip no-op changes, persist actual changes // skip no-op changes, persist actual changes
idx, ok := so.keyToOriginStorageIndex[state.Key] idx, ok := so.keyToOriginStorageIndex[state.Key]
@ -268,6 +271,8 @@ func (so *stateObject) commitState() {
store.Set(state.Key.Bytes(), state.Value.Bytes()) store.Set(state.Key.Bytes(), state.Value.Bytes())
} }
// clean storage as all entries are dirty
so.dirtyStorage = Storage{}
} }
// commitCode persists the state object's code to the KVStore. // commitCode persists the state object's code to the KVStore.

View File

@ -16,7 +16,6 @@ import (
ethtypes "github.com/ethereum/go-ethereum/core/types" ethtypes "github.com/ethereum/go-ethereum/core/types"
ethvm "github.com/ethereum/go-ethereum/core/vm" ethvm "github.com/ethereum/go-ethereum/core/vm"
ethcrypto "github.com/ethereum/go-ethereum/crypto" ethcrypto "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
) )
var ( var (
@ -745,13 +744,8 @@ func (csdb *CommitStateDB) ForEachStorage(addr ethcmn.Address, cb func(key, valu
continue continue
} }
_, content, _, err := rlp.Split(value.Bytes())
if err != nil {
return err
}
// check if iteration stops // check if iteration stops
if cb(key, ethcmn.BytesToHash(content)) { if cb(key, value) {
return nil return nil
} }
} }

View File

@ -1,6 +1,7 @@
package types_test package types_test
import ( import (
"fmt"
"math/big" "math/big"
"testing" "testing"
@ -111,7 +112,7 @@ func (suite *StateDBTestSuite) TestBloomFilter() {
} }
} }
func (suite *StateDBTestSuite) TestStateDBBalance() { func (suite *StateDBTestSuite) TestStateDB_Balance() {
testCase := []struct { testCase := []struct {
name string name string
malleate func() malleate func()
@ -159,7 +160,17 @@ func (suite *StateDBTestSuite) TestStateDBNonce() {
suite.Require().Equal(nonce, suite.stateDB.GetNonce(suite.address)) suite.Require().Equal(nonce, suite.stateDB.GetNonce(suite.address))
} }
func (suite *StateDBTestSuite) TestStateDBState() { func (suite *StateDBTestSuite) TestStateDB_Error() {
nonce := suite.stateDB.GetNonce(ethcmn.Address{})
suite.Require().Equal(0, int(nonce))
suite.Require().Error(suite.stateDB.Error())
}
func (suite *StateDBTestSuite) TestStateDB_Database() {
suite.Require().Nil(suite.stateDB.Database())
}
func (suite *StateDBTestSuite) TestStateDB_State() {
key := ethcmn.BytesToHash([]byte("foo")) key := ethcmn.BytesToHash([]byte("foo"))
val := ethcmn.BytesToHash([]byte("bar")) val := ethcmn.BytesToHash([]byte("bar"))
suite.stateDB.SetState(suite.address, key, val) suite.stateDB.SetState(suite.address, key, val)
@ -195,7 +206,7 @@ func (suite *StateDBTestSuite) TestStateDBState() {
} }
} }
func (suite *StateDBTestSuite) TestStateDBCode() { func (suite *StateDBTestSuite) TestStateDB_Code() {
testCase := []struct { testCase := []struct {
name string name string
address ethcmn.Address address ethcmn.Address
@ -232,7 +243,7 @@ func (suite *StateDBTestSuite) TestStateDBCode() {
} }
} }
func (suite *StateDBTestSuite) TestStateDBLogs() { func (suite *StateDBTestSuite) TestStateDB_Logs() {
testCase := []struct { testCase := []struct {
name string name string
log ethtypes.Log log ethtypes.Log
@ -278,16 +289,15 @@ func (suite *StateDBTestSuite) TestStateDBLogs() {
} }
} }
func (suite *StateDBTestSuite) TestStateDBPreimage() { func (suite *StateDBTestSuite) TestStateDB_Preimage() {
hash := ethcmn.BytesToHash([]byte("hash")) hash := ethcmn.BytesToHash([]byte("hash"))
preimage := []byte("preimage") preimage := []byte("preimage")
suite.stateDB.AddPreimage(hash, preimage) suite.stateDB.AddPreimage(hash, preimage)
suite.Require().Equal(preimage, suite.stateDB.Preimages()[hash]) suite.Require().Equal(preimage, suite.stateDB.Preimages()[hash])
} }
func (suite *StateDBTestSuite) TestStateDBRefund() { func (suite *StateDBTestSuite) TestStateDB_Refund() {
testCase := []struct { testCase := []struct {
name string name string
addAmount uint64 addAmount uint64
@ -331,7 +341,7 @@ func (suite *StateDBTestSuite) TestStateDBRefund() {
} }
} }
func (suite *StateDBTestSuite) TestStateDBCreateAcct() { func (suite *StateDBTestSuite) TestStateDB_CreateAccount() {
prevBalance := big.NewInt(12) prevBalance := big.NewInt(12)
testCase := []struct { testCase := []struct {
@ -364,7 +374,7 @@ func (suite *StateDBTestSuite) TestStateDBCreateAcct() {
} }
} }
func (suite *StateDBTestSuite) TestStateDBClearStateOjb() { func (suite *StateDBTestSuite) TestStateDB_ClearStateObj() {
priv, err := crypto.GenerateKey() priv, err := crypto.GenerateKey()
suite.Require().NoError(err) suite.Require().NoError(err)
@ -377,7 +387,7 @@ func (suite *StateDBTestSuite) TestStateDBClearStateOjb() {
suite.Require().False(suite.stateDB.Exist(addr)) suite.Require().False(suite.stateDB.Exist(addr))
} }
func (suite *StateDBTestSuite) TestStateDBReset() { func (suite *StateDBTestSuite) TestStateDB_Reset() {
priv, err := crypto.GenerateKey() priv, err := crypto.GenerateKey()
suite.Require().NoError(err) suite.Require().NoError(err)
@ -391,7 +401,7 @@ func (suite *StateDBTestSuite) TestStateDBReset() {
suite.Require().False(suite.stateDB.Exist(addr)) suite.Require().False(suite.stateDB.Exist(addr))
} }
func (suite *StateDBTestSuite) TestSuiteDBPrepare() { func (suite *StateDBTestSuite) TestSuiteDB_Prepare() {
thash := ethcmn.BytesToHash([]byte("thash")) thash := ethcmn.BytesToHash([]byte("thash"))
bhash := ethcmn.BytesToHash([]byte("bhash")) bhash := ethcmn.BytesToHash([]byte("bhash"))
txi := 1 txi := 1
@ -402,7 +412,7 @@ func (suite *StateDBTestSuite) TestSuiteDBPrepare() {
suite.Require().Equal(bhash, suite.stateDB.BlockHash()) suite.Require().Equal(bhash, suite.stateDB.BlockHash())
} }
func (suite *StateDBTestSuite) TestSuiteDBCopyState() { func (suite *StateDBTestSuite) TestSuiteDB_CopyState() {
testCase := []struct { testCase := []struct {
name string name string
log ethtypes.Log log ethtypes.Log
@ -439,16 +449,14 @@ func (suite *StateDBTestSuite) TestSuiteDBCopyState() {
} }
} }
func (suite *StateDBTestSuite) TestSuiteDBEmpty() { func (suite *StateDBTestSuite) TestSuiteDB_Empty() {
suite.Require().True(suite.stateDB.Empty(suite.address)) suite.Require().True(suite.stateDB.Empty(suite.address))
suite.stateDB.SetBalance(suite.address, big.NewInt(100)) suite.stateDB.SetBalance(suite.address, big.NewInt(100))
suite.Require().False(suite.stateDB.Empty(suite.address)) suite.Require().False(suite.stateDB.Empty(suite.address))
} }
func (suite *StateDBTestSuite) TestSuiteDBSuicide() { func (suite *StateDBTestSuite) TestSuiteDB_Suicide() {
testCase := []struct { testCase := []struct {
name string name string
amount *big.Int amount *big.Int
@ -600,6 +608,8 @@ func (suite *StateDBTestSuite) TestCommitStateDB_Finalize() {
if !tc.expPass { if !tc.expPass {
suite.Require().Error(err, tc.name) suite.Require().Error(err, tc.name)
hash := suite.stateDB.GetCommittedState(suite.address, ethcmn.BytesToHash([]byte("key")))
suite.Require().NotEqual(ethcmn.Hash{}, hash, tc.name)
continue continue
} }
@ -614,3 +624,86 @@ func (suite *StateDBTestSuite) TestCommitStateDB_Finalize() {
suite.Require().NotNil(acc, tc.name) suite.Require().NotNil(acc, tc.name)
} }
} }
func (suite *StateDBTestSuite) TestCommitStateDB_GetCommittedState() {
hash := suite.stateDB.GetCommittedState(ethcmn.Address{}, ethcmn.BytesToHash([]byte("key")))
suite.Require().Equal(ethcmn.Hash{}, hash)
}
func (suite *StateDBTestSuite) TestCommitStateDB_Snapshot() {
id := suite.stateDB.Snapshot()
suite.Require().NotPanics(func() {
suite.stateDB.RevertToSnapshot(id)
})
suite.Require().Panics(func() {
suite.stateDB.RevertToSnapshot(-1)
}, "invalid revision should panic")
}
func (suite *StateDBTestSuite) TestCommitStateDB_ForEachStorage() {
var storage types.Storage
testCase := []struct {
name string
malleate func()
callback func(key, value ethcmn.Hash) (stop bool)
expValues []ethcmn.Hash
}{
{
"aggregate state",
func() {
for i := 0; i < 5; i++ {
suite.stateDB.SetState(suite.address, ethcmn.BytesToHash([]byte(fmt.Sprintf("key%d", i))), ethcmn.BytesToHash([]byte(fmt.Sprintf("value%d", i))))
}
},
func(key, value ethcmn.Hash) bool {
storage = append(storage, types.NewState(key, value))
return false
},
[]ethcmn.Hash{
ethcmn.BytesToHash([]byte("value0")),
ethcmn.BytesToHash([]byte("value1")),
ethcmn.BytesToHash([]byte("value2")),
ethcmn.BytesToHash([]byte("value3")),
ethcmn.BytesToHash([]byte("value4")),
},
},
{
"filter state",
func() {
suite.stateDB.SetState(suite.address, ethcmn.BytesToHash([]byte("key")), ethcmn.BytesToHash([]byte("value")))
suite.stateDB.SetState(suite.address, ethcmn.BytesToHash([]byte("filterkey")), ethcmn.BytesToHash([]byte("filtervalue")))
},
func(key, value ethcmn.Hash) bool {
if value == ethcmn.BytesToHash([]byte("filtervalue")) {
storage = append(storage, types.NewState(key, value))
return true
}
return false
},
[]ethcmn.Hash{
ethcmn.BytesToHash([]byte("filtervalue")),
},
},
}
for _, tc := range testCase {
suite.Run(tc.name, func() {
suite.SetupTest() // reset
tc.malleate()
suite.stateDB.Finalise(false)
err := suite.stateDB.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([]ethcmn.Hash, len(storage))
for i := range storage {
vals[i] = storage[i].Value
}
suite.Require().ElementsMatch(tc.expValues, vals)
})
storage = types.Storage{}
}
}