|
|
|
@ -194,20 +194,14 @@ func (t *StateTest) checkError(subtest StateSubtest, err error) error { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Run executes a specific subtest and verifies the post-state and logs
|
|
|
|
|
func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config, snapshotter bool, scheme string, postCheck func(err error, snaps *snapshot.Tree, state *state.StateDB)) (result error) { |
|
|
|
|
triedb, snaps, statedb, root, err := t.RunNoVerify(subtest, vmconfig, snapshotter, scheme) |
|
|
|
|
|
|
|
|
|
func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config, snapshotter bool, scheme string, postCheck func(err error, st *StateTestState)) (result error) { |
|
|
|
|
st, root, err := t.RunNoVerify(subtest, vmconfig, snapshotter, scheme) |
|
|
|
|
// Invoke the callback at the end of function for further analysis.
|
|
|
|
|
defer func() { |
|
|
|
|
postCheck(result, snaps, statedb) |
|
|
|
|
|
|
|
|
|
if triedb != nil { |
|
|
|
|
triedb.Close() |
|
|
|
|
} |
|
|
|
|
if snaps != nil { |
|
|
|
|
snaps.Release() |
|
|
|
|
} |
|
|
|
|
postCheck(result, &st) |
|
|
|
|
st.Close() |
|
|
|
|
}() |
|
|
|
|
|
|
|
|
|
checkedErr := t.checkError(subtest, err) |
|
|
|
|
if checkedErr != nil { |
|
|
|
|
return checkedErr |
|
|
|
@ -224,23 +218,24 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config, snapshotter bo |
|
|
|
|
if root != common.Hash(post.Root) { |
|
|
|
|
return fmt.Errorf("post state root mismatch: got %x, want %x", root, post.Root) |
|
|
|
|
} |
|
|
|
|
if logs := rlpHash(statedb.Logs()); logs != common.Hash(post.Logs) { |
|
|
|
|
if logs := rlpHash(st.StateDB.Logs()); logs != common.Hash(post.Logs) { |
|
|
|
|
return fmt.Errorf("post state logs hash mismatch: got %x, want %x", logs, post.Logs) |
|
|
|
|
} |
|
|
|
|
statedb, _ = state.New(root, statedb.Database(), snaps) |
|
|
|
|
st.StateDB, _ = state.New(root, st.StateDB.Database(), st.Snapshots) |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// RunNoVerify runs a specific subtest and returns the statedb and post-state root
|
|
|
|
|
func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapshotter bool, scheme string) (*triedb.Database, *snapshot.Tree, *state.StateDB, common.Hash, error) { |
|
|
|
|
// RunNoVerify runs a specific subtest and returns the statedb and post-state root.
|
|
|
|
|
// Remember to call state.Close after verifying the test result!
|
|
|
|
|
func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapshotter bool, scheme string) (state StateTestState, root common.Hash, err error) { |
|
|
|
|
config, eips, err := GetChainConfig(subtest.Fork) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, nil, nil, common.Hash{}, UnsupportedForkError{subtest.Fork} |
|
|
|
|
return state, common.Hash{}, UnsupportedForkError{subtest.Fork} |
|
|
|
|
} |
|
|
|
|
vmconfig.ExtraEips = eips |
|
|
|
|
|
|
|
|
|
block := t.genesis(config).ToBlock() |
|
|
|
|
triedb, snaps, statedb := MakePreState(rawdb.NewMemoryDatabase(), t.json.Pre, snapshotter, scheme) |
|
|
|
|
state = MakePreState(rawdb.NewMemoryDatabase(), t.json.Pre, snapshotter, scheme) |
|
|
|
|
|
|
|
|
|
var baseFee *big.Int |
|
|
|
|
if config.IsLondon(new(big.Int)) { |
|
|
|
@ -254,8 +249,18 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh |
|
|
|
|
post := t.json.Post[subtest.Fork][subtest.Index] |
|
|
|
|
msg, err := t.json.Tx.toMessage(post, baseFee) |
|
|
|
|
if err != nil { |
|
|
|
|
triedb.Close() |
|
|
|
|
return nil, nil, nil, common.Hash{}, err |
|
|
|
|
return state, common.Hash{}, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
{ // Blob transactions may be present after the Cancun fork.
|
|
|
|
|
// In production,
|
|
|
|
|
// - the header is verified against the max in eip4844.go:VerifyEIP4844Header
|
|
|
|
|
// - the block body is verified against the header in block_validator.go:ValidateBody
|
|
|
|
|
// Here, we just do this shortcut smaller fix, since state tests do not
|
|
|
|
|
// utilize those codepaths
|
|
|
|
|
if len(msg.BlobHashes)*params.BlobTxBlobGasPerBlob > params.MaxBlobGasPerBlock { |
|
|
|
|
return state, common.Hash{}, errors.New("blob gas exceeds maximum") |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Try to recover tx with current signer
|
|
|
|
@ -263,13 +268,10 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh |
|
|
|
|
var ttx types.Transaction |
|
|
|
|
err := ttx.UnmarshalBinary(post.TxBytes) |
|
|
|
|
if err != nil { |
|
|
|
|
triedb.Close() |
|
|
|
|
return nil, nil, nil, common.Hash{}, err |
|
|
|
|
return state, common.Hash{}, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if _, err := types.Sender(types.LatestSigner(config), &ttx); err != nil { |
|
|
|
|
triedb.Close() |
|
|
|
|
return nil, nil, nil, common.Hash{}, err |
|
|
|
|
return state, common.Hash{}, err |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -290,78 +292,32 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh |
|
|
|
|
if config.IsCancun(new(big.Int), block.Time()) && t.json.Env.ExcessBlobGas != nil { |
|
|
|
|
context.BlobBaseFee = eip4844.CalcBlobFee(*t.json.Env.ExcessBlobGas) |
|
|
|
|
} |
|
|
|
|
evm := vm.NewEVM(context, txContext, statedb, config, vmconfig) |
|
|
|
|
|
|
|
|
|
{ // Blob transactions may be present after the Cancun fork.
|
|
|
|
|
// In production,
|
|
|
|
|
// - the header is verified against the max in eip4844.go:VerifyEIP4844Header
|
|
|
|
|
// - the block body is verified against the header in block_validator.go:ValidateBody
|
|
|
|
|
// Here, we just do this shortcut smaller fix, since state tests do not
|
|
|
|
|
// utilize those codepaths
|
|
|
|
|
if len(msg.BlobHashes)*params.BlobTxBlobGasPerBlob > params.MaxBlobGasPerBlock { |
|
|
|
|
return nil, nil, nil, common.Hash{}, errors.New("blob gas exceeds maximum") |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
evm := vm.NewEVM(context, txContext, state.StateDB, config, vmconfig) |
|
|
|
|
|
|
|
|
|
// Execute the message.
|
|
|
|
|
snapshot := statedb.Snapshot() |
|
|
|
|
snapshot := state.StateDB.Snapshot() |
|
|
|
|
gaspool := new(core.GasPool) |
|
|
|
|
gaspool.AddGas(block.GasLimit()) |
|
|
|
|
_, err = core.ApplyMessage(evm, msg, gaspool) |
|
|
|
|
if err != nil { |
|
|
|
|
statedb.RevertToSnapshot(snapshot) |
|
|
|
|
state.StateDB.RevertToSnapshot(snapshot) |
|
|
|
|
} |
|
|
|
|
// Add 0-value mining reward. This only makes a difference in the cases
|
|
|
|
|
// where
|
|
|
|
|
// - the coinbase self-destructed, or
|
|
|
|
|
// - there are only 'bad' transactions, which aren't executed. In those cases,
|
|
|
|
|
// the coinbase gets no txfee, so isn't created, and thus needs to be touched
|
|
|
|
|
statedb.AddBalance(block.Coinbase(), new(uint256.Int)) |
|
|
|
|
state.StateDB.AddBalance(block.Coinbase(), new(uint256.Int)) |
|
|
|
|
|
|
|
|
|
// Commit state mutations into database.
|
|
|
|
|
root, _ := statedb.Commit(block.NumberU64(), config.IsEIP158(block.Number())) |
|
|
|
|
return triedb, snaps, statedb, root, err |
|
|
|
|
root, _ = state.StateDB.Commit(block.NumberU64(), config.IsEIP158(block.Number())) |
|
|
|
|
return state, root, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (t *StateTest) gasLimit(subtest StateSubtest) uint64 { |
|
|
|
|
return t.json.Tx.GasLimit[t.json.Post[subtest.Fork][subtest.Index].Indexes.Gas] |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func MakePreState(db ethdb.Database, accounts core.GenesisAlloc, snapshotter bool, scheme string) (*triedb.Database, *snapshot.Tree, *state.StateDB) { |
|
|
|
|
tconf := &triedb.Config{Preimages: true} |
|
|
|
|
if scheme == rawdb.HashScheme { |
|
|
|
|
tconf.HashDB = hashdb.Defaults |
|
|
|
|
} else { |
|
|
|
|
tconf.PathDB = pathdb.Defaults |
|
|
|
|
} |
|
|
|
|
triedb := triedb.NewDatabase(db, tconf) |
|
|
|
|
sdb := state.NewDatabaseWithNodeDB(db, triedb) |
|
|
|
|
statedb, _ := state.New(types.EmptyRootHash, sdb, nil) |
|
|
|
|
for addr, a := range accounts { |
|
|
|
|
statedb.SetCode(addr, a.Code) |
|
|
|
|
statedb.SetNonce(addr, a.Nonce) |
|
|
|
|
statedb.SetBalance(addr, uint256.MustFromBig(a.Balance)) |
|
|
|
|
for k, v := range a.Storage { |
|
|
|
|
statedb.SetState(addr, k, v) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Commit and re-open to start with a clean state.
|
|
|
|
|
root, _ := statedb.Commit(0, false) |
|
|
|
|
|
|
|
|
|
var snaps *snapshot.Tree |
|
|
|
|
if snapshotter { |
|
|
|
|
snapconfig := snapshot.Config{ |
|
|
|
|
CacheSize: 1, |
|
|
|
|
Recovery: false, |
|
|
|
|
NoBuild: false, |
|
|
|
|
AsyncBuild: false, |
|
|
|
|
} |
|
|
|
|
snaps, _ = snapshot.New(snapconfig, db, triedb, root) |
|
|
|
|
} |
|
|
|
|
statedb, _ = state.New(root, sdb, snaps) |
|
|
|
|
return triedb, snaps, statedb |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (t *StateTest) genesis(config *params.ChainConfig) *core.Genesis { |
|
|
|
|
genesis := &core.Genesis{ |
|
|
|
|
Config: config, |
|
|
|
@ -478,3 +434,61 @@ func rlpHash(x interface{}) (h common.Hash) { |
|
|
|
|
func vmTestBlockHash(n uint64) common.Hash { |
|
|
|
|
return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String()))) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// StateTestState groups all the state database objects together for use in tests.
|
|
|
|
|
type StateTestState struct { |
|
|
|
|
StateDB *state.StateDB |
|
|
|
|
TrieDB *triedb.Database |
|
|
|
|
Snapshots *snapshot.Tree |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// MakePreState creates a state containing the given allocation.
|
|
|
|
|
func MakePreState(db ethdb.Database, accounts core.GenesisAlloc, snapshotter bool, scheme string) StateTestState { |
|
|
|
|
tconf := &triedb.Config{Preimages: true} |
|
|
|
|
if scheme == rawdb.HashScheme { |
|
|
|
|
tconf.HashDB = hashdb.Defaults |
|
|
|
|
} else { |
|
|
|
|
tconf.PathDB = pathdb.Defaults |
|
|
|
|
} |
|
|
|
|
triedb := triedb.NewDatabase(db, tconf) |
|
|
|
|
sdb := state.NewDatabaseWithNodeDB(db, triedb) |
|
|
|
|
statedb, _ := state.New(types.EmptyRootHash, sdb, nil) |
|
|
|
|
for addr, a := range accounts { |
|
|
|
|
statedb.SetCode(addr, a.Code) |
|
|
|
|
statedb.SetNonce(addr, a.Nonce) |
|
|
|
|
statedb.SetBalance(addr, uint256.MustFromBig(a.Balance)) |
|
|
|
|
for k, v := range a.Storage { |
|
|
|
|
statedb.SetState(addr, k, v) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Commit and re-open to start with a clean state.
|
|
|
|
|
root, _ := statedb.Commit(0, false) |
|
|
|
|
|
|
|
|
|
// If snapshot is requested, initialize the snapshotter and use it in state.
|
|
|
|
|
var snaps *snapshot.Tree |
|
|
|
|
if snapshotter { |
|
|
|
|
snapconfig := snapshot.Config{ |
|
|
|
|
CacheSize: 1, |
|
|
|
|
Recovery: false, |
|
|
|
|
NoBuild: false, |
|
|
|
|
AsyncBuild: false, |
|
|
|
|
} |
|
|
|
|
snaps, _ = snapshot.New(snapconfig, db, triedb, root) |
|
|
|
|
} |
|
|
|
|
statedb, _ = state.New(root, sdb, snaps) |
|
|
|
|
return StateTestState{statedb, triedb, snaps} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Close should be called when the state is no longer needed, ie. after running the test.
|
|
|
|
|
func (st *StateTestState) Close() { |
|
|
|
|
if st.TrieDB != nil { |
|
|
|
|
st.TrieDB.Close() |
|
|
|
|
st.TrieDB = nil |
|
|
|
|
} |
|
|
|
|
if st.Snapshots != nil { |
|
|
|
|
// Need to call Disable here to quit the snapshot generator goroutine.
|
|
|
|
|
st.Snapshots.Disable() |
|
|
|
|
st.Snapshots.Release() |
|
|
|
|
st.Snapshots = nil |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|