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:
parent
7b4f712f66
commit
de8d8acf77
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@ -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 != ''"
|
||||||
|
@ -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 ðtypes.Receipt{}, 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the state with pending changes
|
// Update the state with pending changes
|
||||||
|
@ -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)
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user